一、简介
GDB本身是一个debug工具,可以进行以下几类动作:
- 启动程序,specifying anything that might affect its behavior.
- 让我们的程序在特定情况下暂停执行
- 当我们的程序停止执行时,检测下发生了什么
- 修改我们的程序,让我们可以修复了某个bug后可以继续进行测试,寻找其他的bug
二、启动和退出GDB
2.1 启动
我们可以使用命令gdb
来启动gdb,启动后gdb一直等待着我们从命令行输入的指令,一直到我们退出GDB:
gdb带有很多参数,这些参数在我们启动gdb时设定了工作环境。最常用的启动gdb的方式是带有一个变量,指明可执行程序:
gdb program
我们也可以在指明可执行程序的同时,指明一个特定的core file:
gdb program core
如果我们想要debug一个正在运行的程序,那么我们需要使用如下的形式(假设正在运行的进程ID为1234):
gdb program 1234
gdb -p 1234
/待补充
https://sourceware.org/gdb/current/onlinedocs/gdb/Invoking-GDB.html#Invoking-GDB
2.2 退出
退出GDB可以使用如下的两种命令:
quit
q
/待补充
https://sourceware.org/gdb/current/onlinedocs/gdb/Quitting-GDB.html#Quitting-GDB
2.3 shell命令
如果我们在利用GDB进行debug的同时需要执行shell命令,那么我们不需要离开或挂起GDB进程,而是可以利用如下的方式来执行:
shell command-string
!command-string
上面的两种方式都会调用一个shell来执行command-string的内容。在GNU和Unix类系统中,环境变量SHELL决定了是哪个shell程序执行我们的代码,默认是bin/sh。
/待补充
https://sourceware.org/gdb/current/onlinedocs/gdb/Shell-Commands.html#Shell-Commands
2.4 导出日志文件
如果我们想要保存GDB命令行输出的信息,可以使用如下的方法:
首先,开启日志:
set logging on
(关闭日志的方法是:set logging off
)
接下来设置日志信息要保存进哪个文件,默认保存进gdb.txt
,如果我们想要将信息储存在我们指定的文件中,使用:
set logging file file_name
默认情况下,日志文件是追加使用的
/待补充
https://sourceware.org/gdb/current/onlinedocs/gdb/Logging-Output.html#Logging-Output
三、GDB命令
3.1 命令格式
GDB命令是按照行进行区分的,但并没有限制每一行的具体长度。每个命令以一个命令名开始,后面跟着一些和具体命令有关的变量。例如,命令step
后面可以跟一个变量,这个变量指明了需要进行几次step
操作,如果是5的话就是这样:step 5
.我们也可以单独使用step
而不接其他变量,还有一部分指令本身就不能接任何其他变量。
部分缩写没有歧义的命令也可以直接使用缩写表示,有些有歧义的缩写,例如s
,也可以直接使用。因为我们规定s
代表step
而不是其他以s开头的命令。
空白输入代表重复上次的命令,但是注意,有一些特定的指令(例如run指令)是不能重复进行的。
/待补充
https://sourceware.org/gdb/current/onlinedocs/gdb/Command-Syntax.html#Command-Syntax
3.2 命令的设置
https://sourceware.org/gdb/current/onlinedocs/gdb/Command-Settings.html#Command-Settings
3.3 命令的补全
https://sourceware.org/gdb/current/onlinedocs/gdb/Completion.html#Completion
3.4 命令的选项
https://sourceware.org/gdb/current/onlinedocs/gdb/Command-Options.html#Command-Options
3.5 定义别名
3.6 获取帮助
https://sourceware.org/gdb/current/onlinedocs/gdb/Help.html#Help
四、利用GDB进行debug
4.1 编译
为了能够有效的debug一个程序,我们需要在编译时就生成debug信息,这些debug信息存储在目标文件中。debug信息存储了变量类型、函数以及源码和可执行代码间的对应。
为了保存这些debug信息,我们在编译时需要指明-g
选项。但是注意,通常我们使用的程序都会经过优化(编译时使用-o
选项)。但是,一些编译器无法同时进行-g
和-o
的编译。所以需要注意,当我们使用这些编译器时,我们无法编译得到优化后的可debug程序。
我们常用的GCC编译器(GNU C/C++编译器)支持同时使用-g
和-o
。注意一些老版的GNU C编译器允许使用一种-g
选项的变种-gg
,但是GDB已经不支持这种格式了,不要使用它。
/待补充
https://sourceware.org/gdb/current/onlinedocs/gdb/Compilation.html#Compilation
4.2 开始运行程序
有两种方式启动我们的程序:
run
r
这里的程序可以在我们启动gdb时,就利用gdb file
指定,也可以使用file
或exec-file
指令来指定。
命令行选项
-x (examine命令的缩写)
格式:
x nfu addr
其中
n:是正整数,表示需要显示的内存单元的个数,即从当前地址向后显示n个内存单元的内容,一个内存单元的大小由第三个参数u定义。
f:表示addr指向的内存内容的输出格式,s对应输出字符串,此处需特别注意输出整型数据的格式:
x 按十六进制格式显示变量.
d 按十进制格式显示变量。
u 按十进制格式显示无符号整型。
o 按八进制格式显示变量。
t 按二进制格式显示变量。
c 按字符格式显示变量。
f 按浮点数格式显示变量。
a 将数据翻译成地址
i 将数据翻译为指令
u:就是指以多少个字节作为一个内存单元-unit,默认为4。u还可以用被一些字符表示:
如b=1 byte, h=2 bytes,w=4 bytes,g=8 bytes.
<addr>:表示内存地址。
其中f和u的默认值都是上一次使用的值,而n的默认值为1。Default address is following last thing printed with this command or “print”
break设置断点
设置断点的相关命令有break,tbreak,rbreak,hbreak,thbreak,后两种是基于硬件的,我们可以不需要了解太多。
break与tbreak
break及tbreak可以根据行号、函数、条件生成断点。tbreak设置方法与break相同,只不过tbreak只在断点停一次,过后会自动将断点删除,break需要手动控制断点的删除和使用。
break可带如下参数:
- linenum:本地行号,即list命令可见的行号
- filename:linenum:指定文件的行号
- function:函数,可以是自定义函数也可以是库函数,如open
- filename:function:指定文件中的函数
- condition:条件
- *address:地址,可以是函数,变量的地址,这个地址可以通过info add命令得到
例如:
- break 10
- break test.c:10
- break main
- break test.c:main
- break system
- break *0x804840c
如果想要在指定的地址设置断点,比如在main函数的地址处设置断点,我们可以用info add main获得main的地址如0x80484624,然后用break *0x80484624。
条件断点就是在如上述指定断点的同时指定进入断点的条件,例如(假如有int类型变量index):
- break 10 if index==3
- break 12 if index==5
disassemble
我们可以反汇编一个函数:
disassemble func_name
也可以反汇编一个内存地址,第一个参数是起始地址,第二个是终止地址:
disassemble 0x0 0x10
info命令
用法:
- info registers:查看寄存器内容
info proc
- info proc all:打印出所有进程相关信息
- info proc mappings:显示正在运行的进程中映射的内存区域的列表。
c:继续执行
si(stepi)和ni(nexti)
si是单步调试,如果有调用子程序,则进入调用的子程序,而ni是跳过子程序。