GDB十分钟教程

作者: liigo原文链接: http://blog.csdn.net/liigo/archive/2006/01/17/582231.aspx日期: 2006年1月16日
本文写给主要工作在Windows操作系统下而又需要开发一些跨平台软件的程序员朋友,以及程序爱好者。
GDB是一个由GNU开源组织发布的、UNIX/LINUX操作系统下的、基于命令行的、功能强大的程序调试工具。
GDB中的命令固然很多,但我们只需掌握其中十个左右的命令,就大致可以完成日常的基本的程序调试工作。
** 命令**
** 解释**
** 示例**

file <文件名>
加载被调试的可执行程序文件。因为一般都在被调试程序所在目录下执行GDB,因而文本名不需要带路径。
(gdb) file gdb-sample

r
Run的简写,运行被调试的程序。如果此前没有下过断点,则执行完整个程序;如果有断点,则程序暂停在第一个可用断点处。
(gdb) r

c
Continue的简写,继续执行被调试程序,直至下一个断点或程序结束。
(gdb) c

b <行号>b <函数名称>b *<函数名称>b *<代码地址>d [编号]

b: Breakpoint的简写,设置断点。两可以使用“行号”“函数名称”“执行地址”等方式指定断点位置。其中在函数名称前面加“*”符号表示将断点设置在“由编译器生成的prolog代码处”。如果不了解汇编,可以不予理会此用法。d: Delete breakpoint的简写,删除指定编号的某个断点,或删除所有断点。断点编号从1开始递增。

(gdb) b 8(gdb) b main(gdb) b *main(gdb) b *0x804835c(gdb) d

s, n
s: 执行一行源程序代码,如果此行代码中有函数调用,则进入该函数;n: 执行一行源程序代码,此行代码中的函数调用也一并执行。s 相当于其它调试器中的“Step Into (单步跟踪进入)”;n 相当于其它调试器中的“Step Over (单步跟踪)”。
这两个命令必须在有源代码调试信息的情况下才可以使用(GCC编译时使用“-g”参数)。

(gdb) s(gdb) n

si, ni
si命令类似于s命令,ni命令类似于n命令。所不同的是,这两个命令(si/ni)所针对的是汇编指令,而s/n针对的是源代码。
(gdb) si(gdb) ni

p <变量名称>
Print的简写,显示指定变量(临时变量或全局变量)的值。
(gdb) p i(gdb) p nGlobalVar

display ...undisplay <编号>

display,设置程序中断后欲显示的数据及其格式。例如,如果希望每次程序中断后可以看到即将被执行的下一条汇编指令,可以使用命令“display /i $pc”其中 $pc 代表当前汇编指令,/i 表示以十六进行显示。当需要关心汇编代码时,此命令相当有用。undispaly,取消先前的display设置,编号从1开始递增。

(gdb) display /i $pc(gdb) undisplay 1

i
Info的简写,用于显示各类信息,详情请查阅“help i”。
(gdb) i r

q
Quit的简写,退出GDB调试环境。
(gdb) q

help [命令名称]
GDB帮助命令,提供对GDB名种命令的解释说明。如果指定了“命令名称”参数,则显示该命令的详细说明;如果没有指定参数,则分类显示所有GDB命令,供用户进一步浏览和查询。
(gdb) help display

废话不多说,下面开始实践。
先给出一个示例用的小程序,C语言代码,简单的不能再简单了:
123456789101112131415161718192021222324252627282930313233

//此程序仅作为“GDB十分钟教程”的示例代码, by liigo//Email: liigo@sina.com//blog: http://blog.csdn.net/liigo//WebSite: www.liigo.com#include <stdio.h>int nGlobalVar = 0;int tempFunction(int a, int b){ printf("tempFunction is called, a = %d, b = %d /n", a, b); return (a + b);}int main(){ int n; n = 1; n++; n--; nGlobalVar += 100; nGlobalVar -= 12; printf("n = %d, nGlobalVar = %d /n", n, nGlobalVar); n = tempFunction(1, 2); printf("n = %d", n); return 0;}

请将此代码复制出来并保存到文件 gdb-sample.c 中,然后切换到此文件所在目录,用GCC编译之:
gcc gdb-sample.c -o gdb-sample -g

