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.
A frame pointer (the ebp register on intel x86 architectures, rbp on 64-bit architectures) contains the base address of the function's frame.
Stack registers in x86 In 8086, the main stack register is called stack pointer - SP. The stack segment register (SS) is usually used to store information about the memory segment that stores the call stack of currently executed program.
The frame pointer is stored in register $30, also called $fp. A stack frame consists of the memory on the stack between the frame pointer and the stack pointer. Under the calling convention, the following steps are necessary to call a procedure: Pass the arguments.
The compiler passes parameters and return variables in a block of memory known as a frame. The frame is also used to allocate local variables. The stack elements are frames. A stack pointer (sp) defines the end of the current frame, while a frame pointer (fp) defines the end of the last frame.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With