Linux环境下的堆栈--调试C程序
完整的調(diào)試過程,跟蹤堆棧變化,32位下。
注意64位和此不同。
?
a.c代碼:
#include <stdio.h>
int main()
{ AFunc(5,6);return 0;
} int BFunc(int i,int j)
{int m = 1;int n = 2;m = i;n = j; return m;
}int AFunc(int i,int j)
{int m = 3;int n = 4; m = i;n = j;BFunc(m,n);return 8;
}
編譯加上調(diào)試信息
#gcc? -g? -o? a a.c
?
要調(diào)試C程序,在編譯時,必須要把調(diào)試信息加到可執(zhí)行文件中。使用編譯器(cc/gcc/g++)的 -g 參數(shù)可以做到這一點。如:
??? > cc -g hello.c -o hello
??? > g++ -g hello.cpp -o hello
如果沒有-g,你將看不見程序的函數(shù)名、變量名,所代替的全是運行時的內(nèi)存地址。
啟動gdb
#gdb a
?
增加斷點
#break *main
運行
#run
步入
#s???? 進入的單步執(zhí)行
如果已經(jīng)進入了某函數(shù),而想退出該函數(shù)返回到它的調(diào)用函數(shù)中,可使用命令finish
#finsh
#n??? 不進入的單步執(zhí)行
查看數(shù)組的值
有時候,你需要查看一段連續(xù)的內(nèi)存空間的值。比如數(shù)組的一段,或是動態(tài)分配的數(shù)據(jù)的大小。你可以使用GDB的“@”操作符,“@”的左邊是第一個內(nèi)存的地址的值,“@”的右邊則你你想查看內(nèi)存的長度。例如,你的程序中有這樣的語句:
int *array = (int *) malloc (len * sizeof (int));
于是,在GDB調(diào)試過程中,你可以以如下命令顯示出這個動態(tài)數(shù)組的取值:
#p *array@len
如果是靜態(tài)數(shù)組的話,可以直接用print數(shù)組名,就可以顯示數(shù)組中所有數(shù)據(jù)的內(nèi)容了。
?
whatis 命令可以顯示某個變量的類型
(gdb) whatis p
type = int *
?
查看匯編
#disas
#bt 查看棧幀
#f 0查看第0幀
#f 1查看第1幀
#f N查看第N幀
之后查看寄存器也會查看對應的寄存器
#i r
之后也會查看對應寄存器內(nèi)容
#x/40xw $esp
查看堆棧底
#x/40xw $ebp
--《深入理解計算機系統(tǒng)(原書第2版)》
開始
1.main函數(shù)中第1個s
ebp的內(nèi)容為0
?
2.main函數(shù)中第2個s,開始調(diào)用A函數(shù)
很明顯esp和ebp變化了,上一步的ebp地址被pusp到新的ebp的內(nèi)容。
?
3.進入A函數(shù)
顯示ebp入棧;
然后esp指向新的ebp
sub $0x18,%esp即esp減少24個地址;0xbffff618-18=0xbffff600
第1幀:
第0幀:
ebp依次保存了:
“上一個ebp的地址? 0xbffff638;
“main函數(shù)中調(diào)用完A函數(shù)后的執(zhí)行地址 0x080483b1”;
“上級函數(shù)傳遞的參數(shù)5保存在ebp的正向地址”;
“上級函數(shù)傳遞的參數(shù)6保存在ebp的正向地址”
將進入AFunc函數(shù)之前的EBP的值入棧保存,這時候的EBP相當于是AFunc上級函數(shù); 的一個現(xiàn)場信息,所以需要保存起來,以便于AFunc返回后上級函數(shù)可以恢復EBP使其指向其調(diào)用; AFunc之前的堆棧位置(當然,這還需要靠恢復ESP來協(xié)助達到這一目的)
?
4.A函數(shù)中int n=4前
0x8(%ebp)的-8個位置存放3;
0x4(%ebp)的-4個位置存放4。
注意:這里如果調(diào)用f 0則后面的i r和x/40xw $ebp都是查看該棧幀
?
5.A函數(shù)m=j前
函數(shù)的局部變量放置在EBP的負偏移處(Negative; Offset)也就是向低地址方向。
esp在0xbffff618,3和4分別在0xbffff610和0xbffff614。
?
6.A函數(shù)n=j之前
?
0x8(ebp)獲取ebp正向地址的值稍后mov到eax寄存器;
然后將eax寄存器中的5移到-0x8(ebp)即ebp的負地址8
0xbffff610處的3已經(jīng)被替換為5
?
7.進入B函數(shù)之前
0xc(%ebp)從ebp高地址獲取6并存儲在ebp的低地址-4位置,然后放到eax寄存器
?0xbffff614處的4已經(jīng)被替換為6
?
8.進入B函數(shù)
按之前的第7步在進入B函數(shù)之前先把參數(shù)5和6從ebp的-4和-8地址上取出存在eax寄存器,然后存儲到esp的正向地址上
esp和ebp存儲新的內(nèi)存地址位置
參數(shù)5和6依次保存在esp的正向地址中
?
8.B函數(shù)n=2之前
第2幀:
第1幀:
第一幀的ebp保存著:
main函數(shù)的ebp地址0xbffff638;
以及main函數(shù)需要繼續(xù)執(zhí)行的地址0x080483b1
?
第0幀:
這是當前B函數(shù)的幀以及對應的寄存器內(nèi)容,其中1被存儲在ebp的-8位置,和之前的A函數(shù)中的過程一樣,沒有新的差異。
?
9.B函數(shù)的m=i之前
?
10.B函數(shù)的n=j之前
?
11.B函數(shù)return之前
?
12.B函數(shù)返回值
leave 將ebp值賦給esp,
pop先前棧內(nèi)的上級函數(shù)棧的基地址給ebp,
恢復原棧基址相當于:
movl %ebp,%esp??
popl %ebp
返回值放在eax寄存器中
?
13.回到A函數(shù)
8賦給eax寄存器
?
14.A返回值,pop出main函數(shù)的ebp
?
15.回到main函數(shù)
?
16.退出main函數(shù)
?
17.進入__libc_start_main ()系統(tǒng)調(diào)用
18.結(jié)束
?
關(guān)于win32環(huán)境下的堆棧參考:Win32 環(huán)境下的堆棧
關(guān)于gdb查看棧參考:?GDB查看棧信息
關(guān)于gdb調(diào)試可以參考:GDB調(diào)試--以匯編語言為例
?GDB 進行調(diào)試 使用心得???
?
總結(jié)
以上是生活随笔為你收集整理的Linux环境下的堆栈--调试C程序的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Win32 环境下的堆栈
- 下一篇: linux平台的链接与加载