在上面的命令行中,使用 -o 参数指定了编译生成的可执行文件名为 gdb-sample,使用参数 -g 表示将源代码信息编译到可执行文件中。如果不使用参数 -g,会给后面的GDB调试造成不便。当然,如果我们没有程序的源代码,自然也无从使用 -g 参数,调试/跟踪时也只能是汇编代码级别的调试/跟踪。
下面“gdb”命令启动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 arewelcome 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内部命令引导符,等待用户输入GDB命令。
下面使用“file”命令载入被调试程序 gdb-sample(这里的 gdb-sample 即前面 GCC 编译输出的可执行文件):
(gdb) file gdb-sampleReading symbols from gdb-sample...done.

上面最后一行提示已经加载成功。
下面使用“r”命令执行(Run)被调试文件,因为尚未设置任何断点,将直接执行到程序结束:
(gdb) rStarting program: /home/liigo/temp/test_jmp/test_jmp/gdb-samplen = 1, nGlobalVar = 88tempFunction is called, a = 1, b = 2n = 3Program exited normally.

下面使用“b”命令在 main 函数开头设置一个断点(Breakpoint):
(gdb) b mainBreakpoint 1 at 0x804835c: file gdb-sample.c, line 19.

上面最后一行提示已经成功设置断点,并给出了该断点信息:在源文件 gdb-sample.c 第19行处设置断点;这是本程序的第一个断点(序号为1);断点处的代码地址为 0x804835c(此值可能仅在本次调试过程中有效)。回过头去看源代码,第19行中的代码为“n = 1”,恰好是 main 函数中的第一个可执行语句(前面的“int n;”为变量定义语句,并非可执行语句)。
再次使用“r”命令执行(Run)被调试程序:
(gdb) rStarting program: /home/liigo/temp/gdb-sampleBreakpoint 1, main () at gdb-sample.c:1919 n = 1;

程序中断在gdb-sample.c第19行处,即main函数是第一个可执行语句处。
上面最后一行信息为:下一条将要执行的源代码为“n = 1;”,它是源代码文件gdb-sample.c中的第19行。
下面使用“s”命令(Step)执行下一行代码(即第19行“n = 1;”):
(gdb) s20 n++;

上面的信息表示已经执行完“n = 1;”,并显示下一条要执行的代码为第20行的“n++;”。
既然已经执行了“n = 1;”,即给变量 n 赋值为 1,那我们用“p”命令(Print)看一下变量 n 的值是不是 1 :
(gdb) p n$1 = 1

果然是 1。($1大致是表示这是第一次使用“p”命令——再次执行“p n”将显示“$2 = 1”——此信息应该没有什么用处。)
下面我们分别在第26行、tempFunction 函数开头各设置一个断点(分别使用命令“b 26”“b tempFunction”):
(gdb) b 26Breakpoint 2 at 0x804837b: file gdb-sample.c, line 26.(gdb) b tempFunctionBreakpoint 3 at 0x804832e: file gdb-sample.c, line 12.

使用“c”命令继续(Continue)执行被调试程序,程序将中断在第二个断点(26行),此时全局变量 nGlobalVar 的值应该是 88;再一次执行“c”命令,程序将中断于第三个断点(12行,tempFunction 函数开头处),此时tempFunction 函数的两个参数 a、b 的值应分别是 1 和 2:
(gdb) cContinuing.Breakpoint 2, main () at gdb-sample.c:2626 printf("n = %d, nGlobalVar = %d /n", n, nGlobalVar);(gdb) p nGlobalVar$2 = 88(gdb) cContinuing.n = 1, nGlobalVar = 88Breakpoint 3, tempFunction (a=1, b=2) at gdb-sample.c:1212 printf("tempFunction is called, a = %d, b = %d /n", a, b);(gdb) p a$3 = 1(gdb) p b$4 = 2

上面反馈的信息一切都在我们预料之中,哈哈~~~
再一次执行“c”命令(Continue),因为后面再也没有其它断点,程序将一直执行到结束:
(gdb) cContinuing.tempFunction is called, a = 1, b = 2n = 3Program exited normally.

有时候需要看到编译器生成的汇编代码,以进行汇编级的调试或跟踪,又该如何操作呢?
这就要用到display命令“display /i $pc”了(此命令前面已有详细解释):
(gdb) display /i $pc(gdb)

此后程序再中断时,就可以显示出汇编代码了:
(gdb) rStarting program: /home/liigo/temp/test_jmp/test_jmp/gdb-sampleBreakpoint 1, main () at gdb-sample.c:1919 n = 1;1: x/i $pc 0x804835c <main+16>: movl $0x1,0xfffffffc(%ebp)

