main() sometimes keeps frame pointer with -fomit-frame-pointer on x86
assembly, c, gcc, x86, x86-64
Solution
This is unrelated to `-fomit-frame-pointer` as far as I know - instead it is a result of stack alignment.
`main` needs to align the stack (with `and $0xfffffff0, %esp`) so that the functions that it calls get the alignment they expect. This destroys the old value of `esp`, which consequently has to be saved and restored so that the `ret` does the right thing. (When the `ret` is executed, `esp` must be pointing at the same location it was upon entry to `main`: that is, at the return address that was saved on the stack).
So `esp` has to be saved and restored: and why not to a callee-save register such as `ebp`? In fact `ebp` is a good choice because there is a dedicated instruction, `leave`, to perform the desired `movl %ebp, %esp/popl %ebp` at the end of `main`.
On x64 the stack can be expected to be aligned upon entry to `main`, so alignment and the resulting saving and restoring of `esp` is unnecessary.
Problem
I accidentally discovered some strange thing about the -fomit-frame-pointer with GCC on x86 when I was doing homework. Look at the following code (which seems quite nonsense but somehow related to how I discovered the problem) ``` #include <stdio.h> void foo(void); int main() { foo(); return 0; } void foo() { printf("0x%x\n", *(unsigned char *)main); } ``` when compiled with `-m64 -O1` flag (-fomit-frame-pointer enabled), the disassembly is like the following ``` 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 fine because %rbp does not show up at all. However when the code is compiled with `-m32 -O1` flag (starting from gcc 4.6 -fomit-frame-pointer becomes default and mine is GCC 4.8.2) or even use `-fomit-frame-pointer` explicitly, the disassembly is like the following. ``` 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 function `foo` looks quite the same in 32 bit and 64 bit. However, unlike the 64-bit one, the first two instructions of main are (notice that it is compiled with -fomit-frame-pointer): ``` push %ebp mov %esp, %ebp ``` which resembles normal x86 code. After several experiments I found that if `main` calls another function, the code will be like the one above, and if there is no function call in `main`, the code will resembles the 64-bit ones. I know this question may seem strange but I'm just curious about why this difference exists between x86 and x86_64 code, and only exists with `main()` function.