Chinaunix首页 | 论坛 | 博客
  • 博客访问: 340998
  • 博文数量: 54
  • 博客积分: 446
  • 博客等级: 下士
  • 技术积分: 821
  • 用 户 组: 普通用户
  • 注册时间: 2011-04-30 17:37
文章分类

全部博文(54)

文章存档

2015年(35)

2014年(19)

我的朋友

分类: C/C++

2015-04-04 11:00:56

远程调试

有时候需要调试的程序并不在本机上,GDB是支持远程调试的,具体命令如下:

(gdb) target remote IP:PORT

这时,本机的GDB客户端可以远程连接到被调试机器上,对程序进行调试。当然,前提是在远端的机器上,必须要启动好GDB服务端程序。

运行程序

如果是用GDB把程序加载起来,而不是attach到一个已经运行的进程上的话,默认情况下程序是不会运行的,可以通过以下命令将程序执行起来:

(gdb) run [ARGS]

在命令的后面可以跟随发送给程序的参数,参数可以包含Shell通配符(*,[…]等),和输入输出重定向符(<,>,>>)。如果你使用不带任何参数的run命令,GDB就再次使用你上次执行run命令时给的参数。

如果想查看当前默认的参数是什么,可以使用这条命令:

(gdb) show args

如果想修改当前的默认参数,可以使用以下的命令:

(gdb) set args ARGS

修改过后,再次不带参数执行run命令时,就会使用新的默认参数。当然,如果接着执行的run命令带了参数,则新带的参数会覆盖当前的默认参数。

run命令会使程序一直运行,直到碰到第一个断点才会停止下来。而很多情况下,调试的时候会想执行到 main函数先暂停一下,这种情况下,我们可以使用start 命令:

 (gdb) start [ARGS]

使用方法和run命令完全相同。


程序一旦运行起来以后,可以通过下面的命令查看当前程序运行的状态:

(gdb)info program

这条命令将告诉你,现在程序的状态是什么,停在什么位置,因为什么原因停下来的(断点、单步等),是不是已经异常退出了,退出的原因是什么。

添加断点

断点有两种,一种是永久断点,就是设置了之后,除非显式的将其删除,否则一直存在;还有一种是临时断点,就是一旦断在那个断点之后,该断点会自动删除,说白了就是只能断一次的断点。可以通过break命令设置永久断点,而tbreak命令设置临时断点。

breaktbreak的命令格式完全一样,这里只介绍break命令,具体使用方法如下:

(gdb) break LOCATION [CONDITION]

断点的位置可以是本地代码的行号(LineNumber):

(gdb) break 10 #在本地代码的第10行设置断点

也可以是指定代码源文件中代码的行号(FileName:LineNumber):

(gdb) break main.c:20 #在main.c源文件中的第20行代码设置断点

也可以是本地代码中的某一个函数(FunctionName):

(gdb) break pcap_parse #在本地代码的pcap_parse函数处设置断点

也可以是指定代码源文件中的一个函数(FileName:FunctionName):

(gdb) break tcpdump.c:main #在tcpdump.c源文件中的 main函数处设置断点

也可以是某一个指令的地址

(gdb) break *0x12345678 #在地址为0x12345678的指令处设置断点

注意,在对某一个地址的指令下断点时前面要加“*”。

另外,break命令是可以指定触发条件的,即只有当执行到断点指定指令,且条件表达式满足时断点才生效。例如:

(gdb) break 50 if size>0 #只有在size变量大于0的情况下断点才起效

而如果要删除指定编号断点的触发条件可以使用condition命令:

(gdb) condition NUMBER

例如:

(gdb) condition 1 #删除断点编号为1上的触发条件。

注意,这条命令只会删除断点上的触发条件,并不会删除断点本身。

 

有时候希望给一批函数加断点,这些函数名都有一些共同的特点,这时候可以使用rbreak命令:

(gdb) rbreak REGEXP

这个命令的参数是一个正则表达式,作用是给代码中所有函数名匹配这个正则表达式的函数添加断点,例如:

