Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

main() sometimes keeps frame pointer with -fomit-frame-pointer on x86

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.

like image 421
John Ding Avatar asked Nov 21 '13 15:11

John Ding


People also ask

In which x86 register is the frame pointer normally stored?

A frame pointer (the ebp register on intel x86 architectures, rbp on 64-bit architectures) contains the base address of the function's frame.

What does the stack pointer in x86 like processors represent?

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.

Where is the frame pointer stored?

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.

Is frame pointer same as stack pointer?

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.


1 Answers

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.

like image 133
gsg Avatar answered Oct 25 '22 22:10

gsg