GDB堆棧跟蹤與匯編調試
堆棧跟蹤
-
源代碼:
-
對預先編寫的
stack.c
文件進行編譯,並且使用CGDB
進行調試,對堆棧進行跟蹤,了解該代碼堆棧是如何變化的。
-
在
CGDB
中,先設置 main 斷點,接着運行(run),使用frame
info frame
分別查看當前棧幀的簡要信息,以及該棧幀的詳細信息。其中:frame
打印出的信息:棧的層編號,當前的函數名,函數參數值,函數所在文件及行號,函數執行到的語句。info frame
打印出的信息:函數地址,調用函數的地址,被調用函數的地址,目前的函數是由什么樣的程序語言寫成的、函數參數地址及值、局部變量的地址等等。
-
輸入命令
disassemble
,顯示出該代碼(main())的匯編形式
-
info registers
,顯示當前(main()處)寄存器值
-
使用
s
單步運行程序:-
運行到 f1 函數內,觀察此時堆棧情況
-
運行到 g1 函數內,觀察此時堆棧情況
-
使用
up
down
,跳轉不同堆棧,查詢其中的堆棧簡要信息
-
-
該代碼中,共存在過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
、g1
3個函數所對應的匯編代碼:
- 設置
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以及棧幀情況都是上一條指令的結果,在等待正在執行的指令完成后再更改