(gdb) rbreak pcap_* #给所有函数名以pcap_打头的函数添加断点

添加监视点

监视点(Watch Point),顾名思义,其用途一般是观察一个变量、某个内存地址处存放的数值或者是一个表达式的值,是否被程序读取或修改了。可以使用如下命令设置程序中的监视点:

(gdb) watch [EXPR]

表示设置一个监视点,当所指定的表达式EXPR的值被修改了,则程序会停止。

(gdb) rwatch [EXPR]

表示设置一个监视点,当所指定的表达式EXPR的值被读取了,则程序会停止。

(gdb) awatch [EXPR]

表示设置一个监视点,当所指定的表达式EXPR的值被读取或修改了,都会让程序停止。

所谓表达式,含义非常丰富,可以是一个具体的变量:

(gdb) watch i #当变量i被修改了停止程序

也可以是一个内存地址处存放的值:

(gdb) watch *(int *)0x12345678 #当存放在内存地址处的int值被修改了停止程序

还有可能是一个复杂的表达式:

(gdb) watch x+ y #当表达式的值改变时停止程序

注意,如果当前跑的代码已经超出了要监视变量的作用域范围,则那个对应的监视点会被自动删除掉。

添加捕捉点:

GDB除了可以根据代码的位置以及某个变量的改变为条件使得代码执行中断外,还有一个非常强大的功能,就是当某一个特别的事件发生的时候,也可以捕捉到,并暂停程序。具体来说,可以使用如下的命令:

(gdb) catch [EVENT]

上面这条命令的意思是,当对应的EVENT事件发生时,停住程序。EVENT可以是下面的内容:

1)throw:当程序抛出一个异常时;

2)catch:当程序捕获到一个异常时;

3)exec:当程序调用exec时;

4)fork:当程序调用fork时;

5)vfork:当程序调用vfork时;

6)syscall:当程序调用系统调用(system calls)时。

查看断点/监视点/捕捉点

想查看目前在程序中已经设置了哪些断点(这里的断点是广义的概念,包含普通意义上的断点、监视点或捕捉点),可以通过下面的命令:

(gdb) info breakpoints

输出大致如下:

共有六个域:


1)  Num:表示这个断点的编号,可以用这个编号号对某个断点做单独的操作,如删除、禁用等;

2)  Type:可以是普通意义上的断点(breakpoint)、监视点(watchpoint)或捕捉点(catchpoint);

3)  Disp:表示该断点在断下来一次后怎么处理,是继续保持(keep),还是删除(del);

4)  Enb:表示该断点是否是打开的,如果打开的就是“y”,如果被关闭了就是“n”;

5)  Address:断点在内存中的位置,也就是要断在的那条指令的地址;

6)  What:如果是普通意义上的断点的话,则这里显示断点所在代码的位置,如果调试的程序不是debug版本的,则这项显示不出来;如果是监视点的话,则显示要监视的表达式;如果是捕捉点的话,则显示要捕捉的事件。

 

前面的命令会显示当前设置的所有断点、监视点和捕捉点。如果只想看监视点的话,可以用下面的命令:

(gdb)info watchpoints

删除断点/监视点/捕捉点

可以删除已经添加到程序中的断点(这里的断点是广义的概念,包含普通意义上的断点、监视点或捕捉点),命令如下:

(gdb) delete [NUMBER]

如果不接任何参数,则表示删除所有程序中的断点。如果想删除某一个特定的断点,只需要加上相应的编号。例如:

(gdb) delete #删除所有的断点

(gdb) delete 2 #删除断点号为2的断点

 

除了按断点号来删除断点之外,还可以删除指定位置上的断点,命令如下:

(gdb) clear [LOCATION]

如果不加任何参数的话,表示删除当前执行代码位置处的所有断点。当然,还可以带上一个位置作为参数,这样的话,就会清除在指定位置上存在的所有断点。代码位置可以是行号、函数名、内存地址等(和在break命令中参数中所谓的代码位置定义相同)。

禁用/打开断点/监视点/捕捉点

