GDB堆棧跟蹤與匯編調試
堆棧跟蹤
-
源代碼:

-
對預先編寫的
stack.c文件進行編譯,並且使用CGDB進行調試,對堆棧進行跟蹤,了解該代碼堆棧是如何變化的。

-
在
CGDB中,先設置 main 斷點,接着運行(run),使用frameinfo frame分別查看當前棧幀的簡要信息,以及該棧幀的詳細信息。其中:frame打印出的信息:棧的層編號,當前的函數名,函數參數值,函數所在文件及行號,函數執行到的語句。info frame打印出的信息:函數地址,調用函數的地址,被調用函數的地址,目前的函數是由什么樣的程序語言寫成的、函數參數地址及值、局部變量的地址等等。

-
輸入命令
disassemble,顯示出該代碼(main())的匯編形式

-
info registers,顯示當前(main()處)寄存器值

-
使用
s單步運行程序:-
運行到 f1 函數內,觀察此時堆棧情況

-
運行到 g1 函數內,觀察此時堆棧情況

-
使用
updown,跳轉不同堆棧,查詢其中的堆棧簡要信息

-
-
該代碼中,共存在過3個堆棧,對每個堆棧查詢其詳細信息,觀察堆棧變化:
-
由
main()函數形成的堆棧(#2):

-
由
f1函數形成的堆棧(#1):

-
由
g1函數形成的堆棧(#0):

-
-
根據3個堆棧的詳細信息,畫出大致的堆棧示圖(此時,程序運行在
g1,還沒有返回):

匯編調試
-
GDB指令加上i就顯示匯編代碼,例如:n(ext)i、s(tep)i,其中:(gdb)p/x i:打印變量名為 i 的十六進制值(gdb) display /3i $pc:這是一種設置,設置好了調試過程中每一步都回顯一次,這里是一次顯示 3 行,3 在這里是指一次顯示幾行,不輸入,缺省為1(e)xamine:功能和display差不太多,區別就是display是一種設置,每次跳命令顯示一次,x是主動顯示:x/<n/f/u> <addr>- n選擇從當前地址向后顯示幾個
- f是顯示格式,還有s字符串和i整型
- u表示從當前地址往后請求的字節數,如果不指定的話,GDB默認是4個bytes。u參數可以用下面的字符來代替,b表示單字節,h表示雙字節,w表示四字節,g表示八字節。當我們指定了字節長度后,GDB會從指內存定的內存地址開始,讀寫指定字節,並把其當作一個值取出來。
- 例:x/3uh 0x54320表示,從地址0x54320讀取,h表示以雙字節為單位,3表示三個單位,u表示十六進制
-
main、f1、g13個函數所對應的匯編代碼:



- 設置
display,每一步顯示一行代碼,查看當前main()斷點處初始的寄存器值(主要觀察%eax,%ebx,%esp,%eip)

printf("%d\n,f1(1)+6);,轉到fi函數地址處:

int f1(int x){,建立f1函數的堆棧,保存數據,更新信息:



return g1(x);,保存f1函數的數據,之后跳轉到g1函數的地址:



int g1(int x),建立g1函數的堆棧,保存數據,更新信息:


return x+5;,進行計算:


},銷毀g1函數的堆棧,並且回到從f1函數跳出來的下一條指令:


},銷毀f1函數的堆棧,並且回到從main函數跳出來的下一條指令:


printf("%d\n,f1(1)+6);,進行運算,並且轉到執行printf
的代碼處,使之打印出最終結果:





棧幀變化
- 其中,每條代碼指令為正在執行的代碼,所有后面的%esp、%ebp、%eax以及棧幀情況都是上一條指令的結果,在等待正在執行的指令完成后再更改

