本系列主要是记录下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
的别名还有where
和info 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
为单步步入,即执行时如果遇到函数调用,则跳到函数内部。 如果要逐条执行汇编执行,则可以分别使用nexti
和stepi
。nexti
和stepi
的区别同源码级单步执行。
0x09. 继续运行 调试时,可以使用continue
(简写为C
)命令继续运行程序。程序会在遇到断点后再次暂停运行,如果没有遇到断点,就会一直运行到结束。
格式
说明
continue
继续运行程序,直到遇到断点
continue 次数
指定次数可以忽略断点
0x10. 监视点 大型软件或大量使用指针的程序中,很难弄清变量在什么地方被改变。要想找到变量在何处被改变,可以使用watch
命令(监视点,watchpoint)。
格式
说明
watch <表达式>
<表达式>发生变化时暂停运行,<表达式>指常量或变量
awatch <表达式>
<表达式>被访问、改变时暂停运行
rwatch <表达式>
<表达式>被访问时暂停运行
0x11. 删除断点和监视点 用delete
(简写d
)命令删除断点和监视点 格式如下:delete <编号>
删除<编号>指示的断点或监视点。