在调试的时候,有时想禁用某些断点(同样这里的断点是广义的概念,包含普通意义上的断点、监视点或捕捉点),但又不想删除,因为以后可能这些断点还要用,如果删除了以后还得再加上,比较麻烦。这时候可以使用GDB中禁用断点的命令:

(gdb) disable [NUMBER]

如果不加任何参数,则表示禁用程序中所有设置的断点。如果只是想禁用某个断点,只需要加上断点号。例如:

(gdb) disable #禁用所有的断点

(gdb) disable 3 #禁用断点号为3的断点

如果想打开前面被禁用的断点,可以用下面的命令:

(gdb) enable [NUMBER]

用法和禁用断点相同。

查看寄存器的值

GDB支持查看被调试进程当前常用寄存器的值,命令如下:

(gdb) info registers[REGISTER]

如果不加任何参数,则显示所有寄存器的值。如果只想看某个寄存器的值,可以在参数中指定。例如:

(gdb) info registers#查看所有寄存器当前的值

(gdb) info registers pc #只查看PC寄存器当前的值

前面的命令只会显示常用的寄存器值,而如果想查看所有寄存器的值(包括浮点寄存器),可以使用如下的命令:

(gdb) info all-registers

 

同样可以使用print命令来访问寄存器的情况,只需要在寄存器名字前加一个$符号就可以了:

(gdb) print $REGISTER

例如:

(gdb) print $pc #同样查看PC寄存器当前的值

print是一个非常强大的命令,有很多种用法,后面还会提到。

修改寄存器的值

在调试的过程中,有时候会修改被调试程序的寄存器的值,从而达到改变程序流程的目的。这时候可以使用如下命令:

(gdb) set $REGISTER=VALUE

注意,在要修改的寄存器前面,要加上“$”。例如:

(gdb) set $r0 =1 #将r0寄存器的值修改成1

查看内存的值

GDB支持查看指定地址处内存中存放的值,具体的命令如下:

(gdb) x[/FMT] ADDRESS

FMT是可选的参数,主要用来告诉GDB以什么格式解析内存中的值以及显示多少数据,其共由三部分组成:

1)  要显示的数据量,具体显示多少位要看后面指定的长度数据

2)  显示的格式,共有以下几种情况:

a)        ooctal):8进制数

b)        xhex):16进制数

c)        ddecimal):10进制数

d)        tbinary):2进制数

e)        ffloat):浮点数

f)         iinstruction):指令

g)        cchar):单个字符

h)        sstring):字符串

3)  数据单元的长度,共有以下几种情况:

a)        bbyte):以1个字节为长度显示数据

b)        hhalfword):以半字,也就是2个字节为长度显示数据

c)        wword):以字,也就是4个字节为长度显示数据

d)        ggiant):以8个字节为长度显示数据

下面举一些例子:

(gdb) x /xw 0x80040000 #以16进制显示指定地址处的数据

(gdb) x /8s 0x86468700 #显示指定地址处开始的8个字符串

(gdb) x /50i main #显示main函数开头的50条指令

修改内存的值

除了查看某个内存地址所存的值外,GDB还支持直接修改内存的值,命令格式如下:

(gdb) set *ADDRESS=VALUE

关于地址部分,可以辅助指定一下其存值的类型,从而方便赋值,例如:

(gdb) set *0xb6d2a908=0 #将该地址指向的字节设置为0

(gdb) set *(int *)0xb6daaaec=15 #将该地址指向的整数设置为15

(gdb) set *(int**)0x8048a548=0x55aa55aa  #将该地址指向的地址设置为新值

查看调用堆栈

如果想在程序暂停时,显示当前函数调用的栈帧信息,可以使用backtrace命令:

(gdb) backtrace [full] [NUMBER]

如果命令不带任何参数的话,则默认显示所有栈帧的层编号和位置等信息。如果带有参数full的话,则还将显示栈帧内存放的所有局部变量以及函数返回值信息。如果带有大于0的数字NUMBER的话,则表示只显示开头的NUMBER个栈帧;而如果这个数字小于0的话,则表示只显示最后NUMBER个栈帧。注意,这里的NUMBER指的是显示几个栈帧,并不是指栈帧的层编号(所以不可能是0)。例如:

