Main () sometimes holds a frame pointer with -fomit-frame-pointer on x86

I accidentally discovered some strange thing about -fomit-frame-pointer from GCC to x86 when I was doing homework.
Take a look at the following code (which seems pretty clumsy, but somehow related to how I found the problem)

#include <stdio.h> void foo(void); int main() { foo(); return 0; } void foo() { printf("0x%x\n", *(unsigned char *)main); } 

when compiling with the -m64 -O1 flag (-fomit-frame-pointer enabled) the disassembly looks like this

 0000000000400500 <foo>: 400500: 48 83 ec 08 sub $0x8,%rsp 400504: 0f b6 35 14 00 00 00 movzbl 0x14(%rip),%esi # 40051f <main> 40050b: bf c4 05 40 00 mov $0x4005c4,%edi 400510: b8 00 00 00 00 mov $0x0,%eax 400515: e8 c6 fe ff ff callq 4003e0 < printf@plt > 40051a: 48 83 c4 08 add $0x8,%rsp 40051e: c3 retq 000000000040051f <main>: 40051f: 48 83 ec 08 sub $0x8,%rsp 400523: e8 d8 ff ff ff callq 400500 <foo> 400528: b8 00 00 00 00 mov $0x0,%eax 40052d: 48 83 c4 08 add $0x8,%rsp 400531: c3 retq 

Everything looks great because% rbp is not displayed at all. However, when the code is compiled with the -m32 -O1 flag (starting with gcc 4.6, -fomit-frame-pointer becomes the default, and my is GCC 4.8.2) or even explicitly uses -fomit-frame-pointer , the disassembly looks like this.

 08048400 <foo>: 8048400: 83 ec 1c sub $0x1c,%esp 8048403: 0f b6 05 1e 84 04 08 movzbl 0x804841e,%eax 804840a: 89 44 24 04 mov %eax,0x4(%esp) 804840e: c7 04 24 c0 84 04 08 movl $0x80484c0,(%esp) 8048415: e8 b6 fe ff ff call 80482d0 < printf@plt > 804841a: 83 c4 1c add $0x1c,%esp 804841d: c3 ret 0804841e <main>: 804841e: 55 push %ebp 804841f: 89 e5 mov %esp,%ebp 8048421: 83 e4 f0 and $0xfffffff0,%esp 8048424: e8 d7 ff ff ff call 8048400 <foo> 8048429: b8 00 00 00 00 mov $0x0,%eax 804842e: c9 leave 804842f: c3 ret 

The foo function looks approximately the same in 32-bit and 64-bit versions. However, unlike the 64-bit one, the first two commands are main (note that it was compiled with -fomit-frame-pointer):

 push %ebp mov %esp, %ebp 

which resembles regular x86 code.
After several experiments, I found that if main calls another function, the code will look like the one mentioned above, and if main does not have a function call, the code will look like 64-bit.

I know this question may seem strange, but I'm just wondering why this difference exists between x86 and x86_64 code and exists only with main() function.

+6
source share
1 answer

This is not related to -fomit-frame-pointer , as far as I know, instead it is the result of stack alignment.

main you need to align the stack (using and $0xfffffff0, %esp ) so that the functions it invokes get the alignment that they expect. This destroys the old esp value, which therefore needs to be saved and restored, so that ret does everything right. (When ret is executed, esp must point to the same place as when entering main : that is, at the return address that was saved on the stack).

So, esp needs to be saved and restored: and why not save the registration register, for example ebp ? In fact, ebp is a good choice, because to execute the desired movl %ebp, %esp/popl %ebp at the end of main there is a dedicated leave command.

In x64, you can expect the stack to be aligned when you main , so alignment and resulting saving and restoring of esp not needed.

+6
source

Source: https://habr.com/ru/post/958572/


All Articles