调试器gdb的基本使用方法(1)

本系列主要是记录下Linux环境下的标准调试器GDB的使用方法。GDB的功能极其丰富,各种各样的命令使人眼花缭乱。下面按照调试的流程进行说明,流程如下: (1) 带着调试选项编译、构建调试对象。(2) 启动调试器(GDB)、设置断点、显示栈帧、显示值、继续执行。

0x01. 准备

通过gcc的 -g 选项生成调试信息。即如: $ gcc -Wall -O2 -g 源文件
如果使用Makefile构建,一般要给CFLAGS中指定 -g 选项。如: CFLAGS = -Wall -O2 -g
如果用configure脚本生成Makefile,可以这样用: $ ./configur CFLAGS="-Wall -O2 -g"
构建方法通常会写在INSTALL、README等文件中,参考即可。

0x02. 启动

启动gdb调试器很简单,通过如下命令: $ gdb 可执行文件名
启动后会显示一些信息,并出现gdb提示符。

0x03. 设置断点

可以在函数名和行号等上设置断点。程序运行后,到达断点就会自动暂停运行。此时可以查看该时刻的变量值、显示栈帧、重新设置断点或重新运行等。断点命令break(简写b)。

格式:
break 函数名
break 行号
break 文件名:行号
break 文件名:函数名
break +偏移量
break -偏移量
break * 地址

如上所示,断点可以通过函数名、当前文件内的行号来设置,也可以先指定文件名再指定行号,还可以指定与暂停位置的偏移量,或者用地址来设置。

0x04. 运行

run命令开始运行,不加参数只执行run,就会执行到设置了断点的位置后暂停运行。可以简写为r。

1
2
3
4
5
6
7
格式:
run 参数
例子:
(gdb) run -a
Starting program: /home/xxx/work/coreutils/src/uname -a
Breakpoint 1,main (argc=2, argv=0xbf9cd714) at uname.c:184

经常用到的一个操作是main()上设置断点,然后执行到main()函数。start 命令能达到同样的效果。

0x05. 显示栈帧

backtrace 命令可以在遇到断点而暂停执行时显示栈帧。该命令简写为 bt 。此外,backtrace的别名还有whereinfo stack (简写为 info s)。

命令 说明
backtrace 显示所有栈帧
backtrace N 只显示开头N个栈帧
backtrace -N 只显示最后N个栈帧
backtrace full 显示所有栈帧和局部变量
backtrace full N 显示开头N个栈帧和局部变量
backtrace full -N 显示最后N个栈帧和局部变量

0x06. 显示变量

print 命令可以显示变量。简写为p

1
2
3
4
5
6
7
8
9
10
格式:  print 变量
(gdb) p argv
$1 = (char **) 0xbf9cd714
(gdb) p *argv
$2 = 0xbf9cf6a5 "/home/xxx/work/coreutils/src/uname"
(gdb) p argv[0]
$3 = 0xbf9cf6a5 "/home/xxx/work/coreutils/src/uname"
(gdb) p argv[1]
$4 = 0xbf9cf6cd "-a"
(gdb)

0x07. 显示寄存器

info registers 可以显示寄存器内容,简写 info reg
在寄存器名之前添加$,即可显示各个寄存器的内容。
(gdb) p $eax ===> $8 = 97
显示时可以使用以下格式:p/格式 变量

表1-1 显示寄存器可使用的格式

格式 说明
x 显示为十六进制数
d 显示为十进制数
u 显示为无符号十进制数
o 显示为八进制数
t 显示为二进制数,t的由来是two
a 地址
c 显示为字符(ASCII)
f 浮点小数
s 显示为字符串
i 显示为机器语言(仅在显示内存的X命令中可用)

例如:(gdb) p/c $eax ====>$7 = 97 'a'

x命令可以显示内存的内容,x这个名字的由来是eXamining。
显示的格式:x/格式 地址

1
2
3
4
(gdb) x $pc
0x8048ebd <main+173>: 0x0f6ef883
(gdb) x/i $pc // x/i意为显示汇编指令
0x8048ebd <main+173>: cmp $0x6e,%eax

反汇编的命令 disassemble,简写为disas

格式 说明
disassemble 反汇编当前整个函数
disassemble 程序计数器 反汇编程序计数器所在的含数
disassemble 开始地址 结束地址 反汇编从开始地址到结束地址之前的部分

0x08. 单步执行

单步执行的意思是根据源代码一行一行地执行。逐条执行源代码的命令为next(简写为n)和step(简写为s),其中next为单步步过,即执行时如果遇到函数调用,则跳到函数调用的下一行;step为单步步入,即执行时如果遇到函数调用,则跳到函数内部。
如果要逐条执行汇编执行,则可以分别使用nextistepinextistepi的区别同源码级单步执行。

0x09. 继续运行

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

格式 说明
continue 继续运行程序,直到遇到断点
continue 次数 指定次数可以忽略断点

0x10. 监视点

大型软件或大量使用指针的程序中,很难弄清变量在什么地方被改变。要想找到变量在何处被改变,可以使用watch命令(监视点,watchpoint)。

格式 说明
watch <表达式> <表达式>发生变化时暂停运行,<表达式>指常量或变量
awatch <表达式> <表达式>被访问、改变时暂停运行
rwatch <表达式> <表达式>被访问时暂停运行

0x11. 删除断点和监视点

delete(简写d)命令删除断点和监视点
格式如下:delete <编号>
删除<编号>指示的断点或监视点。