(gdb) backtrace 1 #只显示最上面的1个栈帧的概要信息

(gdb) backtrace full -2 #只显示最后的2个栈帧的详细信息

此外,与backtrace等效的命令还有whereinfo stack

(gdb) where [full] [NUMBER]

(gdb) info stack [full] [NUMBER]

它们的用法与backtrace完全一样,显示结果也一样。

 

backtrace命令只会显示栈帧的信息,并不会改变当前选中的栈帧。不切换的情况下,默认当前栈帧指的是当前执行函数使用的栈帧。如果要切换当前栈帧,可以使用下面的命令:

(gdb) frame [NUMBER]

如果不加任何参数的话,会显示当前选中栈帧的信息。如果带一个数字参数NUMBER的话(从0开始),则表示选中那个层编号为NUMBER的栈帧(修改当前栈帧为这个栈帧)。

同时,也可以相对当前栈帧向上移动:

(gdb) up [NUMBER]

如果不接参数,表示相对当前栈帧,向上移动一层。如果带了NUMBER参数,则表示相对当前栈帧,向上移动NUMBER层。

还可以向下移动:

(gdb) down [NUMBER]

例如,假设当前栈帧处于第1层,则:

(gdb) up #当前栈帧处于第2层

(gdb) down 2 #当前栈帧处于第0层,即最上层

签名的命令,在每次切换栈帧的时候都会打印出移动到的栈帧的概要信息。如果你不想让其打出信息,可以使用以下三个对应的命令:

(gdb) select-frame [NUMBER] #对应于 frame 命令

(gdb) up-silently [NUMBER] #对应于 up 命令

(gdb) down-silently [NUMBER] #对应于 down 命令

 

还有一点,前面提到使用backtrace full命令可以查看栈帧的详细信息,但前提是必须要有被调试程序的符号表信息。如果没有符号表信息(比如要调试一个release版的程序),肯定是没法知道栈中保存的当前函数用到的局部变量到底有哪些,但是理论上任然可以得到比如局部变量、入参、函数返回地址等在栈中存放的地址。为此,GDB提供了如下的命令:

(gdb) info frame [NUMBER]

这条命令在没有符号表信息的情况下,任然可以尽可能多的获得栈中的信息。如果不加参数,就显示当前选中栈的信息,加上一个NUMBER参数就显示那个层编号为NUMBER的栈帧的信息。

 

另外,还有一个命令,如果想查看当前栈帧中的异常处理器,也就是显示当前函数中的异常处理信息,可以使用下面的命令:

(gdb) info catch

不要搞错了,这个命令不是用来显示捕捉点信息的。

查看变量的值

在GDB中,可以随时查看程序中以下三种变量的值:

1)全局变量(所有文件可见的);

2)静态全局变量(当前文件可见的);

3)局部变量(当前函数作用域中可见的,存放在当前函数的栈帧中)。

可以通过print命令查看某个具体变量的值:

(gdb) print VAR

参数就是你要显示的变量名。如果你需要查看的变量是一个数组,存储在一段连续的内存空间中,则可以使用GDB的“@”操作符,其左边是第一个内存的地址的值,右边是想查看内存的长度。具体命令格式如下:

(gdb) print *VAR@LEN

这条命令将显示数组变量VAR中前LEN个元素的值

 

如果想显示当前栈帧中包含的所有局部变量的值,可以使用如下命令:

(gdb) info locals

可以通过前面介绍的frame命令查看当前栈帧的信息。info locals命令只能显示局部变量的值,如果想看全局变量的值,只能使用print命令。

而如果想查看函数入参变量的值,可以使用下面的命令:

(gdb) info args

这条命令会查找存放于当前栈帧中的入口参数,并显示出来。注意,不要和前面的show args命令混淆了。

 

如果想看某个变量在代码中被定义的类型,可以用如下命令:

(gdb) whatis VAR

