1 GDB的启动和退出
1.1 启动调试
gdb program core
gdb program PID
启动参数:
1. gdb参数
gdb --args program --arg1 --arg2
2. set args 命令
gdb program
set args --arg1 --arg2
3. run 命令
gdb program
run --arg1 --arg2
1.2 退出调试
quit
q
中断信号(Ctrl+c)不会退出gdb,只是中断当前调试命令
1.3 日志
gdb默认不开启日志,通过如下命令可以开启或关闭日志,并设置日志文件路径,写文件的模式
set logging on
set logging off
set logging file 默认日志文件是gdb.txt
set logging overwrite [on/off] 默认是off,也就是日志是append到之前的日志文件
set logging redirect [on/off] 默认是off,也就是日志同时输出到终端和日志文件
show logging
2 命令
2.1 命令补全
TAB 自动补全,在存在多个候选的情况下需要再次按下TAB才会显示候选
ALT+?直接显示候选
2.2 多线程
info thread 列出所有线程
thread ThreadNo 切换到指定线程(这里的ThreadNo并不是ThreadId,而是info thread列出的线程序号
thread apply [ThreadNo/all] cmd 对每个线程执行cmd命令
2.3 多进程
set follow-fork-mode [parent/child/ask]
- parent 跟踪父进程
- child 跟踪子进程
- ask 每次fork时询问跟踪父进程还是子进程
show follow-fork-mode
3暂停和继续
3.1 断点
break 在当前位置设置断点(对于最内部的函数调用,即在当前暂停的位置设置断点,对于上层调用栈,则是在下一语句设置断点)
break NS::Class::Function
break filename.cpp:linenum
break location thread thread-id
break location [-force-condition] if …
tbreak 一次性断点
rbreak . 所有函数设置断点
rbreak filename:. 指定文件中的所有函数设置断点
info break
Num Type Disp Enb Address What
1 breakpoint keep y
stop only if i==1
breakpoint already hit 1 time
1.1 y 0x080486a2 in void foo() at t.cc:8
1.2 y 0x080486ca in void foo() at t.cc:8
3.2 观察点
watch 监视数据修改
watch var [thread thread-id]
watch (type)address
rwatch 监视数据读取
awatch 监视数据读写
3.3 捕获点
catch throw/rethrow/catch [regexp] 捕获C++异常
catch exec/fork/vfork 捕获对exec/fork/vfork的调用
catch syscall [name | number | group:groupname | g:groupname] 捕获系统调用或返回
catch signal [signal…|all]
不指定信号则捕获除SIGTRAP和SIGINT以外的其他信号
All表示捕获所有信号
tcatch 只捕获一次
3.4 删除断点
clear [location] 删除指定位置的断点
delete [breakpoints] 删除指定的断点,不指定断点则删除所有断点
3.5 禁用/启用/跳过断点
disable [breakpoints] 默认禁用所有断点
enable [breakpoints] 默认启用所有断点
enable [breakpoints] once 启用一次
enable [breakpoints] count [count] 启用指定次数
enable [breakpoints] delete 启用一次,触发后删除断点,类似tbreak
ignore [breakpoints] count 后续count次进入断点不暂停
3.6 断点配置保存和加载
save breakpoints [filename] 保存所有断点
source filename 加载所有断点
3.7 跳转指令
step [count]
next [count]
finish
until [location] 执行到指定位置停下,默认下一行,如果在循环最后一行,则是跳出循环
stepi [count] 执行一个机器指令
nexti [count] 执行一个机器指令,如果是函数调用则跳过
4 调用栈
4.1 打印调用栈列表
backtrace 显示全部调用栈
backtrace n 显示最顶部的n个调用栈
backtrace -n 显示最底部的n个调用栈
4.2 跳转调用栈
frame n 跳转到第n个调用栈,当前正在执行的调用栈为第0个
up [n] 向栈顶方向移动n个调用栈,默认移动一个
down [n] 向栈底方向移动n个调用栈,默认移动一个
4.3 打印调用栈详细信息
info frame 打印当前所在的调用栈信息
info frame n 打印第n个调用栈信息
4.4 在调用栈上执行命令
frame apply [all|n|-n] command
如下命令可以打印出每个调用栈的sp寄存器的值
frame apply all p $sp
5 源码
5.1 list
list linenum 打印当前文件第linenum行周围的代码
list function 打印函数function开始附近的代码
list - 打印上次输出代码之前的代码
list + 打印上次输出代码之后的代码
list 打印上次输出代码之后的代码
list first,last 打印first到last之间的代码
5.2 设置源码路径
set substitute-path <old_path> <new_path>
5.3 查看汇编代码
disassemble
6 数据
6.1 表达式
@ 打印数组
a@2 打印数组a的前两个元素 // int a
*a@2 打印数组a的前两个元素 // int *a = new int[2]
:: 限定范围的变量
file::var 文件内静态变量 function::var 函数内的变量
{type} addr 按照指定类型输出某地址上的数据
6.2 变量可见范围
gdb可见的变量(即print可打印的变量)包括:
- 全局变量和文件内的静态变量
- 当前栈帧中的局部变量(包括静态变量)
6.3 输出格式
- x 十六进制输出
- d 有符号十进制
- u 无符号十进制
- o 八进制
- t 二进制(不是b,因为b在x命令中有别的含义)
- a 地址格式(类似info symbol,可以快速判断栈上变量位于哪个函数中
- c 字符格式,超出ASCII范围用\nnn表示
- f 浮点数格式
- s 字符串格式
- z 十六进制输出,前面用0补全,输出类似0x00000001
- r 使用raw格式,默认基于Python的格式化方式
p/x a
p/s str
6.4 打印内存
x/nfu addr f是输出格式,u每个数据的大小,n是数据数量,三者都是可选参数
f 输出格式
x命令的默认输出格式是x,除了上面提到的输出格式外,x命令还支持两种格式
- i 输出机器指令
- m 显示memory tag (需要CPU架构支持)
u 数据大小
- b 一个字节(byte)
- h 两个字节(halfword)
- w 四个字节(word)
- g 八个字节(giant word)
6.5 自动打印
display/f <expr> 定义一个自动打印的表达式
undisplay <dnums> 删除一个自动打印表达式
delete display <dnums> 删除一个自动打印表达式
disable display <dnums> 禁用一个自动打印表达式
enable display <dnums> 启用一个自动打印表达式
info display 显示所有自动打印的表达式列表
如果表达式中有局部变量,当离开作用域后将自动失效(不是删除),再次进入作用域后会自动生效。
6.6 寄存器
gdb可以打印寄存器的值,寄存器需要在名字前加$,例如指令寄存器就是$pc
info registers 显示除浮点和向量寄存器以外的所有寄存器的名字和值
info all-registers 显示所有寄存器的名字和值
6.7 协处理器信息(arm和x86支持)
info float 显示浮点数计算单元(协处理器)的信息
6.8 生成core文件
gcore [file] 生成core文件,如果没有指定file,默认生成core.<pid>
6.9 搜索内存块
find [/sn] start_addr, +len, val1[, val2, ...] 搜索start_addr到start_addr+len内存范围内是否存在连续的val1,val2,...序列
find [/sn] start_addr, end_addr, val1[, val2, ...] 搜索start_addr到end_addr内存范围内是否存在连续的val1,val2,...序列
s, 搜索的基本单元
- b 字节
- h 两个字节
- w 四个字节
- g 八个字节
这个仅仅是表示后面的val的数据大小,搜索的边界不会和这个字节数对齐
n, 最大搜索数量,默认搜索全部匹配的结果
7 符号
gdb可以查看到可执行文件的符号表信息
什么是符号:
- 具名函数(不含lambda函数)
- 全局变量或文件静态变量
- 类型名
- typedef定义的别名
7.1 查看符号
info address <symbol> 显示符号地址和符号类型(函数、静态变量等)
(gdb) info address main
Symbol "main()" is a function at address 0x4005c0.
(gdb) info address a
Symbol "a" is static storage at address 0x601044.
info symbol <address> 显示地址所在的符号及其位于哪个段
(gdb) info symbol &a
a in section .bss
(gdb) info symbol 0x601044
a in section .bss
(gdb) info symbol &main
main in section .text
7.2 查看类型
whatis <expression>或<address> 显示表达式或地址所在符号的类型
ptype[/flag] arg 显示表达式或类型的类型(如果是类包含成员等信息)
(gdb) ptype/o GetObject()
type = class Base {
private:
/* 8 | 4 */ int a;
/* 12 | 4 */ float b;
/* 16 | 1 */ bool c;
/* XXX 7-byte hole */
/* 24 | 8 */ std::string d;
/* total size (bytes): 32 */
} *
info types [-q] [<regex>] 显示正则匹配到的符号,如果没有指定参数则显示所有符号
info types 显示所有符号
7.3 查看源码文件
info sources [--dirname |--basename] [--] <regex> 列出正则匹配到的源码文件,如果没有指定参数则显示所有源码文件
7.4 查看函数
info functions [-q] [-n] 显示所有的函数
(gdb) info functions
All defined functions:
File /home/hekai/workspace/gdbtest/app/demo.cpp:
18: void func();
23: int main();
Non-debugging symbols:
0x0000000000400440 _init
0x0000000000400470 printf@plt
0x0000000000400480 __gmon_start__@plt
0x0000000000400490 __libc_start_main@plt
0x00000000004004a0 sleep@plt
0x00000000004004b0 _start
0x00000000004004e0 deregister_tm_clones
0x0000000000400510 register_tm_clones
0x0000000000400550 __do_global_dtors_aux
0x0000000000400570 frame_dummy
0x00000000004005f0 __libc_csu_init
0x0000000000400660 __libc_csu_fini
0x0000000000400664 _fini
info functions [-q] [-n] [-t `type_regexp`] [`regexp`] 显示函数签名匹配且函数名匹配的函数
info fun step 显示函数名包含step的函数
info fun -t '(.*int.*' 显示有一个int参数的函数
info fun -t '^int (' 显示返回值类型为int的函数
7.5 查看变量
info variables [-q] [-n] 显示所有的函数外变量(全局变量,静态变量,包含函数静态变量),但是函数内静态变量属于Non-debugging symbols
(gdb) info variables
All defined variables:
Fil>e /home/hekai/workspace/gdbtest/app/demo.cpp:
16: static int a;
Non-debugging symbols:
0x0000000000400670 _IO_stdin_used
0x0000000000400678 __dso_handle
0x00000000004006a0 __GNU_EH_FRAME_HDR
0x00000000004007f0 __FRAME_END__
0x0000000000600de0 __frame_dummy_init_array_entry
0x0000000000600de0 __init_array_start
0x0000000000600de8 __do_global_dtors_aux_fini_array_entry
0x0000000000600de8 __init_array_end
0x0000000000600df0 __JCR_END__
0x0000000000600df0 __JCR_LIST__
0x0000000000600df8 _DYNAMIC
0x0000000000601000 _GLOBAL_OFFSET_TABLE_
0x0000000000601038 __data_start
0x0000000000601038 data_start
0x000000000060103c func()::a
0x0000000000601040 __TMC_END__
0x0000000000601040 __bss_start
0x0000000000601040 _edata
0x0000000000601040 completed
0x0000000000601048 _end
info variables [-q] [-n] [-t `type_regexp`] [`regexp`] 显示类型匹配且名称匹配的变量
info scope <location> 列出指定位置作用域内的本地变量
(gdb) info scope func
Scope for func:
Symbol a is in static storage at address 0x60103c, length 4.
8 注入
gdb调试时可以修改被调试程序的数据,调用被调试程序的方法,甚至直接跳转执行流程。当然仅限于执行中的进程,对于core文件调试是无效的
8.1 修改变量
set var <symbol>=<value> 通过变量名修改变量
set var a=1 set {type}<address> = <value> 通过地址修改数据 set {int}0x83040 = 4
8.2 跳转
jump <location>
jump main.cpp:18 set $pc <address>
set $pc main 跳转到main函数入口 set $pc main+15 跳转到main函数入口地址+15 (如果地址有误将直接引发段错误) set $pc 0x400565 跳转到0x400565 (如果地址有误将直接引发段错误)
8.3 发送信号
signal <signum> 发送信号给被调试程序并继续执行
signal 2 signal SIGINT
通过signal命令发送的信号将直接给到被调试的程序,而通过shell的kill命令发送的信号会被gdb过滤,并根据其信号处理表决定是否交由被调试程序处理。
queue-signal <signum> 在下次继续执行程序前向被调试程序发送信号,可以设置多个信号,SIGINT不能被设置
queue-signal 2
queue-signal SIGINT
8.4 函数返回
return [expression] 立刻退出函数,并可以通过表达式指定函数返回值
8.5 函数调用
print <expression> 如果expression是一个函数,则会执行改函数并打印返回值
print func() call <expression> call func()