Why Doesn't Gcc Support Naked Functions

Using the naked attribute for functions in GCC

If the only thing you do in the naked function is call another function you can just use a single JMP machine code instruction.

The function you jump to will have a valid prologue and it should return directly to the caller of the naked function since JMP doesn't push a return address on the stack.

Equivalent for GCC's naked attribute

Just use asm() outside a function block. The argument of asm() is simply ignored by the compiler and passed directly on to the assembler. For complex functions a separate assembly source file is the better option to avoid the awkward syntax.

Example:

#include <stdio.h>

asm("_one: \n\
movl $1,%eax \n\
ret \n\
");

int one();

int main() {
printf("result: %d\n", one());
return 0;
}

PS: Make sure you understand the calling conventions of your platform. Many times you can not just copy/past assembly code.

PPS: If you care about performance, use extended asm instead. Extended asm essentially inlines the assembly code into your C/C++ code and is much faster, especially for short assembly functions. For larger assembly functions a seperate assembly source file is preferable, so this answer is really a hack for the rare case that you need a function pointer to a small assembly function.

Equivalent of __declspec( naked ) in gcc/g++

I believe there is no such equivalent with a recent GCC under Linux. The compiler emit prologues and epilogues when appropriate, and you should leave that decision to it. It may be quite good at making prologues or epilogues quite small, or even sometimes non-existent.

You could code your function in assembly. Or you can put asm statements inside your function.

And you did not tell why you want to do that. What is your goal, and why precisely are you asking?

clang - error: non-ASM statement in naked function is not supported

I should have written this as an answer instead of a comment. The question was:

Why doesn't Clang support non-ASM statement in naked function? This works fine on gcc.

The answer is that this doesn't work fine in gcc. Quoting from the gcc docs for the naked attribute:

Only basic asm statements can safely be included in naked functions. While using extended asm or a mixture of basic asm and C code may appear to work, they cannot be depended upon to work reliably and are not supported.

If there is a less ambiguous way to phrase this, I couldn't come up with it.

Note that while the specific link above is for ARM (which is what I'm guessing the OP is using), I believe the same text applies to all platforms that support naked.

inside naked function - how to do simple assignment

It was only luck that your previous version of the function happened to work without crashing.

The only thing that can safely be put inside a naked function is a pure Basic Asm statement. https://gcc.gnu.org/onlinedocs/gcc/ARM-Function-Attributes.html. You can split it up into multiple Basic Asm statements, instead of asm("insn \n\t" / "insn2 \n\t" / ...);, but you have to write the entire function in asm yourself.

While using extended asm or a mixture of basic asm and C code may appear to work, they cannot be depended upon to work reliably and are not supported.

If you want to run C++ code from a naked function, you could call a regular function (or bl on ARM, jal on MIPS, etc.), following to the standard calling convention.


As for the specific reason in this case? Maybe creating that address in a register stepped on the function args, leading to the branches going wrong? Inspect the generated asm if you want, but it's 100% unsupported.

Or maybe it ended up using more registers, and since it's naked didn't properly save/restore call-preserved registers? I haven't looked at the code-gen myself for naked functions.

Are you sure this function needs to be naked? I guess that's because you manipulate lr to return to the new context.

If you don't want to just write more logic in asm, maybe have this function's caller do more work (and maybe pass it pointer and/or boolean args telling it more simply what it needs to do, so your inputs are already in registers, and you don't need to access globals).

why doesn't GCC place a statically-initialized C++ class object into .data

Not marking the constuctor with constexpr seems to prevent optimizations here. The following type seems to get initialized properly, see godbolt:

struct ObjC2 {
int x;
constexpr ObjC2(int x)
: x(x)
{
}
};

ObjC2 obj3(300);

Is the Link Register (LR) affected by inline or naked functions?

re: update. Leaving my old answer below, since it answers the original question before the edit.

__attribute__((naked)) basically exists so you can write the whole function in asm, inside asm statements instead of in a separate .S file. The compiler doesn't even emit a return instruction, you have to do that yourself. It doesn't make sense to use this for inline functions (like I already answered below).

Calling a naked function will generate the usual call sequence, with a bl my_naked_function, which of course sets LR to point to the instruction after the bl. A naked function is essentially a never-inline function that you write in asm. "prologue" and "epilogue" are the instructions that save and restore callee-saved registers, and the return instruction itself (bx lr).


Try it and see. It's easy to look at gcc's asm output. I changed your function names to help explain what's going on, and fixed the syntax (The GNU C __attribute__ extension requires doubled parens).

extern void extfunc(void);

__attribute__((always_inline))
inline void break_the_stack() { asm volatile("PUSH LR"); }

__attribute__((naked))
void myFunc() {
asm volatile("PUSH {r3, LR}\n\t" // keep the stack aligned for our callee by pushing a dummy register along with LR
"bl extfunc\n\t"
"pop {r3, PC}"
);
}

int foo_simple(void) {
extfunc();
return 0;
}

int foo_using_inline(void) {
break_the_stack();
extfunc();
return 0;
}

asm output with gcc 4.8.2 -O2 for ARM (default is a thumb target, I think).

myFunc():            # I followed the compiler's foo_simple example for this
PUSH {r3, LR}
bl extfunc
pop {r3, PC}
foo_simple():
push {r3, lr}
bl extfunc()
movs r0, #0
pop {r3, pc}
foo_using_inline():
push {r3, lr}
PUSH LR
bl extfunc()
movs r0, #0
pop {r3, pc}

The extra push LR means we're popping the wrong data into PC. Maybe another copy of LR, in this case, but we're returning with a modified stack pointer, so the caller will break. Don't mess with LR or the stack in an inline function, unless you're trying to do some kind of binary instrumentation thing.



re: comments: if you just want to set a C variable = LR:

As @Notlikethat points out, LR might not hold the return address. So you might want __builtin_return_address(0) to get the return address of the current function. However, if you're just trying to save register state, then you should save/restore whatever the function has in LR if you hope to correctly resume execution at this point:

#define get_lr(lr_val)  asm ("mov %0, lr" : "=r" (lr_val))

This might need to be volatile to stop it from being hoisted up the call tree during whole-program optimization.

This leads to an extra mov instruction when perhaps the ideal sequence would be to store lr, rather than copy to another reg first. Since ARM uses different instructions for reg-reg move vs. store to memory, you can't just use a rm constraint for the output operand to give the compiler that option.

You could wrap this inside an inline function. A GNU C statement-expression in a macro would also work, but an inline function should be fine:

__attribute__((always_inline)) void* current_lr(void) {  // This should work correctly when inlined, or just use the macro
void* lr;
get_lr(lr);
return lr;
}

For reference: What are SP (stack) and LR in ARM?



A naked always_inline function is not useful.

The docs say a naked function can only contain asm statements, and only "Basic" asm (without operands, so you have to get args from the right place for the ABI yourself). Inlining that makes zero sense, because you won't know where the compiler put your args.

If you want to inline some asm, don't use a naked function. Instead, use an inline function that uses correct contraints for input/output parameters.

The x86 wiki has some good inline asm links, and they're not all specific to x86. For example, see the collection of GNU inline asm links at the end of this answer for examples of how to make good use of the syntax to let the compiler make as efficient code as possible around your asm fragment.



Related Topics



Leave a reply



Submit