歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> GCC “-fomit-frame-pointer”編譯選項的含義

GCC “-fomit-frame-pointer”編譯選項的含義

日期:2017/3/1 9:59:58   编辑:Linux編程
優化你的軟件時,發覺"-fomit-frame-pointer"這個選項還是蠻有用的。

GCC手冊上面這麼說:
Don't keep the frame pointer in a register for functions that don't need one. This avoids the instructions to save, set up and restore frame pointers; it also makes an extra register available in many functions. It also makes debugging impossible on some machines.

On some machines, such as the VAX, this flag has no effect, because the standard calling sequence automatically handles the frame pointer and nothing is saved by pretending it doesn't exist. The machine-description macro "FRAME_POINTER_REQUIRED" controls whether a target machine supports this flag.

這裡,引入了一個"frame pointer"的概念,什麼是"stack frame pointer(SFP)"呢?

我們知道,backtrace是利用堆棧中的信息把函數調用關系層層遍歷出來的,其中這裡的堆棧信息就是SFP。
一般情況下,每一個函數都包含一個堆棧邊界指針,也就是說會存在一個棧底和棧頂指針。在X86下,假設堆棧由上往下發展,棧底大地址而棧頂小地址,那麼,通常情況下,寄存器ESP為棧頂指針,而EBP就為棧底指針。而EBP和ESP之間的空間就是這個函數的stack frame。
GCC在默認情況下會在每個函數的開始加入一些堆棧設置代碼,而在函數退出的時候恢復原來的樣子,SFP就是在這個時候設置的。還是看一下這個時候的匯編代碼吧 ;-)

環境:X86+RedHat 9.0,gcc 3.2.2

源文件如下:

$ cat test.c
void a(unsigned long a, unsigned int b)
{
unsigned long i;
unsigned int j;

i = a;
j = b;

i++;

j += 2;

}

默認編譯選項:
$ gcc -c test.c -o with_SFP.o

反匯編後是這個樣子:
$ objdump -D with_SFP.o

with_SFP.o: file format elf32-i386

Disassembly of section .text:

00000000 <a>:
0: 55 push %ebp
1: 89 e5 mov %esp,%ebp
3: 83 ec 08 sub $0x8,%esp
6: 8b 45 08 mov 0x8(%ebp),%eax
9: 89 45 fc mov %eax,0xfffffffc(%ebp)
c: 8b 45 0c mov 0xc(%ebp),%eax
f: 89 45 f8 mov %eax,0xfffffff8(%ebp)
12: 8d 45 fc lea 0xfffffffc(%ebp),%eax
15: ff 00 incl (%eax)
17: 8d 45 f8 lea 0xfffffff8(%ebp),%eax
1a: 83 00 02 addl $0x2,(%eax)
1d: c9 leave
1e: c3 ret
Disassembly of section .data:

可以看到函數ENTER時首先把上一層函數的EBP入棧,設置本函數的EBP,然後會根據臨時變量的數量和對齊要求去設置ESP,也就產生了函數的stack frame。
我們再看看函數的返回:"leave"指令相當於"mov %ebp,%esp;pop %ebp",也就是ENTER是兩條指令的恢復過程,所以,後面的"ret"指令和"call"指令對應。
這裡backtrace就可以根據現有函數EBP指針得知上一個函數的EBP----棧底再往上保存著上一個函數的EBP和EIP,然後就可以得知函數調用的路徑。
Copyright © Linux教程網 All Rights Reserved