notice:coredump章节、汇编章节还需要完善
预备知识:
1、注意使用gdb时,最好带上-g参数编译可执行文件。否则出现(No debugging symbols found in a.out)
gcc -g main.c -o a.out
2、查看命令行可带参数帮助
gdb --help
3、查看命令行可使用命令及详情
(gdb) help all //查看所有命令
(gdb) help break //查看break 使用,其他命令也可单独help
4、gdb 长选项 -和--都可以
gdb --help 和 gdb -help 都可以
5、gdb 使用的命令不必完整,只需命令的前面几个字母即可(只要无歧义),可以tab补全
6、回车会重复执行上一次命令
7、查看gdb版本
(gdb) show version
或者gdb -v
1、gdb 命令行可带参数
参数 | 作用 | 示例 | 相关命令 |
--args或-args | 向可执行文件传递参数 | gdb --args a.out xxx 111 向a.out可执行文件传递参数 xxx和111 | set args xxx 111 show args |
-p或--pid attach | 指定attach进程 | gdb -p 进程PID gdb attach 进程PID | |
-c或-c= --core或者--core= | 指定coredump文件 | gdb a.out core.27503 gdb a.out --core core.27503 gdb a.out --core=core.27503 | |
-e | 指定可执行文件名 | | |
-d | 指定目录加入到源文件搜索路径 | | |
--cd | 指定目录作为路径运行gdb | | |
-s | 指定文件读取符号表 | | |
-q | gdb 启动时,关掉提示或者打印信息 | gdb -q a.out | |
2、gdb 调试可使用命令及参数
2.1 断点
命令 | 含义 | 示例 | 相关命令 |
break 或b | 设置断点 | break local-break break local-break if if-command local-break格式为: 函数名(function-name) 文件名:行号(b main.c:5) 文件名:函数名(b main.c:main) 函数A:A调用的函数B(b main:test_b) +offset-number 以当前程序暂停位置为准,向后+ offset-number 行设置break 点 -offset-number 以当前程序暂停位置为准,向前- offset-number 行设置break 点 beadk *address if-command格式为: shell 脚本 (例如 break main if argc == 5 断到main时都判断 argc == 5 ,为真才停住) | print 函数名 得到函数地址 ignore break-number count-number |
info break或i b | 查看所有断点 | | |
delete或 d | 删除断点 | delete break-number-xxx 或者 d break-number-xxx | |
tbreak或tb | 设置断点,但只生效一次 | | |
save breakpoints file-name | 保存断点到file-name | | |
source file-name | 加载保存的gdb断点 | | |
ignore break-number count-number | 设置断点,但是忽略断点多少次,接下来count-number次编号的断点触发都不会让程序中断,只有count-number+1次触发的中断才会让程序中断 | | |
command | 当运行到某断点时,执行某指定的脚本或程序 | command command-number 当运行到断点时,执行的命令(commands 即可输入gdb命令等,end表示退出输入) | |
//在程序入口处打断点
readlef -h a.out 中 ertry point addrss //获取程序入口
gdb 查看 info files 中 entry point //获取程序入口
b *程序起始地址
然后 run
2.2 运行/调试/退出程序
命令 | 含义 | 示例 | 相关命令 |
step | (区别:step会进入有符号表的下一行函数内部,next不会进入) | | |
next | 单步执行 | | |
run或r | 从头运行可执行文件 | run para_1 para_2 para_3 run可以直接携带参数 | |
start | 直接运行程序 调试没有调试信息程序,直接运行是无效的。找不到main入口。 调试没有调试信息的可参考:调试无调试信息程序 |
| |
continue或c | 继续运行可执行程序 | | |
quit或q | 退出gdb | | |
finish | 执行完当前函数 | | |
return | 函数不会继续执行,直接按照指定的返回值xxx返回 | return 返回值xxx | |
call 或 print | 直接调用函数执行 | call xxxx_func() | |
set args | 设置可执行文件运行参数 | set args xxx 111 (参数会保留,直接清空则使用set args ) | |
show args | 查看可执行文件携带参数 | | |
set | 改变变量的值 | set main::string_test="xxxxx" 改变main函数中字符串string_test 或通过内存地址改变变量(字符串) p &p1 查看p1类型变量 (char (*)[4]) 0x80477a4 set {char [4]}0x80477a4 = "xxxxx" 设置变量的值 set var var_param = 3 或者 set{para_type}para_address=para_number 修改寄存器的值 set var $eax = 3 | |
jump | 跳转到指定地址执行(PC寄存器保存程序下一条要执行的指令,通过修改这个寄存器的值,可以修改程序执行的流程) | | |
2.3 函数/堆栈/帧
命令 | 含义 | 示例 | 相关命令 |
backtrace或bt | 打印此时的堆栈情况 | backtrace full(显示所以局部变量值) | |
info frame | 显示函数堆栈帧 | |
|
frame | 切到第几个栈帧上 | frame number。切到第几个栈帧上, frame address 切到指定栈地址上 (bt可以查看到内层的函数在第几个栈帧上) | info frame |
up | 向上切换栈帧。默认up 1,可以指定up n | | |
down | 向下切换栈帧,默认 down 1,可以指定 down n | | |
up-silently | 安静向上切换栈帧,不打印 | | |
down-silently | 安静向下切换栈帧,不打印 | | |
debug entry-values | 可以输出正常堆栈,以及尾调用关系 (尾调用会被优化) | | |
P func2:pointer | 打印某函数内部的变量(打印某栈帧中变量) 或先切换到该栈帧(函数)中,然后打印 | | |
2.4 打印/查看命令
//gdb中直接显示的打印静态变量,可能不一定是正确的,(全局变量和局部变量重名情况)可以直接指定文件 p xxx.c::xxx_para
命令 | 含义 | 示例(打印变量a) | 相关命令 | |
或者p | 变量 (可强转变量类型) | 直接打印变量 默认10进制 | | |
/x | 十六进程打印 | | | |
/o | 8进程打印 | | | |
/u | 无符号打印(10进制) | | | |
/d | 有符号打印(10进制) | | | |
/t | 二进制打印 | | | |
/c | 字符打印 | | | |
/s | 字符串打印 | | | |
/f | 浮点打印 | | | |
/a | 16进制打印 | | | |
array@length | 打印array数组的前length个变量的值 | | | |
p array[index]@number或p *array@number | 打印数组中任意元素连续元素的值 | | | |
func2:pointer | 打印某函数内部的变量(打印某栈帧中变量) | | |
命令 | 含义 | 示例 | 相关命令 | |
x/nfu | n | 按照u类型为单位,输出指定地址n个 | | |
f | 输出格式进制:x(16)、o(8)、d(10)、h(2)、s(字符) | | | |
u | 单个单元长度:b(一个byte)、h(两个byte)、 w(四个byte)、g(8个byte) | | | |
$_ | 保存着x命令最后检测的内存地址(很多命令都会调用x命令检查内存) | | | |
$__ | 保存着x命令最后检测的内存地址的值 | | |
命令 | 含义 | 示例 | 相关命令 |
set print pretty on/off | 每行打印一个结构体成员 | | |
set print array on/off | 设置数组打印 | | |
set print array-indexes on/off | 设置数组下标 (数组默认不打印索引下标) | | |
set print union on/off | 设置打印联合体 | | |
set print elements number-of-elements | 设置数组最大限制数 (缺省最多打印200个元素) | set print elements 0 (设置为0,等效于不设置限制) | |
set print elements unlimited | 不设置限制 | | |
set print object on | 如果缺省按照派生类打印 | | |
set pagination off | 设置gdb不提示,直接输出全部内容 (当单次输出内容较多时,gdb会打印部分内容,并提示是否输出后面内容) | | |
命令 | 含义 | 示例 | 相关命令 |
list或l | 打印代码 (list 可以带行号、函数、指定行号范围(1、10)、向前向后打印(-、+)) | | |
where | 显示当前行号以及所处函数 | | |
命令 | 含义 | 示例 | 相关命令 |
backtrace full | 打印函数的局部变量 | | |
backtrace full n | 从内向外显示n个栈帧 | | |
backtrace full -n | 从外向内显示n个栈帧 | | |
info locals | 打印当前函数局部变量 | | |
命令 | 含义 | 示例 | 相关命令 |
whatis xxx_para | 查看某一个变量类型 | | |
ptype xxx_para | 查看更详细的信息类型 | | |
info variables xxx_para | 查看定义变量的文件 | info variables ^xxx_para$ (可以通配查找) | |
mallocinfo | 打印程序动态分配的内存 | | |
2.5 coredump文件
命令 | 含义 | 示例 | 相关命令 |
generate-core-file 或 gcore | 被调试进程主动产生core dump文件 | | |
core | 在gdb中指定coredump文件 (一般前面先file 可执行文件) | | |
gdb 可执行文件 coredump的文件 | 解析coredump文件 | | |
2.6 进程/线程
//gdb调试多进程时候默认追踪父进程,子进程独立运行
//info threads
//显示信息:
//第一项ID:gdb标识每个线程的唯一ID,
//第二项 Target id:linux 对每一个线程的标识ID
//第三项 Frame :显示的是线程执行到哪个函数,理解为GDB。前面带*表示的是current thread,及gdb attach进程时,默认attach的线程。
命令 | 含义 | 示例 | 相关命令 |
info thread | 列出当前进程的线程 | info threads 或info threads th-pid | |
thread | 切换到指定线程 | thread 线程-PID | |
info proc mappings | 查看进程的内存映射信息 | | |
info files或info target | 查看进程的内存映射信息以及库情况 | | |
attach | 跟踪进程 | | |
detach | 脱离进程 | | |
set follow-fork-mode child | 调试子进程 | | |
set detach-on-fork off | 同时调试父子进程(默认detach-on-fork 是on的状态)在调试一个进程时,另外一个进程处于挂起 | | |
info inferiors | 查看进程情况 ,显示*表示正在调试进程 | | |
inferiors info-number | 切换到调试进程去 | | |
set schedule-multiplen on | 父子进程同时运行,默认 schdule-multiple 是off的 | | |
set print thread-events off | 线程产生和退出时的打印提示信息 | | |
set scheduler-locking on | gdb调试多线程程序时,只允许一个调试线程运行,其他处于暂停状态(默认是off状态) | | |
$_thread | 用来保存当前正在调试的线程号(gdb 7.2引入) | | |
add-inferior [ -copies n ] [ -exec executable ] | gdb 同时调试多个程序,加载可执行文件 ,n 默认是1 | | |
maint info program-spaces | 打印当前所有被调试的进程信息(调试多个进程) | | |
$_exitcode | 记录调试程序正常退出的exit code | | |
2.7 观察点/异常捕获
//观察点有软件和硬件两种,软件实现的观察点会导致程序运行较慢。
//如果支持硬件观察点,则设置观察点时会打印Hardware watchpoint 2: i
//系统调用和编号的映射关系为 include/asm/unisted.h
命令 | 含义 | 示例 | 相关命令 |
watch | 当一个变量的值发生变化时,程序会停止下来 | watch para_a | |
set can-use-hw-watchpoint | 不使用硬件观察点 | | |
info watchpoint | 罗列当前所有的观察点 | | |
disable | 去使能观察点 | disable para_a | |
enable | 使能观察点 | | |
delete | 删除观察点 | | |
watch 观察表达式 thread thread-number | 设置观察点,只有特定线程才生效(这种只能使用硬件观察) | | |
rwatch或rw | 设置读观察点,当发生读时,会暂停住(这种只能使用硬件观察) | | |
awatch 或aw | 设置写观察点(这种只能使用硬件观察) | | |
catchpoint或catch | 异常捕获 | catch syscall [name | number] 系统调用的名字或者 系统调用编号 catch 函数名 catch signal signal-number catch syscall :为所有的系统调用设置catchpoint 其他可以help catch | |
tcatch | 设置 catchpoint 只生效一次 | | |
2.8 日志相关
//gdb中,默认是不保存历史命令的,可以通过如下命令设置成保存历史命令。
命令 | 含义 | 示例 | 相关命令 |
set history filename file_name | 设置历史命令保存在文件file_name中 (默认保存在.gdb_history) | | |
set history save on | 设置保存历史命令 | | |
set logging on | 执行gdb的过程记录下来 (默认的日志文件是gdb.txt) | | |
set logging file file-name | 改变日志记录文件为file-name | | |
set logging overwrite on | 让输出覆盖之前的日志文件 | | |
set logging redirect on | gdb的日志不会打印在终端 | | |
2.9 汇编/寄存器
//若需预处理器宏信息,需要带 -g3编译 p NAME
//gdb默认显示汇编指令格式为AT&T格式
//break 不会把端点设置在汇编指令层次函数的开头,会停留在第三条汇编指令(汇编调用函数会先push 栈,再移动栈指针,再call)
//如果要把断点设置在汇编指令层次函数的开头,则使用b *func
命令 | 含义 | 示例 | 相关命令 |
info registers | 列出寄存器 | 打印单个寄存器的值: info registers reg-name 或者 p $eax | |
info all-registers | 可以看到包括浮点寄存器和向量寄存器的内容 | | |
display | 查看汇编指令 | display /3i $pc 显示当程序停止时,将要执行的汇编指令(一次看几条) | |
disassemble main | 查看main函数的汇编 | | |
set disassembly-flavor | 设置汇编指令格式 | set disassembly-flavor intel 改为intel 格式 set disassembly-flavor att 改为AT&T | |
set disassemble-next-line | 设置反汇编后续代码 | set disassemble-next-line on (任意情况都反汇编) set disassemble-net-line auto 在没有源码的情况下才反汇编 set disassemble-net-line off | |
disassemble | 反汇编代码 | disassemble /m func函数 将函数代码和汇编指令映射起来(每一条c语句都对应了汇编) disassemble 代码开始地址,代码结束地址。可以查看指定起始地址的代码的汇编代码(查看某一行对应的地址范围info line 13) | |
2.10 信号
命令(对收到的信号处理) | | 含义 | 示例 | 相关命令 |
handle signal_xxx | stop/nostop | 设置信号发生时,是否暂停程序 设置stop,默认print。设置noprint ,默认nostop | | |
print/noprint | 设置信号发生时,是否打印 | | | |
pass/nopass | 设置信号发生时,是否把信号丢给程序处理 nopass表示不给程序。三种选项可一同设置 | | |
info signals
Signal Stop Print Pass to program Description
SIGHUP Yes Yes Yes Hangup
//Signal:表示信号量
//stop:表示被调试程序有信号(SIGHUP)发生时,gdb是否会暂停程序
//Print:表示被调试程序有信号(SIGHUP)发生时,gdb是否会打印相关信息
//Pass to program: gdb是否会把这个信号发送给被调试程序
命令(主动触发信号处理) | 含义 | 示例 | 相关命令 | |
signal signal_xxxx | 发送信号signal_xxxx给进程 | signal 0 可以使程序从新运行,但不发信号给进程 | | |
$_siginfo | 在gdb把信号丢给程序之前, 可以通过$_siginfo 变量读取一些额外信号信息 | | |
2.11 脚本/GDB配置文件
gdb 启动时读取home目录和当前目录下的配置文件,执行里面的命令(.gdbinit文件)
# 常见列入 .gdbinit文件的配置,可自行配置
# 保存历史命令
set history filename ~/.gdb_history
set history save on
# 退出时不显示提示信息
set confirm off
# 按照派生类型打印对象
set print object on
# 打印数组的索引下标
set print array-indexes on
# 每行打印一个结构体成员
set print pretty on
//gdb 支持的脚本文件:一种为gdb自身命令的脚本,.gdbinit gdb启动时会执行里面的命令;第二种gdb还支持其他语言的脚本
命令 | 含义 | 示例 | 相关命令 | |
shell 或 !ls 或 ,ls | 执行shell脚本 | shell cat 1.txt | | |
set script-extension | off | 所有的脚本文件都解析成gdb的命令脚本 | | |
soft | 根据脚本文件扩展名决定如何解析脚本。 如果gdb支持解析这种脚本语言(比如python),就按这种语言解析,否则就按命令脚本解析; | | | |
strict | 根据脚本文件扩展名决定如何解析脚本。 如果gdb支持解析这种脚本语言(比如python),就按这种语言解析,否则不解析; | | | |
show script-extension | 查看GDB解析脚本文件格式 | | |
2.12 lib库/源文件
带*的表示库缺少调试信息
命令 | 含义 | 示例 | 相关命令 |
info files | 列出当前文件 | | |
info share 或info sharedlibrary regex | 显示程序加载的共享链接库信息 regex为正则表达式 | | |
directory | gdb不能找到源文件位置,可以使用directory命令设置源文件的路径 | directory 源文件 | |
set substitute-path | 当源文件路径改变时,设置新的源文件路径 | set substitute-path 旧的路径 新的路径 | |
2.13 其他
命令 | 含义 | 示例 | 相关命令 |
make CFLAGS="-g -O0" | gdb里面可直接make编译程序 | | |
cd | 切换工作目录(路径) | | |
pwd | 查看当前工作目录(路径) | directory 源文件 | |
set prompt | 设置命令提示符(debug调试前面显示设置的字符),区分多个gdb。格 | set prompt (main gdb) (结尾有空) | |
set env | 设置被调试程序的环境变量 | set env LD_PRELOAD=/lib/libpthread.so.0 | |
show version | 查看gdb版本信息 | | |
show copying | 查看gdb版权信息 | | |
show warranty | 查看gdb版权信息 | | |
set confirm off | 把退出时提示信息(Quit anyway?(y or n))关掉 | | |
tty | 为程序指定单独终端 (gdb默认输出输入为同一个终端。) | gdb -tty /dev/pts/2 ./a.out | |
3、附件:
1、调试无调试信息程序
在程序入口处打断点
获取程序入口地址:
readlef -h a.out 中 ertry point addrss
或者 gdb 中 info files 中 entry point 可获取
直接 b *程序起始地址,然后 run
2、尾调用(tail call)
一个函数的最后一条指令是调用另外一个函数时,开启编译优化后,编译器尝尝将最后一个被调用的函数返回值直接作为调用者的返回值。
3、gdb 打印不全
---Type <return> to continue, or q <return> to quit---
set pagination off