天天看點

如何手工展開函數棧來定位問題

作者:[email protected]

部落格: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的預設行為。

繼續閱讀