自动显示

调试的时候进场碰到这种情况,每次单步执行完成之后都想查看某一个变量或内存位置当前存放的值,每次再print太麻烦了。这时候,可以使用display命令,从而当程序停住或在单步跟踪时自动显示指定变量的值。命令格式如下:

(gdb) display[/FMT] EXPR

FMT是可选参数,主要用来告诉GDB以什么格式解析内存中的值以及显示多少数据,其定义和前面介绍过的x命令中的相同,这里不再赘述。

EXPR是一个表达式,

当你用display设定好了一个或多个表达式后,只要你的程序被停下来,GDB就会自动帮你显示所设置的这些表达式当前的值。

 

如果想看当前设置了哪些要自动显示的表达式,可以用下面的命令:

(gdb) info display

该命令将显示三部分信息:

1) Num:对应该自动显示表达式的编号(后面可以用这个编号来禁用或者删除这个自动显示表达式);

2) Enb:表示这个自动显示表达式当前是有效的(y),还是禁用的(n);

3) Expression:具体要自动显示的表达式。

 

如果想删除自动显示表达式,可以用下面的命令:

(gdb) undisplay [NUMBER]

(gdb) delete display [NUMBER]

上面的两条命令是完全等效的。如果不带任何参数的话,默认会删除所有的自动显示表达式。而如果带一个NUMBER作为参数,表示只删除编号为NUMBER的那个自动显示表达式。

 

如果不想删除自动表达式,只是想先将其禁用,可以用下面的命令:

(gdb) disable display [NUMBER]

如果不带任何参数的话,将默认禁用所有的自动显示表达式。如果带一个NUMBER作为参数的话,表示只禁用编号为NUMBER的那个自动显示表达式。

将自动显示表达式禁用后,其就暂时失效了,但是任然可以通过info display命令查到它,如果将其删除了就查不到了。暂时禁用后,后面还可以将其恢复,命令如下:

(gdb) enable display [NUMBER]

同样,如果不带任何参数的话,将默认恢复所有的自动显示表达式。如果带一个NUMBER作为参数的话,表示只恢复编号为NUMBER的那个自动显示表达式。

调试控制

程序一旦断到了你设置的断点位置,除了查看程序当前的状态之外,最想做的应该还是按照你想要的顺序执行程序,边执行边分析。

首先,我们来看看如何单步执行,GDB中提供了四个相关的命令,先来看其中两个:

(gdb) next

(gdb) step

这两条命令都是单步执行源代码中的一行代码。它们的区别是,如果下一个单步执行的代码是一个函数调用的话,next命令不会执行到该被调用函数的函数内部,会把函数调用当成一条普通的语句来处理(相当于Step Over);而step命令则相反,会进入到被调用函数的内部继续执行(相当于Step Into)。所以,如果下一个单步执行的代码不是函数调用的话,这两个命令就没有任何差别。

前面的两个命令都是执行源代码中的一行。而如果要逐条执行汇编指令(在没有被调试程序源码的情况下尤为有用),可以使用下面两条命令:

(gdb) nexti

(gdb) stepi

这两条命令只会执行下一条汇编/机器指令。i代表instruction,即汇编/机器指令,同一条源码可能被编译成多条机器指令。它们两的区别同样在对函数调用指令的处理上,nexti不会进入被调用函数,而stepi会进入被调用函数。

 

除了前面的单步执行外,也可以指定代码执行到某个位置就停止。

如果当前代码停在某个函数内部,同时想让程序一直执行到当前函数结束返回,即跳出当前函数回到调用该函数的代码位置,可以使用如下命令:

(gdb) finish

 

也可以让代码从当前停止的位置一直继续运行,直到到达指定的位置结束,命令如下:

(gdb) until [LOCATION]

如果不加任何参数的话,则表示当程序执行的代码位置超过了当前代码位置(也就是敲until命令时的代码位置)的时候,代码将停止运行。这个命令有一个特别有用处的地方,如果现在你的代码正处于一个循环当中,这时你想让程序一直运行直到这个循环结束。当然可以一直不停的敲next命令直到循环结束,但要是循环1万次呢,那不累死了。这时,可以用next命令将当前的代码执行位置移动到循环的最末端,然后敲入until命令,无论要循环执行多少遍都没有问题。

