部落格:blog.focus-linux.net linuxfocus.blog.chinaunix.net
當程式crash的時候,我們可以通過coredump檔案,來定位問題。比如使用bt指令可以完整的展開函數的調用棧。但是有些時候,部分棧的資料可能被損壞,導緻gdb無法直接顯示函數的調用棧。那麼這時就需要我們手工展開函數棧。
關于x86的函數調用棧的示意圖基本如下圖所示:

關于參數的壓棧順序,上圖為cdecl方式,這個可以通過編譯選項修改。GCC預設使用cdecl。
下面看一下例子:
#include <stdlib.h>
#include <stdio.h>
static int test(int a, int b, int c)
{
return a+b+c;
}
int main()
int a = 1;
int b = 2;
int c = 3;
int d = test(a, b, c);
printf("%d\n", d);
return 0;
編譯:gcc -g -Wall test.c
進入test,檢視函數調用棧:
Breakpoint 1, test (a=1, b=2, c=3) at test.c:7
7 return a+b+c;
Missing separate debuginfos, use: debuginfo-install glibc-2.11-2.i686
(gdb) bt
#0 test (a=1, b=2, c=3) at test.c:7
#1 0x08048412 in main () at test.c:16
那麼現在檢視一下寄存器:
eax 0x1 1
ecx 0x2c0187d8 738297816
edx 0x1 1
ebx 0x73fff4 7602164
esp 0xbffff048 0xbffff048
ebp 0xbffff048 0xbffff048
esi 0x0 0
edi 0x0 0
eip 0x80483c7 0x80483c7 <test+3>
eflags 0x286 [ PF SF IF ]
cs 0x73 115
ss 0x7b 123
ds 0x7b 123
es 0x7b 123
fs 0x0 0
gs 0x33 51
得到ebp的位址為0xbffff048,現在檢查這個位址的記憶體
(gdb) x /8x 0xbffff048
0xbffff048: 0xbffff078 0x08048412 0x00000001 0x00000002
0xbffff058: 0x00000003 0x0073fff4 0x00000001 0x00000002
下面分析一下這些記憶體的内容:
1. 0xbffff078:為test的調用者,即main函數的bp位址;BP位址即為該函數的棧頂指針。
2. 0x08048412:為test的傳回位址,與前面的bt的輸出相符;
3. 後面的0x00000001,0x00000002,0x00000003,為傳給test的三個參數,且參數順序為由右向左壓棧——注意這個順序是可以通過改變編譯參數改變的。
回到main中,驗證一下bp寄存器的内容:
0x08048412 in main () at test.c:16
16 int d = test(a, b, c);
Value returned is $2 = 6
(gdb) info registers
eax 0x6 6
ecx 0x39ff7a48 973044296
esp 0xbffff050 0xbffff050
ebp 0xbffff078 0xbffff078
可見BP的位址确實為0xbffff078,與之前的分析相符。
注:關于壓棧順序,參數的傳遞方式等等,都可以通過編譯選項來指定或者禁止的。本文的情況為GCC的預設行為。