gdb调试笔记
12 Jun 2020
|
一、环境安装
gdb 源码下载:https://ftp.gnu.org/gnu/gdb/
gdb 源码编译:
mkdir gdb‐build‐7.7
cd gdb‐build‐7.7
../gdb‐7.7/configure
或者 ‐‐target=i686‐elf ‐‐prefix=../gdb/install
make ‐j4
make install
安装后程序在/usr/local/bin
原先的程序在/usr/bin
二、自动化处理
(1)内核调试脚本
gdb \
‐ex "add‐auto‐load‐safe‐path $(pwd)" \
‐ex "file vmlinux" \
‐ex 'set arch i386:x86‐64:intel' \
‐ex 'target remote localhost:1234' \
‐ex 'continue' \
‐ex 'disconnect' \
‐ex 'set arch i386:x86‐64:intel' \
‐ex 'target remote localhost:1234'
三、断点相关
(1)条件断点
break write if $rsi == 2
(2)软件断点
beak 普通
tbreak 一次性
rbreak 接受正则表达式成批设置
原理:
-
基于CPU的断点指令,如x86的INT 3(机器码0xCC)
-
替换断点位置的指令
-
CPU执行到此时触发断点异常
-
没有数量限制
(3)硬件断点
hbreak 普通
thbreak 一次性
原理:
- 基于CPU的调试寄存器,如x86的DR0-DR7
- 不需要修改程序代码,可以针对EEPROM上的代码位置
- 有数量限制,x86上最多4个
(4)监视断点
监视表达式,值变化时中断
watch a*b + c/d
watch *(int *)0x12345678
watch *global_ptr
访问监视断点:
rwatch 表达式被读时断下
awatch 表达式被读或者被写时断下
(5)catch 断点:用于捕捉事件
事件包括如下:
exception [name]
exception unhandled
handlers [name]
assert
exec
syscall
syscall [name|number|group:groupname|g:groupname]
例:(gdb)catch syscall chroot
(gdb) catch syscall group:process
Catchpoint 1 (syscalls ’exit’ [1] ’fork’ [2] ’waitpid’ [7]
’execve’ [11] ’wait4’ [114] ’clone’ [120] ’vfork’ [190]
’exit_group’ [252] ’waitid’ [284] ’unshare’ [310])
fork
vfork
load [regexp]
unload [regexp]
signal [signal...|'all']
tcatch event 捕捉一次catch
(6)dprinf 遇到断点是打印信息
set dprintf‐style gdb/call/agent 设置用哪里的打印函数打印信息
set dprintf‐function fprintf 设置用什么函数打印
dprintf 25,"at line 25, glob=%d\n",glob
(7)trace 跟踪点
和break命令非常相似。其参数可以是源代码行,函数名或者目标程序的某个地址,trace 命令创建跟踪点,程序在此点上短暂中断,收集数据,然后程序继续往下执行。设置跟踪点 或者改变跟踪点命令直到下个tstart命令才会生效;因此,不能在跟踪会话过程中改变跟踪 点的属性。
delete tracepoint [num]
disable tracepoint [num]
enable tracepoint [num]
passcount [n [num]]
(gdb) passcount 5 2 // 跟踪点2在第5次执行时中止
(gdb) passcount 12 // 最近创建的跟踪点,在第12次执行时中断
(gdb) trace foo
(gdb) pass 3
(gdb) trace bar
(gdb) pass 2
(gdb) trace baz
(gdb) pass 1 // 在foo执行过3次,或者bar执行过2次,或者baz执行过1次时,中止跟踪
action [num] 执行到跟踪点要执行的命令,只有两种:collect, while‐stepping 例:
(gdb) trace foo
(gdb) actions
Enter actions for tracepoint 1, one per line:
> collect bar,baz
> collect $regs
> while‐stepping 12
> collect $fp, $sp
> end
end
collect expr1, expr2, …
tstart 开始一次跟踪会话
tstop 结束一次跟踪会话
tstatus 显示当前跟踪数据收集的状态
(8)断下后执行命令
commands num(断点编号)在触发断点后执行commands,命令以end结束 用define 编写自定义的宏
(gdb) define br_info Type commands for definition of "br_info". End with a line saying just "end". >b $arg0 >comm >i locals >i args >end >(gdb) br_info binary_search if target == 5
当if target == 5条件满足时,br_info binary_search会被执行。 br_info展开成为一系列命令,并用binary_search替换掉$arg0。
(9)管理断点
info/disable/delete break
保存断点到文件中
save breakpoints [filename] 保存现在的断点到文件中
source [filename] 恢复断点,将文件中的断点打一遍, watchpoints可能会失效
四、保存现场和回溯
(1)gdb 的快照保存
checkpoint: 生成当前状态的快照
info checkpoint:显示快照信息
restart checkpoint‐id:恢复到某个checkpoint
delete checkpoint checkpoint‐id:删除某个checkpoint
值得注意的是
- 保存快照的进程ID和之前不同
- 已经写入文件或者关闭设备这些操作不能撤回到原先的状态
- 恢复到快照后,会将快照覆盖,所以如果还想再调试一次,就要在恢复后重新建立一个快照。
- 快照是对原先进程的复制,所以地址相同,调试的时候可以对地址下断点,而不用管随机化
(2)逆向执行
首先启动record 功能,就可以进行命令回溯
reverse‐continue 缩写rc
reverse‐step /rs
reverse‐step /rsi
reverse‐next /rn
reverse‐nexti /rni
reverse‐finish
如果嫌麻烦可以设置执行方向
set exec‐direction reverse/forward 这样执行ni就是reverse‐nexti
record stop
(3)记录功能
record goto begin/start 跳转到记录的起始位置
record goto end 跳转到记录的结束位置
record goto n 跳到记录的第n条指令,默认可以记录20万条
record save filename 保存记录
record restore filename 恢复记录
(4)日志信息
set logging on 开启日志记录
set logging off 关闭日志记录
set logging file file 记录日志文件,默认是gdb.txt
set logging overwrite [on|off] 是否覆盖,默认是不覆盖,以追加的方式记录日志
show logging 显示日志设置
(5)栈回溯
bt 或where , info s
bt full 会打印栈里的变量
五、打印和单步调试
p *&argv[0]@3:表示打印argv[0] argv[1] argv[2]
p {int}argv 以int类型显示argv变量
p/x 显示十六进制
p/d 显示有符号的十进制
p/u 显示无符号的十进制
p/o 显示八进制
p/t 显示二进制
p/a 显示地址
p/c 显示符号
p/f 显示浮点数
p/r 以上一次的格式显示
x/i 显示汇编
x/s 显示字符
x/b 单字节显示
x/h 双字节显示
x/w 四字节显示
x/g 八字节显示
disassemble/disas 显示反汇编
disassemble/r 显示反汇编前的机器码 混合
disassemble/m 显示源码 混合
disas start, end
disas start, +length
$表示上一个显示的变量
$n表示上n个显示的变量
$$表示上上个显示的变量
$$n表示上n个显示的变量的值
$_表示上一个用x显示的变量
$__表示上一个用x显示地址上存放的数据
$_thread 表示最新创建的线程
p $_strlen(str) 计算str字符串的长度
p $_streq(str1, str2) 比较两个字符串是否相等,相等返回1
help function 查看可使用的函数
explore val :可以显示变量的类型
(1)观察类型
pt(ptype) 观察数据类型(结构)
whatis
print v@10
(2)单步跟踪
next /n 代码单步步过
step/s 代码单步步入
stepi(si) 单步步入
stepi 4 执行4个指令
nexti 单步步过
skip function 始终步过指定函数
skip file [filename] 始终不进入指定文件
until 3 继续执行直到命中断点3
finish 继续执行直到当前函数返回
set print finish on 继续执行到函数后会打印返回值,本机测试不成功???
call func 执行func函数并打印返回值
return result 强制返回,返回值为result
六、实用操作
gdb 重放操作 repeat
(1)启动参数
gdb ‐n :可以不加载任何gdbinit文件,不想使用插件时不必去注释gdbinit文件了
gdb ‐q :不打印版本和介绍信息启动
gdb ‐write:对二进制程序可读可写启动,可对二进制程序指令进行修改,并保存到文件中,或者启动后"set write on"
gdb ‐statistics:可打印每条指令执行的时间
调试时可以通过"|"管道符对输出结果进行处理
gdb ‐‐pid=<n> 调试已经运行的程序
(2)执行系统命令
!command
(3)转储分析
gdb ‐‐core=<file>
gdb program core
gcore [file] 生成一个core文件用于保存当前gdb调试的快照(默认生成core.pid文件)
(4)dump 内存
dump 内存到文件中
dump [format] memory filename start_addr end_addr
format:binary/ihex
dump [format] value filename expr
追加内存到文件中
append [binary] memory filename start_addr end_addr
append [binary] value filename expr
从文件中恢复到内存
restore filename [binary] bias start end
(5)窗口调试
layout 用于分割窗口,可以一边查看代码,一边测试
layout split 显示源代码和汇编窗口
layout next 显示下一个layout
layout prev 显示上一个layout
layout regs 显示源代码/汇编和寄存器窗口
focus cmd (三个窗口:cmd、asm、src)
Ctrl + L:刷新窗口
Ctrl + x,再按1:单窗口模式,显示一个窗口
Ctrl + x,再按2:双窗口模式,显示两个窗口
Ctrl + x,再按a:回到传统模式,即退出layout,回到执行layout之前的调试窗口。
(6)搜索内存:
find [/sn] start_addr, +len, val1 [, val2, …]
find [/sn] start_addr, end_addr, val1 [, val2, …]
s可以为b,h,w,g等值,分别表示 字节(byte),两个字节(half words),4个字节(words),8个字节(giant words)
n表示要找的东西的最多个个数,默认是把所有的都找出来
$_ 保存着找到的最后一个地址
(7)宏处理
宏展开
macro exp expression
例:macro exp __is_constexpr(1)
宏展开一次
macro exp1 expression
(8)在gdb中编译和注入代码
七、设置和显示
(1)设置操作
set args 设置程序参数
show args 显示程序参数
set print vtbl on/off 开启打印虚表功能
set print union on/off 开启打印联合类型
set print symbol on/off 开启打印符号表
set print array on/off 开启打印数组类型
set print object on/off 开启打印object类型
set charset ASCII 设置字符集为ASCII
(2)显示符号
info variables/var regexp 查找变量
info classes regexp
info functions/func regexp 查找函数
info types regexp
info address symbol 查找symbol所在的地址
info symbol addr 查找地址对应的symbol,如果找不到,会返回最近的symbol+偏移
directory/dir dirname 设置符号表搜索路径
info program 显示程序状态,是否在运行,程序名,为什么停止
info stack 查看栈信息
info r 查看寄存器
八、调试特定场景
(1)调试多线程
info threads
thread 2 切换到线程2
thread apply [thread‐id‐list | all [‐ascending]] command
对多个线程执行命令,例如: thread apply all bt 对所有线程进行栈回溯
对当前线程命名: thread name [name]
(2)调试fork子进程
set follow‐fork‐mode parent/child 默认是调试父进程,而子进程继续执行
set detach‐on‐fork mode on/off 同时调试父进程和子进程
开启后可以控制所有fork的子进程,通过info inferiors查看信息,用inferior 命令进行切换
调试exec产生的子进程
set follow‐exec‐mode new/same
new 是新建一个inferior,而父进程的inferior仍然保留,当前保留的inferior的程序状态是没有执行。
same是保持在原来的inferior,gdb默认是same模式
set schedule‐multiple on 所有进程同时运行,detach‐on‐fork开启调试一个进程时,其他进程挂起
bt 显示所有参数
set print frame‐arguments all
(3)调试信号
info handle 查看各种信号的设置,设置包括如下:
print 对信号量进行通知
noprint 对信号量不打印信息,如果有信号量干扰,可以用handle xxx noprint将其屏蔽
stop 中断执行
nostop 不中断执行
pass 允许程序接管信号量
nopass 不允许程序接收信号量
handle SIGCONT 查看continued的信号量设置
九、随机化设置
关闭ASLR:
set disable‐randomization on
开启ASLR:
set disable‐randomization off
查看ASLR状态:
show disable‐randomization
默认是关闭随机化的,也就是on状态。
十、调试内核时出现“Remote ‘g’ packet reply is too long”问题
gdb源码下载:https://mirrors.ustc.edu.cn/gnu/gdb/
gdb源码根目录/gdb/remote.c里面,将
if (buf_len > 2 * rsa‐>sizeof_g_packet)
error (_(“Remote ‘g’ packet reply is too long: %s”), rs‐>buf);
修改为
if (buf_len > 2 * rsa‐>sizeof_g_packet) {
rsa‐>sizeof_g_packet = buf_len ;
for (i = 0; i < gdbarch_num_regs (gdbarch); i++) {
if (rsa‐>regs‐>pnum == ‐1)
continue;
if (rsa‐>regs‐>offset >= rsa‐>sizeof_g_packet)
rsa‐>regs‐>in_g_packet = 0;
else
rsa‐>regs‐>in_g_packet = 1;
}
}
重新编译gdb
./configure ‐‐prefix=/home/ubuntu/gdb‐8.2/install
make
sudo apt install ‐y texinfo
make install