until命令也可以跟一个代码位置作为参数,代码位置可以是行号、函数名、内存地址等(和在break命令中参数中所谓的代码位置定义相同)。程序将一直运行,直到指定的那个位置。

使用until命令时,还有一点需要特别注意,如果程序退出当前调用栈时,程序也会停止。比如,如果在执行完until命令后,但是还没到指定的位置,这时函数返回了(return),则GDB也会在返回的代码处停下来。但这只针对退出当前调用栈的情况,对于调用栈增加的情况(如调用了另一个函数,或者是递归调用自己),并不会停止,而是一直执行。

 

调试时还可以使用 continue 命令继续运行程序。程序会在遇到断点后再次暂停运行。如果没有遇到断点,就会一直运行到结束,命令如下:

(gdb) continue [NUMBER]

如果没有任何参数,则表示遇到下一个断点就停止程序。而如果在命令中指定了参数NUMBER,则表示可以忽略NUMBER次断点。例如:

(gdb) continue 5 #遇到5个断点不停止,直到第6次遇到断点

 

如果你的调试断点在某个函数中,并还有语句没有执行完。你可以使用以下命令强制函数忽略还没有执行的语句并返回:

(gdb) return [EXPR]

使用return命令取消当前函数的执行,并立即返回,如果指定了表达式EXPR,那么该表达式的值会被当做函数的返回值。例如:

(gdb) return 0 #退出当前函数并返回0

执行完return命令后,会退出当前执行函数的调用栈,回到上一级调用栈。

 

一般来说,被调试程序会按照程序代码的运行顺序依次执行。GDB提供了打乱执行顺序的功能,也就是说GDB可以让程序执行随意跳跃。这个功能由GDB的jump命令来完:

(gdb) jump LOCATION

该命令会带一个参数,即要跳转到的代码位置,可以是源代码的行号:

(gdb) jump 555 #跳转到源代码的第555行的位置

可以是相对当前代码位置的偏移量:

(gdb) jump +10 #跳转到距当前代码下10行的位置

也可以是代码所处的内存地址:

(gdb) jump *0x12345678 #跳转到位于该地址的代码处

注意,在内存地址前面要加“*”。还有,jump命令不会改变当前程序调用栈的内容,所以当你从一个函数跳到另一个函数时,当函数运行完返回进行退栈操作时就会发生错误,因此最好还是在同一个函数中进行跳转。

 

当然,GDB也可以强制调用一个指定的函数:

(gdb) call FUNCTION(PARAMTERS…)

参数是要调用的函数名以及调用该函数所需要带的参数。运行完命令后,该指定函数会被执行,并显示函数的返回值。如果函数返回值是void,那么就不显示。注意,即使被调用的函数不需要任何参数,也要加上括号(“()”)。

同样,print命令也可以用来强制调用一个指定函数:

(gdb) print FUNCTION(PARAMTERS…)

两者的差别非常小,如果函数返回voidcall命令不显示,但是print命令会显示,并把该值存入历史数据中。

查看源代码

GDB可以打印出所调试程序的源代码。当然,在程序编译时一定要加上–g的参数,把源程序信息编译到执行文件中,不然就看不到源程序了。当程序停下来以后,GDB会报告程序停在了哪个源文件的第几行代码上。

你可以用list命令来打印程序的源代码,其有很多种格式,下面一一解释:

(gdb) list

如果不带任何参数的话,该命令会接着打印上次list命令打印出代码后面的代码。如果是第一次执行list命令则会显示当前正在执行代码位置附近的代码。

(gdb) list -

如果参数是一个减号的话,则和前面刚好相反,会打印上次list命令打印出代码前面的代码。

(gdb) list LOATION

list命令还可以带一个代码位置作为参数,顾名思义,这样的话就会打印出该代码位置附近的代码。这个代码位置的定义和在break命令中定义的相同,可以是一个行号:

