GDB十分钟教程
GDB十分鐘教程
作者: liigo
原文鏈接:?http://blog.csdn.net/liigo/archive/2006/01/17/582231.aspx
日期: 2006年1月16日
本文寫給主要工作在Windows操作系統(tǒng)下而又須要開發(fā)一些跨平臺(tái)軟件的程序猿朋友,以及程序愛好者。
GDB是一個(gè)由GNU開源組織公布的、UNIX/LINUX操作系統(tǒng)下的、基于命令行的、功能強(qiáng)大的程序調(diào)試工具。
GDB中的命令固然非常多,但我們僅僅需掌握當(dāng)中十個(gè)左右的命令,就大致能夠完畢日常的主要的程序調(diào)試工作。
| ?命令 | ?解釋 | ?演示樣例 |
| file <文件名稱> | 載入被調(diào)試的可運(yùn)行程序文件。 由于一般都在被調(diào)試程序所在文件夾下運(yùn)行GDB,因而文本名不須要帶路徑。 | (gdb) file gdb-sample |
| r | Run的簡寫,執(zhí)行被調(diào)試的程序。 假設(shè)此前沒有下過斷點(diǎn),則執(zhí)行完整個(gè)程序;假設(shè)有斷點(diǎn),則程序暫停在第一個(gè)可用斷點(diǎn)處。 | (gdb) r |
| c | Continue的簡寫,繼續(xù)運(yùn)行被調(diào)試程序,直至下一個(gè)斷點(diǎn)或程序結(jié)束。 | (gdb) c |
| b <行號(hào)> b <函數(shù)名稱> b *<函數(shù)名稱> b *<代碼地址> d [編號(hào)] | b: Breakpoint的簡寫,設(shè)置斷點(diǎn)。兩能夠使用“行號(hào)”“函數(shù)名稱”“運(yùn)行地址”等方式指定斷點(diǎn)位置。 當(dāng)中在函數(shù)名稱前面加“*”符號(hào)表示將斷點(diǎn)設(shè)置在“由編譯器生成的prolog代碼處”。假設(shè)不了解匯編,能夠不予理會(huì)此使用方法。 d: Delete breakpoint的簡寫,刪除指定編號(hào)的某個(gè)斷點(diǎn),或刪除全部斷點(diǎn)。斷點(diǎn)編號(hào)從1開始遞增。 | (gdb) b 8 (gdb) b main (gdb) b *main (gdb) b *0x804835c (gdb) d |
| s, n | s: 運(yùn)行一行源程序代碼,假設(shè)此行代碼中有函數(shù)調(diào)用,則進(jìn)入該函數(shù); n: 運(yùn)行一行源程序代碼,此行代碼中的函數(shù)調(diào)用也一并運(yùn)行。 s 相當(dāng)于其他調(diào)試器中的“Step Into (單步跟蹤進(jìn)入)”; 這兩個(gè)命令必須在有源碼調(diào)試信息的情況下才干夠使用(GCC編譯時(shí)使用“-g”參數(shù))。 | (gdb) s (gdb) n |
| si, ni | si命令相似于s命令,ni命令相似于n命令。所不同的是,這兩個(gè)命令(si/ni)所針對(duì)的是匯編指令,而s/n針對(duì)的是源碼。 | (gdb) si (gdb) ni |
| p <變量名稱> | Print的簡寫,顯示指定變量(暫時(shí)變量或全局變量)的值。 | (gdb) p i (gdb) p nGlobalVar |
| display ... undisplay <編號(hào)> | display,設(shè)置程序中斷后欲顯示的數(shù)據(jù)及其格式。 比如,假設(shè)希望每次程序中斷后能夠看到即將被運(yùn)行的下一條匯編指令,能夠使用命令 “display /i $pc” 當(dāng)中 $pc 代表當(dāng)前匯編指令,/i 表示以十六進(jìn)行顯示。當(dāng)須要關(guān)心匯編代碼時(shí),此命令相當(dāng)實(shí)用。 undispaly,取消先前的display設(shè)置,編號(hào)從1開始遞增。 | (gdb) display /i $pc (gdb) undisplay 1 |
| i | Info的簡寫,用于顯示各類信息,詳情請(qǐng)查閱“help i”。 | (gdb) i r |
| q | Quit的簡寫,退出GDB調(diào)試環(huán)境。 | (gdb) q |
| help [命令名稱] | GDB幫助命令,提供對(duì)GDB名種命令的解釋說明。 假設(shè)指定了“命令名稱”參數(shù),則顯示該命令的具體說明;假設(shè)沒有指定參數(shù),則分類顯示全部GDB命令,供用戶進(jìn)一步瀏覽和查詢。 | (gdb) help display |
廢話不多說,以下開始實(shí)踐。
先給出一個(gè)演示樣例用的小程序,C語言代碼,簡單的不能再簡單了:
| 1 | //此程序僅作為“GDB十分鐘教程”的演示樣例代碼, by liigo |
請(qǐng)將此代碼復(fù)制出來并保存到文件 gdb-sample.c 中,然后切換到此文件所在文件夾,用GCC編譯之:
| gcc gdb-sample.c -o gdb-sample -g |
在上面的命令行中,使用 -o 參數(shù)指定了編譯生成的可運(yùn)行文件名稱為 gdb-sample,使用參數(shù) -g 表示將源碼信息編譯到可運(yùn)行文件里。假設(shè)不使用參數(shù) -g,會(huì)給后面的GDB調(diào)試造成不便。當(dāng)然,假設(shè)我們沒有程序的源碼,自然也無從使用 -g 參數(shù),調(diào)試/跟蹤時(shí)也僅僅能是匯編代碼級(jí)別的調(diào)試/跟蹤。
以下“gdb”命令啟動(dòng)GDB,將首先顯示GDB說明,無論它:
| GNU gdb Red Hat Linux (5.3post-0.20021129.18rh) Copyright 2003 Free Software Foundation, Inc. GDB is free software, covered by the GNU General Public License, and you are welcome to change it and/or distribute copies of it under certain conditions. Type "show copying" to see the conditions. There is absolutely no warranty for GDB. Type "show warranty" for details. This GDB was configured as "i386-redhat-linux-gnu". (gdb)? |
上面最后一行“(gdb) ”為GDB內(nèi)部命令引導(dǎo)符,等待用戶輸入GDB命令。
以下使用“file”命令加載被調(diào)試程序 gdb-sample(這里的 gdb-sample 即前面 GCC 編譯輸出的可運(yùn)行文件):
| (gdb) file gdb-sample Reading symbols from gdb-sample...done. |
上面最后一行提示已經(jīng)載入成功。
以下使用“r”命令運(yùn)行(Run)被調(diào)試文件,由于尚未設(shè)置不論什么斷點(diǎn),將直接運(yùn)行到程序結(jié)束:
| (gdb) r Starting program: /home/liigo/temp/test_jmp/test_jmp/gdb-sample n = 1, nGlobalVar = 88 tempFunction is called, a = 1, b = 2 n = 3 Program exited normally. |
以下使用“b”命令在 main 函數(shù)開頭設(shè)置一個(gè)斷點(diǎn)(Breakpoint):
| (gdb) b main Breakpoint 1 at 0x804835c: file gdb-sample.c, line 19. |
上面最后一行提示已經(jīng)成功設(shè)置斷點(diǎn),并給出了該斷點(diǎn)信息:在源文件 gdb-sample.c 第19行處設(shè)置斷點(diǎn);這是本程序的第一個(gè)斷點(diǎn)(序號(hào)為1);斷點(diǎn)處的代碼地址為 0x804835c(此值可能僅在本次調(diào)試過程中有效)。回過頭去看源碼,第19行中的代碼為“n = 1”,恰好是 main 函數(shù)中的第一個(gè)可運(yùn)行語句(前面的“int n;”為變量定義語句,并不是可運(yùn)行語句)。
再次使用“r”命令運(yùn)行(Run)被調(diào)試程序:
| (gdb) r Starting program: /home/liigo/temp/gdb-sample Breakpoint 1, main () at gdb-sample.c:19 19 n = 1; |
程序中斷在gdb-sample.c第19行處,即main函數(shù)是第一個(gè)可運(yùn)行語句處。
上面最后一行信息為:下一條將要運(yùn)行的源碼為“n = 1;”,它是源碼文件gdb-sample.c中的第19行。
以下使用“s”命令(Step)運(yùn)行下一行代碼(即第19行“n = 1;”):
| (gdb) s 20 n++; |
上面的信息表示已經(jīng)運(yùn)行完“n = 1;”,并顯示下一條要運(yùn)行的代碼為第20行的“n++;”。
既然已經(jīng)運(yùn)行了“n = 1;”,即給變量 n 賦值為 1,那我們用“p”命令(Print)看一下變量 n 的值是不是 1 :
| (gdb) p n $1 = 1 |
果然是 1。($1大致是表示這是第一次使用“p”命令——再次運(yùn)行“p n”將顯示“$2 = 1”——此信息應(yīng)該沒有什么用處。)
以下我們分別在第26行、tempFunction 函數(shù)開頭各設(shè)置一個(gè)斷點(diǎn)(分別使用命令“b 26”“b tempFunction”):
| (gdb) b 26 Breakpoint 2 at 0x804837b: file gdb-sample.c, line 26. (gdb) b tempFunction Breakpoint 3 at 0x804832e: file gdb-sample.c, line 12. |
使用“c”命令繼續(xù)(Continue)運(yùn)行被調(diào)試程序,程序?qū)⒅袛嘣诘诙€(gè)斷點(diǎn)(26行),此時(shí)全局變量 nGlobalVar 的值應(yīng)該是 88;再一次運(yùn)行“c”命令,程序?qū)⒅袛嘤诘谌齻€(gè)斷點(diǎn)(12行,tempFunction 函數(shù)開頭處),此時(shí)tempFunction 函數(shù)的兩個(gè)參數(shù) a、b 的值應(yīng)各自是 1 和 2:
| (gdb) c Continuing. Breakpoint 2, main () at gdb-sample.c:26 26 printf("n = %d, nGlobalVar = %d /n", n, nGlobalVar); (gdb) p nGlobalVar $2 = 88 (gdb) c Continuing. n = 1, nGlobalVar = 88 Breakpoint 3, tempFunction (a=1, b=2) at gdb-sample.c:12 12 printf("tempFunction is called, a = %d, b = %d /n", a, b); (gdb) p a $3 = 1 (gdb) p b $4 = 2 |
上面反饋的信息一切都在我們預(yù)料之中,哈哈~~~
再一次運(yùn)行“c”命令(Continue),由于后面再也沒有其他斷點(diǎn),程序?qū)⒁恢边\(yùn)行到結(jié)束:
| (gdb) c Continuing. tempFunction is called, a = 1, b = 2 n = 3 Program exited normally. |
有時(shí)候須要看到編譯器生成的匯編代碼,以進(jìn)行匯編級(jí)的調(diào)試或跟蹤,又該怎樣操作呢?
這就要用到display命令“display /i $pc”了(此命令前面已有詳解):
| (gdb) display /i $pc (gdb)? |
此后程序再中斷時(shí),就能夠顯示出匯編代碼了:
| (gdb) r Starting program: /home/liigo/temp/test_jmp/test_jmp/gdb-sample Breakpoint 1, main () at gdb-sample.c:19 19 n = 1; 1: x/i $pc 0x804835c <main+16>: movl $0x1,0xfffffffc(%ebp) |
看到了匯編代碼,“n = 1;”相應(yīng)的匯編代碼是“movl $0x1,0xfffffffc(%ebp)”。
而且以后程序每次中斷都將顯示下一條匯編指定(“si”命令用于運(yùn)行一條匯編代碼——差別于“s”運(yùn)行一行C代碼):
| (gdb) si 20 n++; 1: x/i $pc 0x8048363 <main+23>: lea 0xfffffffc(%ebp),%eax (gdb) si 0x08048366 20 n++; 1: x/i $pc 0x8048366 <main+26>: incl (%eax) (gdb) si 21 n--; 1: x/i $pc 0x8048368 <main+28>: lea 0xfffffffc(%ebp),%eax (gdb) si 0x0804836b 21 n--; 1: x/i $pc 0x804836b <main+31>: decl (%eax) (gdb) si 23 nGlobalVar += 100; 1: x/i $pc 0x804836d <main+33>: addl $0x64,0x80494fc |
接下來我們?cè)囈幌旅睢癰 *<函數(shù)名稱>”。
為了更簡明,有必要先刪除眼下全部斷點(diǎn)(使用“d”命令——Delete breakpoint):
| (gdb) d Delete all breakpoints? (y or n) y (gdb) |
當(dāng)被詢問是否刪除全部斷點(diǎn)時(shí),輸入“y”并按回車鍵就可以。
以下使用命令“b *main”在 main 函數(shù)的 prolog 代碼處設(shè)置斷點(diǎn)(prolog、epilog,分別表示編譯器在每一個(gè)函數(shù)的開頭和結(jié)尾自行插入的代碼):
| (gdb) b *main Breakpoint 4 at 0x804834c: file gdb-sample.c, line 17. (gdb) r The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /home/liigo/temp/test_jmp/test_jmp/gdb-sample Breakpoint 4, main () at gdb-sample.c:17 17 { 1: x/i $pc 0x804834c <main>: push %ebp (gdb) si 0x0804834d 17 { 1: x/i $pc 0x804834d <main+1>: mov %esp,%ebp (gdb) si 0x0804834f in main () at gdb-sample.c:17 17 { 1: x/i $pc 0x804834f <main+3>: sub $0x8,%esp (gdb) si 0x08048352 17 { 1: x/i $pc 0x8048352 <main+6>: and $0xfffffff0,%esp (gdb) si 0x08048355 17 { 1: x/i $pc 0x8048355 <main+9>: mov $0x0,%eax (gdb) si 0x0804835a 17 { 1: x/i $pc 0x804835a <main+14>: sub %eax,%esp (gdb) si 19 n = 1; 1: x/i $pc 0x804835c <main+16>: movl $0x1,0xfffffffc(%ebp) |
此時(shí)能夠使用“i r”命令顯示寄存器中的當(dāng)前值———“i r”即“Infomation Register”:
| (gdb) i r eax 0xbffff6a4 -1073744220 ecx 0x42015554 1107383636 edx 0x40016bc8 1073834952 ebx 0x42130a14 1108544020 esp 0xbffff6a0 0xbffff6a0 ebp 0xbffff6a8 0xbffff6a8 esi 0x40015360 1073828704 edi 0x80483f0 134513648 eip 0x8048366 0x8048366 eflags 0x386 902 cs 0x23 35 ss 0x2b 43 ds 0x2b 43 es 0x2b 43 fs 0x0 0 gs 0x33 51 |
當(dāng)然也能夠顯示隨意一個(gè)指定的寄存器值:
| (gdb) i r eax eax 0xbffff6a4 -1073744220 |
最后一個(gè)要介紹的命令是“q”,退出(Quit)GDB調(diào)試環(huán)境:
| (gdb) q The program is running. Exit anyway? (y or n) y |
?
版權(quán)全部:liigo.com
轉(zhuǎn)載請(qǐng)事先征得作者liigo允許。
liigo@sina.com
www.liigo.com
http://blog.csdn.net/liigo/
QQ: 175199125
?
總結(jié)
- 上一篇: UEditor文本浏览器,引号加斜杠解决
- 下一篇: Skype for Business S