2008-08-18:

Naked functions in gcc/g++

c:c++:assembler
Recently I was creating in C++ (MinGW g++) a small library for runtime-patching. A need came to create an assembler-only functions, without any additions from the compiler - a "naked" function. However, even if compilers from Redmond support __declspec(naked) attribute for the x86 [Visual C++ Language Reference - naked (C++)], GNU compilers don't - they only support "naked" in ports for ARM, AVR, IP2K and SPU [Using the GNU Compiler Collection (For GCC version 4.3.0) - Function Attributes]. The problem had several possible solutions:
1. An external source file with the assembler function could be created, compiled to an object file (GNU AS), and in the end linked with the rest of the project.
2. Like above, but compiled with nasm (Netwide Assembler), thrown into an ASCIIZ string, and then the string would be casted to a function pointer (it might sound strange, but thats the way I usually do it).
3. Check how gcc/g++ compiles a function to assembler, and then use the inline assembler to do the same.
The first point was a no-go since I didn't want to create additional external source files. The second one was also no good since I wanted the code to be readable and easy to modify. Only the third method had remain.

Checking how gcc/g++ compiles something to assembler is rather trivial, since there is a compiler option for it: -S (produces an .s file with assembler source instead of an object/executable file). If one likes Intel syntax over AT&T, one can add also -masm=intel option. It does not make any difference for me, so I stayed with the default AT&T.
The tests were made on a simple function gimme_five, which returns 5, and does little more. The below listing is a result of compiling the function code with the MinGW gcc C compiler.

.globl _gimme_five
  .def _gimme_five; .scl 2; .type 32; .endef
_gimme_five:
  pushl %ebp
  movl %esp, %ebp
  movl $5, %eax
  popl %ebp
  ret

The most important line in the above listing is the label declaration (3rd line), which is _gimme_five: (one has to remember that MinGW gcc appends an additional underscore at the beginning of function name; linux gcc don't append anything, neither does DJGPP). The two first lines, .globl _gimme_five and the one starting with .def are optional. The first one is useful when we want the function to be visible during linking phase (if we add the static argument to the function, then it would not appear). The second line is used to define additional options regarding the resulting object file [Using as (GNU Binutils version 2.17.90) - Assembler Directives] - I've totally omitted it.

A size-optimised code of the above function, with the necessary assembler directives looks like this:

.globl _gimme_five
_gimme_five:
  movl $5, %eax
  ret

Translating it to C syntax we get the following (a C function declaration must be added of course, the compiler has to know that such a function indeed exists):

int gimme_five(void);
__asm(
  ".globl _gimme_five\n"
  "_gimme_five:\n"
  "  movl $5, %eax\n"
  "  ret"
);

Another thing is using the above schema in C++ - the function decoration issue occurs. Function name int gimme_five(void) will be translated in C++ (MinGW g++) to __Z10gimme_fivev, hence it's necessary to add additional label with the decorated function name (it's good that in inline assembler a place can has several labels describing it ;>):

int gimme_five(void);
__asm(
  ".globl __Z10gimme_fivev\n"
  ".globl _gimme_five\n"
  "__Z10gimme_fivev:\n"
  "_gimme_five:\n"
  "  movl $5, %eax\n"
  "  ret"
);

Of course instead of doubling the labels one can throw the C function declaration into the extern "C" { } block.

It's necessary to note that the above method does not allow the usage of function argument names. However it might be a good thing after all - the Microsoft compiler doesn't do so well when it comes to handling arguments in a naked function - it generates not working code (there were a couple of times I've looked for a bug for several hours, just to find out that the assembler code seems to forget about the missing function prologue/epilogue).
To avoid this problem one can create two functions - an assembler "naked" wrapper, and a normal C function what would be called by the wrapper. The wrapper function would setup the arguments/rest of the environment, and thanks to that the C function could be constructed without the "naked" argument.

That's all for now ;>

Update 1:
I've corrected the line termination marker. It was \r\n while it's supposed to be \n (either version should work... on Windows, not sure on other systems).

Update 2:
Of course, instead of writing each line like this:
__asm("line1 \n"
"line2 \n"
"line3");

You can use the backslash and write like this:
__asm("line1 \n\
line2 \n\
line3");


Update 2:
See also: Lazy Coding - DLLExport with naked functions.

Comments:

2010-01-14 16:05:29 = till
{
Thanks for this article. I also spent some time searching for a way that lets me create naked functions with mingw. However, I think your approach is same than writing a function directly in assembly. The only advantage is that you don't have to maintain an additional file, which doesn't weigh much in bigger projects. I still would like to see gcc/mingw supporting __delspec(naked) so I could just write

int __delspec(naked) gimme_five(void) { return 5};

Anyways, thanks for this blog post.
}
2010-01-16 02:14:45 = Gynvael Coldwind
{
@till
Glad you like it!
Yeah, I totally agree with you - having a decent __declspec(naked) would be great.
}
2010-09-20 20:32:59 = avejidah
{
Thanks, I'll give this a try - it will be useful for game... modifaction. When I want to modify a function in an arbitrary process I've always written the patch in assembly, compiled it with nasm, dumped the byte code and copied it over to a unsigned char buffer, then used memcpy to overwrite the original function with the new one.
}
2012-09-17 03:31:27 = lefteris
{
Hello man,

Came here via google search for the __declspec(naked) with GCC. It's the year 2012, close to 2013 and they still don't seem to have added it. I will probably follow your method to achieve my purpose. Thanks a lot for posting a detailed explanation.
}
2013-09-24 15:59:08 = castaway
{
This is what i'am searching for. Thanks!
}

Add a comment:

Nick:
URL (optional):
Math captcha: 7 ∗ 1 + 5 =