(gdb) list 100 #列出当前代码文件中第100行附近代码

(gdb) list tcpdump.c:450 #列出tcpdump.c文件中第450行附近代码

也可以是一个函数名:

(gdb) list main #列出当前代码文件中main函数附近代码

(gdb) list inet.c:pcap_lookupdev #列出inet.c代码文件中指定函数附近代码

list命令还可以指定要显示代码的具体范围:

(gdb) list FIRST,LAST

这里FIRSTLAST都是具体的代码位置,此时该命令将显示FIRSTLAST之间的代码。可以不指定FIRST或者LAST参数,这样的话就将显示LAST之前或者FIRST之后的代码。注意,即使只指定一个参数也要带逗号,否则就编程前面的命令,显示代码位置附近的代码了。

 

list命令默认只会打印出10行源代码,如果觉得不够,可以使用如下命令修改:

(gdb) set listsize COUNT

这样的话,下次list命令就会显示COUNT行源代码了。如果想查看这个参数当前被设置成多少,可以使用如下命令:

(gdb) show listsize

 

还有一个非常有用的命令,如果你想看程序中一共定义了哪些函数,可以使用下面的命令:

(gdb) info functions

这个命令会显示程序中所有函数的名词,参数格式,返回值类型以及函数处于哪个代码文件中。

查看汇编指令

如果被调试程序没有包含源码的信息,那么也可以查看当前程序的汇编代码。其命令如下:

(gdb) disassemble START,END

(gdb) disassemble START,+LENGTH

有两种格式,第一种指定要查看代码的地址范围,从START开始到END结束;第二种是指定从START开始的LENGTH条汇编代码。

另外,如果想源代码和汇编代码一起看的话,可以在命令后跟一个/m参数。

 

如果想单步执行之后,看到下一步要执行的汇编指令,可以用下面的命令:

(gdb) set disassemble-next-line on

 

关于ARM,还有一点值得注意的。大家知道,ARM处理器支持两种指令集,一种就是ARM,还有一种是Thumb,那么在显示汇编代码时,GDB将怎么解释二进制代码呢?到底解释成ARM还是Thumb呢?如果有符号表,那么GDB就使用符号表来决定指令是 ARM指令还是Thumb指令。如果被调试程序没有符号表的话,可以使用如下命令指定:

(gdb) set arm fallback-mode MODE

作为参数的模式MODE有三种:

1)  arm:将二进制代码用ARM指令解释;

2)  thumb:将二进制代码用Thumb指令解释;

3)  auto:让GDB使用当前的执行模式(从CPSR寄存器的T位得到)。

而如果想看当前使用的是那种解释模式,可以用下面的命令:

(gdb) show arm fallback-mode

退出

如果想退出GDB调试,只需要键入以下命令:

(gdb) quit

命令简写

前面大致把GDB常用的命令介绍了一下,其本身的功能还是非常强大的,覆盖了调试需要的所有的功能。但是,和GUI的调试工具相比,每操作一步都需要敲入命令。如果每条命令都全部敲入的话,调试起来会非常麻烦。因此GDB支持命令的简写形式。

简写的规则非常的简单,只使用足以区别其它命令的前几个字符来执行命令就行了。如果你键入的命令不足以唯一定位一条命令,也就是有歧义,则GDB会给出提示。例如:

(gdb) info breakpoints

(gdb) i b #不会冲突

上面两条命令是完全等价的。但也有冲突的情况:

(gdb) info watchpoints

(gdb) info warranty

(gdb) i w #会冲突

(gdb) i wat #不会冲突

可以看出来,单敲入命令i w,GDB是无法知道你到底是想执行info watchpoints命令还是info warranty命令的。如果再打几个字母,消除歧义就没问题了。

 

下面的表格列出了常用命令的简写形式:


全命令

简写命令

break

b

list

l

info

i

backtrace

bt

frame

f

continue

c

next

n

step

s

nexti

ni

stepi

si

delete

d

print

p


阅读(3135) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~