Reputation: 33
I am trying to create a class containing custom prologue/epilogues that shall be used by methods of other "consumer" classes.
It should look like the following example. (Keep in mind that these example prologue/epilogue bodies are just placeholders for illustration purposes. Surely they are going to be written in pure assembly)
class foo
{
public:
static inline void prologue(void) __attribute__ ((always_inline))
{
asm("nop");
}
static inline void epilogue(void) __attribute__ ((always_inline))
{
asm("nop");
}
};
Now, in "consumer" classes that contain methods using these custom prologue/epilogues, I use the naked attribute like bellow. (Keep in mind this example method body structure is what matters. More code shall be placed between the prologue/epilogue but is not necessary for this illustration)
class bar
{
public:
static void method() __attribute__ ((naked))
{
foo::prologue();
//more cpp instructions shall go here
foo::epilogue();
}
};
The problem I am facing is that clang compiler does not allow calling standard Cpp code like foo::prologue()
in naked functions and returns the following error
<source>:23:9: error: non-ASM statement in naked function is not supported
23 | foo::prologue();
| ^
Interestingly enough GCC seems to allows this.
Am I missing something? Is there any alternative elegant way to do this?
godbolt test can be found here: https://godbolt.org/z/YfnvfEM3f
Upvotes: 1
Views: 123
Reputation: 11
By nature, naked function doesn't allow C++ statement because you don't have any setups for stack, registers or addresses.
For a workaround, you can call function as extern C symbols for ARM instruction which you can pass in your ASM.inside of your naked function.
class foo {
public:
static inline void prologue(void) __attribute__((always_inline)) {
asm("nop");
}
static inline void epilogue(void) __attribute__((always_inline)) {
asm("nop");
}
};
extern "C" void foo_prologue_wrapper() {
foo::prologue();
}
extern "C" void foo_epilogue_wrapper() {
foo::epilogue();
}
extern "C" void foo_otherfunctioncall(){
//code the rest here
return;
}
class bar {
public:
static void method() __attribute__((naked)) {
asm volatile(
"bl foo_prologue_wrapper\n"
"bl foo_otherfunctioncall\n"
"bl foo_epilogue_wrapper\n"
"ret\n"
);
}
};
int main(void) {
bar::method();
return 0;
}
With this, you should be able to access your C++ inside of the "foo_otherfunctioncall" function.
#include <vector>
extern "C" void foo_otherfunctioncall(){
std::vector<int> a;
return;
}
It does compile into this :
foo_prologue_wrapper:
nop
ret
foo_epilogue_wrapper:
nop
ret
foo_otherfunctioncall:
sub sp, sp, #48
stp x29, x30, [sp, #32]
add x29, sp, #32
add x0, sp, #8
str x0, [sp]
bl std::vector<int, std::allocator<int>>::vector() [base object constructor]
ldr x0, [sp]
bl std::vector<int, std::allocator<int>>::~vector() [base object destructor]
ldp x29, x30, [sp, #32]
add sp, sp, #48
ret
main:
sub sp, sp, #32
stp x29, x30, [sp, #16]
add x29, sp, #16
mov w8, wzr
str w8, [sp, #8]
stur wzr, [x29, #-4]
bl bar::method()
ldr w0, [sp, #8]
ldp x29, x30, [sp, #16]
add sp, sp, #32
ret
bar::method():
bl foo_prologue_wrapper
bl foo_otherfunctioncall
bl foo_epilogue_wrapper
ret
__clang_call_terminate:
stp x29, x30, [sp, #-16]!
mov x29, sp
bl __cxa_begin_catch
bl std::terminate()
DW.ref.__gxx_personality_v0:
.xword __gxx_personality_v0
Upvotes: 0