GDB调试信息分享
在一定程度上学习了C源码之后,决定提高对各类开发工具的学习。准备学习一下GDB,Makefile,Cmake和git等工具。
目录
- 1. GDB简介
- 2. 安装配置
- 3. 基础命令
- 4. 断点操作
- 5. 检查程序状态
- 6. 控制程序执行
- 7. 监视变量
- 8. 调试多线程程序
- 9. 调试核心转储文件
- 10. 高级技巧
- 11. 常见错误与调试案例
- 12. 实用配置与插件
- 13. 参考资料
1. GDB简介
GDB (GNU Debugger) 是GNU软件系统中的强大调试工具,它可以让我们在程序运行时观察程序的内部状态,主要用于调试用C、C++等编译型语言编写的程序。GDB的主要功能包括:
- 启动程序,并指定可能影响其行为的任何内容
- 使程序在指定条件下停止
- 程序停止时检查发生了什么
- 修改程序中的内容,以便纠正错误的影响
1.1 为什么选择GDB?
- 开源免费:作为GNU项目的一部分,GDB是完全开源的
- 功能强大:从简单到复杂的调试场景都能胜任
- 跨平台:支持多种操作系统和处理器架构
- 社区支持:有大量的文档、教程和用户社区
- 广泛集成:很多IDE都集成了GDB作为其调试后端
2. 安装配置
2.1 Linux系统
在大多数Linux发行版中,可以通过包管理器安装GDB:
# Debian/Ubuntu系统
sudo apt-get update
sudo apt-get install gdb# Red Hat/CentOS系统
sudo yum install gdb# Arch Linux
sudo pacman -S gdb
2.2 macOS系统
在macOS上,可以通过Homebrew安装:
brew install gdb
注意:macOS有安全限制,需要对GDB进行代码签名才能正常调试程序。详细步骤如下:
- 创建自签名证书
- 使用证书对GDB进行签名
- 配置系统信任该证书
具体命令请参考macOS下配置GDB部分。
2.3 Windows系统
在Windows上,可以通过以下方式获取GDB:
- 安装MinGW或Cygwin,它们包含GDB
- 使用MSYS2环境
- 通过Visual Studio的WSL扩展使用Linux版GDB
2.4 验证安装
安装完成后,打开终端输入:
gdb --version
如果显示版本信息,则表示安装成功。
3. 基础命令
3.1 启动GDB
# 调试可执行文件
gdb program# 调试正在运行的进程
gdb -p PID# 调试core dump文件
gdb program core# 启动并传递参数
gdb --args program arg1 arg2
3.2 基本GDB命令
命令 | 缩写 | 功能 |
---|---|---|
help | h | 显示帮助信息 |
run | r | 运行程序 |
break | b | 设置断点 |
continue | c | 继续执行 |
next | n | 单步执行(不进入函数) |
step | s | 单步执行(进入函数) |
finish | fin | 运行到当前函数返回 |
print | p | 打印变量值 |
backtrace | bt | 显示调用栈 |
info | i | 显示各种信息 |
list | l | 显示源代码 |
quit | q | 退出GDB |
3.3 GDB交互模式
GDB提供了一个交互式命令行界面。启动GDB后,你会看到(gdb)
提示符,表示GDB已准备好接收命令。
$ gdb ./myprogram
GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2
...
(gdb)
在这个提示符下,你可以输入各种命令来控制程序的执行和检查程序状态。
4. 断点操作
断点是调试的核心功能,它让程序在指定位置暂停执行,以便你检查程序状态。
4.1 设置断点
# 在指定行设置断点
(gdb) break filename:linenum
(gdb) b main.c:15# 在函数入口设置断点
(gdb) break function
(gdb) b main# 按条件设置断点
(gdb) break filename:linenum if condition
(gdb) b main.c:15 if i==100
4.2 管理断点
# 列出所有断点
(gdb) info breakpoints
(gdb) i b# 删除断点
(gdb) delete breakpoint_num
(gdb) d 1# 禁用/启用断点
(gdb) disable breakpoint_num
(gdb) enable breakpoint_num# 设置临时断点(只触发一次)
(gdb) tbreak location
4.3 高级断点
# 在满足条件时中断程序
(gdb) watch expression
(gdb) watch x>0# 读取变量时中断
(gdb) rwatch variable# 修改变量时中断
(gdb) awatch variable# 捕获异常
(gdb) catch throw
5. 检查程序状态
当程序在断点处停止时,可以检查其状态:
5.1 查看源代码
# 显示当前位置周围的代码
(gdb) list
(gdb) l# 显示指定函数的代码
(gdb) list function
(gdb) l main# 显示指定行周围的代码
(gdb) list linenum
(gdb) l 10
5.2 检查变量
# 打印变量值
(gdb) print variable
(gdb) p i# 打印表达式
(gdb) print expression
(gdb) p i+j*2# 打印数组
(gdb) print array[index]
(gdb) p array[5]
(gdb) p *array@len
5.3 检查内存
# 检查指定地址的内存内容
(gdb) x/nfu address
(gdb) x/10xw ptr # 以十六进制显示10个字长# 参数说明:
# n - 内存单元的数量
# f - 显示格式(x-十六进制,d-十进制,c-字符等)
# u - 单元大小(b-字节,h-半字,w-字,g-双字)
5.4 检查调用栈
# 显示调用栈
(gdb) backtrace
(gdb) bt# 查看详细栈帧
(gdb) info frame
(gdb) i f# 切换栈帧
(gdb) frame frame_num
(gdb) f 2
6. 控制程序执行
6.1 启动与停止
# 开始运行程序
(gdb) run [args]
(gdb) r# 继续执行到下一个断点
(gdb) continue
(gdb) c# 中止程序
(gdb) kill
6.2 单步执行
# 执行一行源代码,不进入函数
(gdb) next
(gdb) n# 执行一行源代码,进入函数
(gdb) step
(gdb) s# 执行到当前函数返回
(gdb) finish
(gdb) fin# 继续执行指定行数
(gdb) advance location
6.3 跳转执行
# 跳过一段代码
(gdb) jump linenum
(gdb) j 25# 执行到指定位置
(gdb) until location
(gdb) u 50
7. 监视变量
7.1 显示命令
# 设置自动显示
(gdb) display expression
(gdb) display i# 查看display设置
(gdb) info display# 取消自动显示
(gdb) undisplay display_num
(gdb) delete display display_num
7.2 检查数据结构
# 打印结构体
(gdb) print struct_var
(gdb) p *struct_ptr# 打印类的成员
(gdb) print object.member
(gdb) p this->member# 设置打印选项
(gdb) set print pretty on # 美化打印结构体
(gdb) set print array on # 美化打印数组
(gdb) set print object on # 显示完整对象
8. 调试多线程程序
8.1 查看线程
# 显示所有线程
(gdb) info threads# 切换到指定线程
(gdb) thread thread_num
8.2 控制线程
# 仅在当前线程执行命令
(gdb) set non-stop on# 设置所有线程的断点
(gdb) break location thread all# 让一个特定线程在断点处停止
(gdb) break location thread thread_num
8.3 多线程调试策略
# 在一个线程停止时,所有线程都停止
(gdb) set schedule-multiple off# 定位线程间竞争
(gdb) set scheduler-locking on # 只运行当前线程
9. 调试核心转储文件
当程序崩溃时,系统可能会生成核心转储文件(core dump),包含程序崩溃时的状态。
9.1 生成核心转储文件
首先确保系统允许生成核心转储文件:
# 允许生成无限大小的核心转储文件
ulimit -c unlimited
9.2 调试核心转储文件
# 调试核心转储文件
gdb program core
在GDB中,可以检查崩溃时的状态:
(gdb) bt # 查看崩溃时的调用栈
(gdb) frame 0 # 切换到崩溃位置
(gdb) print variables # 检查相关变量
10. 高级技巧
10.1 GDB脚本
可以创建GDB命令脚本文件:
# mycommands.gdb
break main
run
next 5
print i
continue
然后在GDB中执行:
(gdb) source mycommands.gdb
10.2 自定义命令
# 定义自定义命令
(gdb) define mycommand
Type commands for definition of "mycommand".
End with a line saying just "end".
> print i
> print j
> print i+j
> end# 使用自定义命令
(gdb) mycommand
10.3 条件断点与日志
# 条件断点
(gdb) break location if condition# 断点命令列表
(gdb) break location
(gdb) commands
> silent
> print variable
> continue
> end
10.4 修改程序状态
# 修改变量值
(gdb) set variable = value
(gdb) set i = 100# 调用函数
(gdb) call function(args)
(gdb) call printf("i = %d\n", i)# 执行表达式
(gdb) print expression
11. 常见错误与调试案例
11.1 段错误 (Segmentation Fault)
段错误是最常见的错误之一,通常由以下原因导致:
- 访问未初始化或已释放的指针
- 数组越界访问
- 栈溢出
调试步骤:
- 运行程序直到崩溃
- 使用
backtrace
查看调用栈 - 检查指针变量
(gdb) run
Program received signal SIGSEGV, Segmentation fault.
(gdb) bt
(gdb) frame 0
(gdb) print pointer
(gdb) x/10xw pointer
11.2 内存泄漏
使用Valgrind等工具配合GDB:
valgrind --leak-check=full ./program
然后在报告的泄漏位置设置断点进行调试。
11.3 死锁
多线程死锁调试:
(gdb) info threads
(gdb) thread apply all bt
检查所有线程的调用栈,寻找互相等待的资源。
11.4 调试实际案例
案例1: 空指针解引用
#include <stdio.h>void process(int *p) {printf("%d\n", *p); // 可能的空指针解引用
}int main() {int *ptr = NULL;process(ptr);return 0;
}
调试流程:
(gdb) break process
(gdb) run
(gdb) next
(gdb) print p
$1 = (int *) 0x0
案例2: 数组越界
#include <stdio.h>int main() {int arr[5] = {1, 2, 3, 4, 5};for (int i = 0; i <= 5; i++) { // 错误:应该是i < 5arr[i] *= 2;}return 0;
}
调试流程:
(gdb) break main
(gdb) run
(gdb) watch arr[5] // 监视越界访问
(gdb) continue
12. 实用配置与插件
12.1 .gdbinit文件
在主目录创建.gdbinit
文件可以设置GDB的启动配置:
# ~/.gdbinit
set print pretty on
set print array on
set print array-indexes on
set pagination off# 自定义提示符
set prompt \033[01;31mgdb$ \033[0m# 自定义命令
define llinfo locals
end
12.2 GDB前端工具
- GDB Dashboard: 基于TUI的GDB增强界面
- GDB-Frontend: 基于浏览器的GDB前端
- DDD (Data Display Debugger): 图形化GDB前端
- VSCode调试器: 通过插件支持GDB
12.3 GDB插件
- GEF (GDB Enhanced Features): 为漏洞研究者优化
- PEDA (Python Exploit Development Assistance): 增强逆向工程功能
- pwndbg: 专为CTF设计的GDB扩展
12.4 远程调试配置
服务器端:
gdbserver :1234 ./program
客户端:
(gdb) target remote hostname:1234
13. 参考资料
- GDB官方文档
- GDB快速参考
- GDB调试实战指南
- C/C++高级调试技术
- GDB在线调试教程
附录A: GDB命令速查表
类别 | 命令 | 描述 |
---|---|---|
启动 | gdb program | 启动GDB调试程序 |
gdb -p PID | 附加到运行中的进程 | |
gdb program core | 调试崩溃转储 | |
运行控制 | run | 运行程序 |
continue | 继续执行 | |
next | 单步执行(不进入函数) | |
step | 单步执行(进入函数) | |
finish | 运行至函数结束 | |
until | 运行至指定行 | |
kill | 终止调试程序 | |
断点 | break location | 设置断点 |
tbreak location | 设置临时断点 | |
watch expression | 设置数据断点 | |
info breakpoints | 列出所有断点 | |
delete breakpoint_num | 删除断点 | |
disable/enable breakpoint_num | 禁用/启用断点 | |
查看数据 | print expression | 显示表达式值 |
x/nfu address | 检查内存 | |
info locals | 显示局部变量 | |
info args | 显示函数参数 | |
ptype variable | 显示变量类型 | |
display expression | 每步显示表达式 | |
栈操作 | backtrace | 显示调用栈 |
frame frame_num | 选择栈帧 | |
info frame | 显示栈帧详情 | |
up/down | 向上/下移动栈帧 | |
多线程 | info threads | 显示所有线程 |
thread thread_num | 切换线程 | |
thread apply all command | 对所有线程执行命令 | |
其他 | set var variable=value | 修改变量值 |
call function() | 调用函数 | |
define command | 定义自定义命令 | |
source file | 执行GDB命令文件 | |
shell command | 执行shell命令 | |
help [command] | 显示帮助信息 |