看到了汇编代码,“n = 1;”对应的汇编代码是“movl $0x1,0xfffffffc(%ebp)”。
并且以后程序每次中断都将显示下一条汇编指定(“si”命令用于执行一条汇编代码——区别于“s”执行一行C代码):
(gdb) si20 n++;1: x/i $pc 0x8048363 <main+23>: lea 0xfffffffc(%ebp),%eax(gdb) si0x08048366 20 n++;1: x/i $pc 0x8048366 <main+26>: incl (%eax)(gdb) si21 n--;1: x/i $pc 0x8048368 <main+28>: lea 0xfffffffc(%ebp),%eax(gdb) si0x0804836b 21 n--;1: x/i $pc 0x804836b <main+31>: decl (%eax)(gdb) si23 nGlobalVar += 100;1: x/i $pc 0x804836d <main+33>: addl $0x64,0x80494fc

接下来我们试一下命令“b *<函数名称>”。
为了更简明,有必要先删除目前所有断点(使用“d”命令——Delete breakpoint):
(gdb) dDelete all breakpoints? (y or n) y(gdb)

当被询问是否删除所有断点时,输入“y”并按回车键即可。
下面使用命令“b *main”在 main 函数的 prolog 代码处设置断点(prolog、epilog,分别表示编译器在每个函数的开头和结尾自行插入的代码):
(gdb) b *mainBreakpoint 4 at 0x804834c: file gdb-sample.c, line 17.(gdb) rThe program being debugged has been started already.Start it from the beginning? (y or n) yStarting program: /home/liigo/temp/test_jmp/test_jmp/gdb-sampleBreakpoint 4, main () at gdb-sample.c:1717 {1: x/i $pc 0x804834c <main>: push %ebp(gdb) si0x0804834d 17 {1: x/i $pc 0x804834d <main+1>: mov %esp,%ebp(gdb) si0x0804834f in main () at gdb-sample.c:1717 {1: x/i $pc 0x804834f <main+3>: sub $0x8,%esp(gdb) si0x08048352 17 {1: x/i $pc 0x8048352 <main+6>: and $0xfffffff0,%esp(gdb) si0x08048355 17 {1: x/i $pc 0x8048355 <main+9>: mov $0x0,%eax(gdb) si0x0804835a 17 {1: x/i $pc 0x804835a <main+14>: sub %eax,%esp(gdb) si19 n = 1;1: x/i $pc 0x804835c <main+16>: movl $0x1,0xfffffffc(%ebp)

此时可以使用“i r”命令显示寄存器中的当前值———“i r”即“Infomation Register”:
(gdb) i reax 0xbffff6a4 -1073744220ecx 0x42015554 1107383636edx 0x40016bc8 1073834952ebx 0x42130a14 1108544020esp 0xbffff6a0 0xbffff6a0ebp 0xbffff6a8 0xbffff6a8esi 0x40015360 1073828704edi 0x80483f0 134513648eip 0x8048366 0x8048366eflags 0x386 902cs 0x23 35ss 0x2b 43ds 0x2b 43es 0x2b 43fs 0x0 0gs 0x33 51

当然也可以显示任意一个指定的寄存器值:
(gdb) i r eaxeax 0xbffff6a4 -1073744220

最后一个要介绍的命令是“q”,退出(Quit)GDB调试环境:
(gdb) qThe program is running. Exit anyway? (y or n) y

版权所有:liigo.com
转载请事先征得作者liigo同意。
liigo@sina.comwww.liigo.comhttp://blog.csdn.net/liigo/QQ: 175199125

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容

  • 概述 GDB是一个由GNU开源组织发布的、UNIX/Linux操作系统下的、基于命令行的、功能强大的程序调试工具。...
    咕咕鷄阅读 20,700评论 0 8
  • 程序调试的基本思想是“分析现象->假设错误原因->产生新的现象去验证假设”这样一个循环过程,根据现象如何假设错误原...
    Manfred_Zone阅读 16,499评论 0 26
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,442评论 25 707
  • 版权声明:本文为 cdeveloper 原创文章,可以随意转载,但必须在明确位置注明出处! gdb 简介 gdb ...
    登龙zZ阅读 7,527评论 1 15
  • 顾小西突然从床上惊醒,摸着脸上的冷汗,双手不断的颤抖着,他不停的叫着雪儿的名字。却没有任何反应。原来这不是梦,...
    慕子西阅读 226评论 0 1