檢視棧資訊
—————
當程式被停住了,你需要做的第一件事就是檢視程式是在哪裡停住的。當你的程式調用了一個函數,函數的位址,函數參數,函數内的局部變量都會被壓入“棧”(Stack)中。你可以用GDB指令來檢視目前的棧中的資訊。
下面是一些檢視函數調用棧資訊的GDB指令:
backtrace
bt
列印目前的函數調用棧的所有資訊。如:
(gdb) bt
#0 func (n=250) at tst.c:6
#1 0x08048524 in main (argc=1, argv=0xbffff674) at tst.c:30
#2 0x400409ed in __libc_start_main () from /lib/libc.so.6
從上可以看出函數的調用棧資訊:__libc_start_main --> main() --> func()
backtrace <n>
bt <n>
n是一個正整數,表示隻列印棧頂上n層的棧資訊。
backtrace <-n>
bt <-n>
-n表一個負整數,表示隻列印棧底下n層的棧資訊。
如果你要檢視某一層的資訊,你需要在切換目前的棧,一般來說,程式停止時,最頂層的棧就是目前棧,如果你要檢視棧下面層的詳細資訊,首先要做的是切換目前棧。
frame <n>
f <n>
n是一個從0開始的整數,是棧中的層編号。比如:frame 0,表示棧頂,frame 1,表示棧的第二層。
up <n>
表示向棧的上面移動n層,可以不打n,表示向上移動一層。
down <n>
表示向棧的下面移動n層,可以不打n,表示向下移動一層。
上面的指令,都會列印出移動到的棧層的資訊。如果你不想讓其打出資訊。你可以使用這三個指令:
select-frame <n> 對應于 frame 指令。
up-silently <n> 對應于 up 指令。
down-silently <n> 對應于 down 指令。
檢視目前棧層的資訊,你可以用以下GDB指令:
frame 或 f
會列印出這些資訊:棧的層編号,目前的函數名,函數參數值,函數所在檔案及行号,函數執行到的語句。
info frame
info f
這個指令會列印出更為詳細的目前棧層的資訊,隻不過,大多數都是運作時的内内位址。比如:函數位址,調用函數的位址,被調用函數的位址,目前的函數是由什麼樣的程式語言寫成的、函數參數位址及值、局部變量的位址等等。如:
(gdb) info f
Stack level 0, frame at 0xbffff5d4:
eip = 0x804845d in func (tst.c:6); saved eip 0x8048524
called by frame at 0xbffff60c
source language c.
Arglist at 0xbffff5d4, args: n=250
Locals at 0xbffff5d4, Previous frame's sp is 0x0
Saved registers:
ebp at 0xbffff5d4, eip at 0xbffff5d8
info args
列印出目前函數的參數名及其值。
info locals
列印出目前函數中所有局部變量及其值。
info catch
列印出目前的函數中的異常處理資訊。
檢視源程式
一、顯示源代碼
GDB 可以列印出所調試程式的源代碼,當然,在程式編譯時一定要加上-g的參數,把源程式資訊編譯到執行檔案中。不然就看不到源程式了。當程式停下來以後,GDB會報告程式停在了那個檔案的第幾行上。你可以用list指令來列印程式的源代碼。還是來看一看檢視源代碼的GDB指令吧。
list <linenum>
顯示程式第linenum行的周圍的源程式。
list <function>
顯示函數名為function的函數的源程式。
list
顯示目前行後面的源程式。
list -
顯示目前行前面的源程式。
一般是列印目前行的上5行和下5行,如果顯示函數是是上2行下8行,預設是10行,當然,你也可以定制顯示的範圍,使用下面指令可以設定一次顯示源程式的行數。
set listsize <count>
設定一次顯示源代碼的行數。
show listsize
檢視目前listsize的設定。
list指令還有下面的用法:
list <first>, <last>
顯示從first行到last行之間的源代碼。
list , <last>
顯示從目前行到last行之間的源代碼。
list +
往後顯示源代碼。
一般來說在list後面可以跟以下這們的參數:
<linenum> 行号。
<+offset> 目前行号的正偏移量。
<-offset> 目前行号的負偏移量。
<filename:linenum> 哪個檔案的哪一行。
<function> 函數名。
<filename:function> 哪個檔案中的哪個函數。
<*address> 程式運作時的語句在記憶體中的位址。
二、搜尋源代碼
不僅如此,GDB還提供了源代碼搜尋的指令:
forward-search <regexp>
search <regexp>
向前面搜尋。
reverse-search <regexp>
全部搜尋。
其中,<regexp>就是正規表達式,也主一個字元串的比對模式,關于正規表達式,我就不在這裡講了,還請各位檢視相關資料。
三、指定源檔案的路徑
某些時候,用-g編譯過後的執行程式中隻是包括了源檔案的名字,沒有路徑名。GDB提供了可以讓你指定源檔案的路徑的指令,以便GDB進行搜尋。
directory <dirname ... >
dir <dirname ... >
加一個源檔案路徑到目前路徑的前面。如果你要指定多個路徑,UNIX下你可以使用“:”,Windows下你可以使用“;”。
directory
清除所有的自定義的源檔案搜尋路徑資訊。
show directories
顯示定義了的源檔案搜尋路徑。
四、源代碼的記憶體
你可以使用info line指令來檢視源代碼在記憶體中的位址。info line後面可以跟“行号”,“函數名”,“檔案名:行号”,“檔案名:函數名”,這個指令會列印出所指定的源碼在運作時的記憶體位址,如:
(gdb) info line tst.c:func
Line 5 of "tst.c" starts at address 0x8048456 <func+6> and ends at 0x804845d <func+13>.
還有一個指令(disassemble)你可以檢視源程式的目前執行時的機器碼,這個指令會把目前記憶體中的指令dump出來。如下面的示例表示檢視函數func的彙編代碼。
(gdb) disassemble func
Dump of assembler code for function func:
0x8048450 <func>: push %ebp
0x8048451 <func+1>: mov %esp,%ebp
0x8048453 <func+3>: sub $0x18,%esp
0x8048456 <func+6>: movl $0x0,0xfffffffc(%ebp)
0x804845d <func+13>: movl $0x1,0xfffffff8(%ebp)
0x8048464 <func+20>: mov 0xfffffff8(%ebp),%eax
0x8048467 <func+23>: cmp 0x8(%ebp),%eax
0x804846a <func+26>: jle 0x8048470 <func+32>
0x804846c <func+28>: jmp 0x8048480 <func+48>
0x804846e <func+30>: mov %esi,%esi
0x8048470 <func+32>: mov 0xfffffff8(%ebp),%eax
0x8048473 <func+35>: add %eax,0xfffffffc(%ebp)
0x8048476 <func+38>: incl 0xfffffff8(%ebp)
0x8048479 <func+41>: jmp 0x8048464 <func+20>
0x804847b <func+43>: nop
0x804847c <func+44>: lea 0x0(%esi,1),%esi
0x8048480 <func+48>: mov 0xfffffffc(%ebp),%edx
0x8048483 <func+51>: mov %edx,%eax
0x8048485 <func+53>: jmp 0x8048487 <func+55>
0x8048487 <func+55>: mov %ebp,%esp
0x8048489 <func+57>: pop %ebp
0x804848a <func+58>: ret
End of assembler dump.