GDB调试器基础使用:命令行调试完全指南¶
学习目标¶
完成本教程后,你将能够:
- 理解GDB调试器的工作原理和基本概念
- 掌握GDB的基本命令和操作流程
- 使用断点、单步执行等调试功能
- 查看和修改变量、内存和寄存器
- 分析程序调用栈和线程状态
- 进行远程调试和自动化调试
前置要求¶
在开始本教程之前,你需要:
知识要求: - 了解C/C++编程基础 - 熟悉编译和链接的基本概念 - 了解程序执行流程 - 掌握基本的命令行操作
技能要求: - 能够使用GCC编译器 - 会使用基本的Shell命令 - 了解调试的基本概念 - 能够阅读汇编代码(可选)
准备工作¶
软件准备¶
- 操作系统: Linux、macOS 或 Windows (WSL/MinGW)
- GDB: GNU Debugger 8.0+
- 编译器: GCC 或 ARM GCC工具链
- 文本编辑器: 任何文本编辑器
安装GDB¶
Linux系统:
# Ubuntu/Debian
sudo apt install gdb
# CentOS/RHEL
sudo yum install gdb
# Arch Linux
sudo pacman -S gdb
macOS系统:
# 使用Homebrew
brew install gdb
# 配置代码签名(macOS需要)
# 参考:https://sourceware.org/gdb/wiki/PermissionsDarwin
Windows系统:
验证安装¶
预期输出:
GNU gdb (GDB) 10.2
Copyright (C) 2021 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
系统要求¶
- 内存: 至少2GB RAM
- 磁盘: 100MB可用空间
- 权限: 普通用户权限(某些功能需要root)
步骤1: 理解GDB调试器¶
1.1 什么是GDB?¶
GDB (GNU Debugger) 是GNU项目开发的强大的命令行调试器,支持多种编程语言和处理器架构。
GDB的主要特点: - 开源免费,功能强大 - 支持多种语言(C、C++、Fortran等) - 支持多种架构(x86、ARM、MIPS等) - 可以调试本地和远程程序 - 支持脚本自动化 - 可扩展性强
GDB的核心功能: - 启动程序并指定运行参数 - 在指定条件下停止程序 - 检查程序停止时的状态 - 修改程序的变量和内存 - 分析程序崩溃原因
1.2 GDB工作原理¶
调试架构:
关键概念: 1. 符号表: 包含变量名、函数名和地址的映射 2. 断点: 程序执行到指定位置时暂停 3. 单步执行: 逐行或逐指令执行程序 4. 调用栈: 函数调用的层次关系 5. 观察点: 监视变量或内存的变化
1.3 调试信息¶
要使用GDB调试,程序必须包含调试信息。
编译时添加调试信息:
# 添加调试信息(-g选项)
gcc -g program.c -o program
# 添加详细调试信息(-g3)
gcc -g3 program.c -o program
# 禁用优化(便于调试)
gcc -g -O0 program.c -o program
调试信息级别:
- -g0: 无调试信息
- -g1: 最小调试信息(行号)
- -g2: 默认调试信息(推荐)
- -g3: 最详细调试信息(包括宏定义)
优化级别对调试的影响:
- -O0: 无优化,最适合调试
- -O1: 基本优化,部分变量可能被优化
- -O2: 标准优化,调试困难
- -O3: 激进优化,不推荐调试
1.4 GDB vs 图形化调试器¶
| 特性 | GDB | 图形化调试器 |
|---|---|---|
| 界面 | 命令行 | 图形界面 |
| 学习曲线 | 陡峭 | 平缓 |
| 功能 | 强大完整 | 功能有限 |
| 远程调试 | 原生支持 | 需要配置 |
| 自动化 | 脚本支持 | 较困难 |
| 资源占用 | 低 | 高 |
| 适用场景 | 服务器、嵌入式 | 桌面开发 |
选择建议: - 使用GDB: 远程调试、嵌入式开发、服务器调试、自动化测试 - 使用图形化: 桌面应用开发、初学者、快速调试
步骤2: GDB基本操作¶
2.1 创建测试程序¶
创建 test.c:
#include <stdio.h>
#include <stdlib.h>
int add(int a, int b) {
return a + b;
}
int multiply(int a, int b) {
int result = 0;
for (int i = 0; i < b; i++) {
result = add(result, a);
}
return result;
}
int main(int argc, char *argv[]) {
int x = 5;
int y = 3;
printf("x = %d, y = %d\n", x, y);
int sum = add(x, y);
printf("x + y = %d\n", sum);
int product = multiply(x, y);
printf("x * y = %d\n", product);
return 0;
}
编译程序:
2.2 启动GDB¶
方法1: 直接启动
方法2: 附加到运行中的进程
方法3: 调试core文件
2.3 GDB界面¶
启动GDB后,你会看到:
GNU gdb (GDB) 10.2
Copyright (C) 2021 Free Software Foundation, Inc.
...
Reading symbols from ./test...
(gdb)
GDB提示符: (gdb) 表示等待输入命令
2.4 基本命令¶
运行程序¶
# 运行程序
(gdb) run
# 或简写
(gdb) r
# 带参数运行
(gdb) run arg1 arg2
# 带输入重定向
(gdb) run < input.txt
# 带输出重定向
(gdb) run > output.txt
退出GDB¶
获取帮助¶
# 查看所有命令
(gdb) help
# 查看特定命令的帮助
(gdb) help run
(gdb) help break
# 查看命令类别
(gdb) help breakpoints
(gdb) help data
步骤3: 断点管理¶
3.1 设置断点¶
在函数处设置断点:
在行号处设置断点:
在地址处设置断点:
3.2 条件断点¶
设置条件断点:
# 当i等于5时才触发断点
(gdb) break 12 if i == 5
# 当指针不为空时触发
(gdb) break function if ptr != 0
# 复杂条件
(gdb) break 15 if x > 10 && y < 20
修改断点条件:
3.3 临时断点¶
3.4 查看断点¶
# 查看所有断点
(gdb) info breakpoints
# 或简写
(gdb) i b
# 输出示例:
# Num Type Disp Enb Address What
# 1 breakpoint keep y 0x0000000000401142 in main at test.c:16
# 2 breakpoint keep y 0x0000000000401126 in add at test.c:4
断点信息说明: - Num: 断点编号 - Type: 断点类型 - Disp: 触发后的处理(keep保留/del删除) - Enb: 是否启用(y启用/n禁用) - Address: 内存地址 - What: 断点位置
3.5 管理断点¶
禁用断点:
启用断点:
删除断点:
忽略断点:
3.6 观察点(Watchpoint)¶
观察点用于监视变量或内存的变化。
设置观察点:
读观察点:
访问观察点:
查看观察点:
步骤4: 程序执行控制¶
4.1 单步执行¶
逐语句执行(Step Into):
逐过程执行(Step Over):
单步执行汇编指令:
4.2 继续执行¶
继续执行到下一个断点:
执行到函数返回:
执行到指定位置:
4.3 跳转执行¶
跳转到指定行:
⚠️ 注意: 跳转可能导致程序状态不一致,谨慎使用。
4.4 信号处理¶
查看信号处理:
设置信号处理:
# 捕获SIGINT信号(Ctrl+C)
(gdb) handle SIGINT stop print
# 忽略SIGPIPE信号
(gdb) handle SIGPIPE nostop noprint
# 传递信号给程序
(gdb) handle SIGUSR1 pass
发送信号:
步骤5: 查看和修改数据¶
5.1 查看变量¶
打印变量值:
# 打印变量x的值
(gdb) print x
# 或简写
(gdb) p x
# 打印表达式
(gdb) print x + y
(gdb) p x * 2
# 打印函数返回值
(gdb) print add(3, 5)
不同格式打印:
# 十六进制
(gdb) print/x x
(gdb) p/x x
# 二进制
(gdb) print/t x
# 八进制
(gdb) print/o x
# 十进制
(gdb) print/d x
# 字符
(gdb) print/c x
# 浮点数
(gdb) print/f x
# 地址
(gdb) print/a x
打印数组:
打印结构体:
# 打印结构体
(gdb) print my_struct
# 打印结构体成员
(gdb) print my_struct.member
# 打印指针指向的结构体
(gdb) print *ptr_struct
5.2 查看内存¶
查看内存内容:
格式说明: - 数量: 显示多少个单元 - 格式: x(十六进制) d(十进制) u(无符号) o(八进制) t(二进制) a(地址) c(字符) s(字符串) - 大小: b(字节) h(半字) w(字) g(双字)
示例:
# 查看10个字节(十六进制)
(gdb) x/10xb 0x12345678
# 查看5个字(十六进制)
(gdb) x/5xw 0x12345678
# 查看字符串
(gdb) x/s 0x12345678
# 查看变量的内存
(gdb) x/10x &variable
5.3 修改变量¶
修改变量值:
# 修改变量x的值
(gdb) set variable x = 10
(gdb) set var x = 10
# 修改表达式
(gdb) set var x = x + 5
# 修改数组元素
(gdb) set var array[5] = 100
# 修改结构体成员
(gdb) set var my_struct.member = 20
修改内存:
# 修改内存地址的值
(gdb) set {int}0x12345678 = 100
# 修改字节
(gdb) set {char}0x12345678 = 0xFF
# 修改指针指向的值
(gdb) set *ptr = 50
5.4 查看寄存器¶
查看所有寄存器:
查看特定寄存器:
# 查看rax寄存器
(gdb) info registers rax
(gdb) i r rax
# 打印寄存器值
(gdb) print $rax
(gdb) p $rax
# x86-64常用寄存器
(gdb) p $rip # 指令指针
(gdb) p $rsp # 栈指针
(gdb) p $rbp # 基址指针
修改寄存器:
5.5 自动显示¶
设置自动显示:
查看自动显示列表:
删除自动显示:
步骤6: 调用栈分析¶
6.1 查看调用栈¶
显示调用栈:
# 显示完整调用栈
(gdb) backtrace
# 或简写
(gdb) bt
# 显示前5层调用栈
(gdb) backtrace 5
(gdb) bt 5
# 显示完整信息(包括参数)
(gdb) backtrace full
(gdb) bt full
调用栈输出示例:
#0 add (a=5, b=3) at test.c:4
#1 0x0000000000401165 in multiply (a=5, b=3) at test.c:10
#2 0x00000000004011a5 in main (argc=1, argv=0x7fffffffe0b8) at test.c:23
栈帧信息说明:
- #0: 栈帧编号(0是当前帧)
- add: 函数名
- (a=5, b=3): 函数参数
- at test.c:4: 源文件和行号
- 0x0000000000401165: 返回地址
6.2 切换栈帧¶
选择栈帧:
查看当前栈帧:
6.3 查看局部变量¶
查看当前函数的局部变量:
查看指定栈帧的变量:
6.4 查看源代码¶
显示当前位置的源代码:
# 显示当前行附近的代码
(gdb) list
(gdb) l
# 显示第10行附近的代码
(gdb) list 10
# 显示函数的代码
(gdb) list add
# 显示指定文件的代码
(gdb) list test.c:10
设置显示行数:
反汇编:
# 反汇编当前函数
(gdb) disassemble
(gdb) disas
# 反汇编指定函数
(gdb) disassemble add
# 反汇编地址范围
(gdb) disassemble 0x400500,0x400550
# 显示源代码和汇编混合
(gdb) disassemble /m
步骤7: 多线程调试¶
7.1 查看线程¶
查看所有线程:
输出示例:
Id Target Id Frame
* 1 Thread 0x7ffff7fc0740 (LWP 12345) main () at test.c:20
2 Thread 0x7ffff6fbf700 (LWP 12346) thread_func () at test.c:10
3 Thread 0x7ffff5fbe700 (LWP 12347) thread_func () at test.c:10
线程信息说明:
- *: 当前线程
- Id: GDB内部线程ID
- Target Id: 系统线程ID
- Frame: 当前执行位置
7.2 切换线程¶
切换到指定线程:
对所有线程执行命令:
# 对所有线程执行backtrace
(gdb) thread apply all backtrace
(gdb) thread apply all bt
# 对线程1和2执行命令
(gdb) thread apply 1 2 print x
7.3 线程断点¶
在特定线程设置断点:
线程特定的条件断点:
7.4 调度器锁定¶
锁定调度器:
# 锁定调度器(只运行当前线程)
(gdb) set scheduler-locking on
# 解锁调度器(所有线程运行)
(gdb) set scheduler-locking off
# 单步时锁定,其他时候解锁
(gdb) set scheduler-locking step
# 查看当前设置
(gdb) show scheduler-locking
应用场景: - 调试多线程竞态条件 - 隔离单个线程的行为 - 避免其他线程干扰调试
步骤8: 远程调试¶
8.1 远程调试架构¶
8.2 使用gdbserver¶
在目标机上启动gdbserver:
# 启动程序并等待连接
gdbserver :1234 ./program
# 附加到运行中的进程
gdbserver :1234 --attach <PID>
# 指定监听地址
gdbserver 192.168.1.100:1234 ./program
在开发机上连接:
# 启动GDB
gdb ./program
# 连接到远程目标
(gdb) target remote 192.168.1.100:1234
# 或使用extended-remote(支持运行新程序)
(gdb) target extended-remote 192.168.1.100:1234
8.3 嵌入式远程调试¶
使用OpenOCD进行ARM调试:
启动OpenOCD:
连接GDB:
# 启动ARM GDB
arm-none-eabi-gdb firmware.elf
# 连接到OpenOCD
(gdb) target extended-remote localhost:3333
# 加载程序
(gdb) load
# 复位并停止
(gdb) monitor reset halt
# 开始调试
(gdb) continue
8.4 远程调试技巧¶
文件传输:
# 设置sysroot(远程文件系统根目录)
(gdb) set sysroot /path/to/sysroot
# 设置远程文件路径
(gdb) set remote exec-file /path/on/target/program
符号文件:
# 加载符号文件
(gdb) symbol-file /path/to/program.elf
# 添加额外的符号文件
(gdb) add-symbol-file /path/to/library.so 0x12345678
远程命令:
# 发送命令到远程调试器
(gdb) monitor reset
(gdb) monitor halt
# OpenOCD特定命令
(gdb) monitor flash write_image erase firmware.bin 0x08000000
步骤9: GDB脚本和自动化¶
9.1 命令文件¶
创建GDB命令文件 debug.gdb:
# 连接到远程目标
target extended-remote localhost:3333
# 加载程序
load
# 复位
monitor reset halt
# 设置断点
break main
break error_handler
# 设置自动显示
display x
display y
# 运行
continue
使用命令文件:
9.2 初始化文件¶
用户初始化文件 ~/.gdbinit:
# 设置历史记录
set history save on
set history size 10000
set history filename ~/.gdb_history
# 美化输出
set print pretty on
set print array on
set print array-indexes on
# 禁用分页
set pagination off
# 设置反汇编风格
set disassembly-flavor intel
# 自定义命令
define hook-stop
info registers
x/10i $pc
end
9.3 Python脚本¶
GDB支持Python脚本扩展。
简单Python命令:
Python脚本示例 script.py:
import gdb
class HelloCommand(gdb.Command):
"""打印Hello World"""
def __init__(self):
super(HelloCommand, self).__init__("hello", gdb.COMMAND_USER)
def invoke(self, arg, from_tty):
print("Hello, World!")
HelloCommand()
class PrintVarCommand(gdb.Command):
"""打印变量的详细信息"""
def __init__(self):
super(PrintVarCommand, self).__init__("pvar", gdb.COMMAND_USER)
def invoke(self, arg, from_tty):
try:
val = gdb.parse_and_eval(arg)
print(f"Variable: {arg}")
print(f"Type: {val.type}")
print(f"Value: {val}")
print(f"Address: {val.address}")
except Exception as e:
print(f"Error: {e}")
PrintVarCommand()
使用自定义命令:
(gdb) source script.py
(gdb) hello
Hello, World!
(gdb) pvar x
Variable: x
Type: int
Value: 5
Address: 0x7fffffffe0a4
9.4 条件断点脚本¶
使用命令列表:
# 设置断点并添加命令
(gdb) break main
(gdb) commands 1
> silent
> printf "x = %d, y = %d\n", x, y
> continue
> end
说明:
- silent: 不显示断点触发信息
- printf: 打印信息
- continue: 自动继续执行
- end: 结束命令列表
9.5 自动化测试¶
测试脚本示例 test.gdb:
# 设置断点
break test_function
# 运行程序
run
# 检查结果
if result == expected
printf "Test PASSED\n"
else
printf "Test FAILED: expected %d, got %d\n", expected, result
end
# 退出
quit
批处理模式运行:
步骤10: 高级调试技巧¶
10.1 调试优化代码¶
问题: 优化后的代码难以调试,变量可能被优化掉。
解决方法:
-
使用volatile关键字:
-
部分禁用优化:
-
编译时保留调试信息:
10.2 调试宏¶
查看宏定义:
编译时包含宏信息:
10.3 调试内联函数¶
问题: 内联函数没有独立的栈帧。
解决方法:
-
禁用内联:
-
或在代码中禁用:
10.4 反向调试¶
GDB支持反向执行(需要记录执行历史)。
启用记录:
反向执行命令:
# 反向继续
(gdb) reverse-continue
(gdb) rc
# 反向单步
(gdb) reverse-step
(gdb) rs
# 反向next
(gdb) reverse-next
(gdb) rn
# 反向执行到函数开始
(gdb) reverse-finish
应用场景: - 找出变量何时被修改 - 追踪bug的根源 - 理解程序执行流程
10.5 checkpoint(检查点)¶
创建检查点:
恢复检查点:
删除检查点:
10.6 调试core dump¶
生成core文件:
调试core文件:
# 加载core文件
gdb program core
# 或
gdb program
(gdb) core-file core
# 查看崩溃时的状态
(gdb) bt
(gdb) info registers
(gdb) info locals
分析崩溃原因:
故障排除¶
问题1: 找不到调试符号¶
现象:
原因: - 编译时没有添加-g选项 - 程序被strip删除了符号
解决方法:
# 重新编译,添加调试信息
gcc -g program.c -o program
# 检查是否包含调试信息
file program
# 应该显示:not stripped
# 如果已经strip,需要重新编译
问题2: 源代码找不到¶
现象:
原因: - 源文件路径改变 - 编译时使用了相对路径
解决方法:
# 设置源代码搜索路径
(gdb) directory /path/to/source
(gdb) dir /path/to/source
# 查看当前搜索路径
(gdb) show directories
# 替换源代码路径
(gdb) set substitute-path /old/path /new/path
问题3: 断点无法设置¶
现象:
原因: - 程序未加载 - 符号表损坏 - 函数名错误
解决方法:
# 确保程序已加载
(gdb) file program
# 查看所有函数
(gdb) info functions
# 使用地址设置断点
(gdb) break *0x400500
# 延迟断点(动态库)
(gdb) set breakpoint pending on
(gdb) break library_function
问题4: 变量显示为optimized out¶
现象:
原因: - 编译器优化导致变量被优化
解决方法:
# 重新编译,禁用优化
gcc -g -O0 program.c -o program
# 或使用volatile
volatile int x = 0;
# 查看汇编代码确认变量位置
(gdb) disassemble
问题5: 远程调试连接失败¶
现象:
原因: - gdbserver未启动 - 防火墙阻止 - 网络不通
解决方法:
# 检查gdbserver是否运行
ps aux | grep gdbserver
# 检查端口是否监听
netstat -an | grep 1234
# 检查防火墙
sudo iptables -L
# 测试网络连接
ping 192.168.1.100
telnet 192.168.1.100 1234
问题6: 多线程调试混乱¶
现象: - 线程切换频繁 - 难以跟踪特定线程
解决方法:
# 锁定调度器
(gdb) set scheduler-locking on
# 只在特定线程设置断点
(gdb) break function thread 2
# 对所有线程执行命令
(gdb) thread apply all bt
问题7: GDB响应缓慢¶
原因: - 符号表过大 - 自动显示过多 - 记录模式开启
解决方法:
# 禁用自动显示
(gdb) undisplay
# 关闭记录模式
(gdb) record stop
# 禁用分页
(gdb) set pagination off
# 限制backtrace深度
(gdb) set backtrace limit 10
实用技巧¶
技巧1: 快速命令¶
命令缩写:
重复上一条命令:
技巧2: 命令历史¶
使用历史记录:
保存历史:
技巧3: 条件表达式¶
复杂条件:
# 字符串比较
(gdb) break function if strcmp(str, "test") == 0
# 指针检查
(gdb) break function if ptr != 0 && *ptr == 100
# 范围检查
(gdb) break function if x >= 10 && x <= 20
技巧4: 便捷函数¶
调用函数:
# 调用printf
(gdb) call printf("x = %d\n", x)
# 调用自定义函数
(gdb) call my_debug_function()
# 修改全局状态
(gdb) call reset_state()
技巧5: 日志记录¶
记录GDB会话:
# 开始记录
(gdb) set logging on
(gdb) set logging file debug.log
# 记录所有输出
(gdb) set logging overwrite on
# 停止记录
(gdb) set logging off
技巧6: TUI模式¶
启用文本用户界面:
# 启动时开启TUI
gdb -tui program
# 在GDB中切换TUI
(gdb) tui enable
Ctrl+X Ctrl+A
# TUI布局
(gdb) layout src # 源代码
(gdb) layout asm # 汇编
(gdb) layout split # 源代码+汇编
(gdb) layout regs # 寄存器
# 刷新屏幕
Ctrl+L
技巧7: 便捷宏¶
在~/.gdbinit中定义宏:
# 打印数组
define parray
set $i = 0
while $i < $arg1
print $arg0[$i]
set $i = $i + 1
end
end
# 使用:parray array 10
# 打印结构体数组
define pstruct_array
set $i = 0
while $i < $arg1
print $arg0[$i]
set $i = $i + 1
end
end
# 十六进制dump
define hexdump
set $addr = $arg0
set $len = $arg1
set $i = 0
while $i < $len
if $i % 16 == 0
printf "\n0x%08x: ", $addr + $i
end
printf "%02x ", *(unsigned char*)($addr + $i)
set $i = $i + 1
end
printf "\n"
end
技巧8: 调试技巧总结¶
调试流程: 1. 编译时添加-g选项 2. 设置断点在关键位置 3. 运行程序到断点 4. 检查变量和状态 5. 单步执行分析问题 6. 修改代码重新测试
常用调试模式: - 断点调试: 在关键位置停止 - 单步调试: 逐行执行分析 - 观察点调试: 监视变量变化 - 条件调试: 特定条件触发 - 反向调试: 回溯执行历史
总结¶
通过本教程,你学习了:
- ✅ GDB调试器的基本概念和工作原理
- ✅ 编译程序时添加调试信息的方法
- ✅ GDB的启动和基本操作命令
- ✅ 断点、观察点的设置和管理
- ✅ 程序执行控制(单步、继续、跳转)
- ✅ 变量、内存和寄存器的查看和修改
- ✅ 调用栈分析和栈帧切换
- ✅ 多线程程序的调试技巧
- ✅ 远程调试和嵌入式调试
- ✅ GDB脚本和自动化调试
- ✅ 高级调试技巧和故障排除
关键要点: 1. 编译时必须添加-g选项才能使用GDB调试 2. 断点是最常用的调试工具,合理设置断点很重要 3. 单步执行可以详细分析程序执行流程 4. 观察点用于监视变量变化,找出修改位置 5. 远程调试适用于嵌入式和服务器调试 6. GDB脚本可以实现自动化调试和测试 7. 熟练使用命令缩写可以提高调试效率
下一步学习¶
建议继续学习以下内容:
初级进阶¶
中级进阶¶
- OpenOCD调试工具使用 - 开源调试方案
- J-Link调试器高级功能 - 专业调试工具
- 内存泄漏检测与分析 - 内存问题调试
高级进阶¶
- 单元测试框架搭建 - 自动化测试
- 硬件在环(HIL)测试系统 - 系统级测试
实践项目建议¶
项目1: 简单程序调试¶
难度: ⭐ 目标: 使用GDB调试简单的C程序 任务: - 设置断点并运行程序 - 单步执行观察变量变化 - 修改变量值测试不同路径 - 分析调用栈
学习要点: - 基本GDB命令 - 断点使用 - 变量查看 - 程序流程控制
项目2: 多线程程序调试¶
难度: ⭐⭐ 目标: 调试多线程程序的竞态条件 任务: - 查看所有线程状态 - 切换线程进行调试 - 使用调度器锁定 - 找出竞态条件
学习要点: - 多线程调试 - 线程切换 - 调度器控制 - 竞态条件分析
项目3: 远程嵌入式调试¶
难度: ⭐⭐⭐ 目标: 使用GDB调试ARM嵌入式程序 任务: - 配置OpenOCD和GDB - 远程连接到目标板 - 设置断点调试固件 - 查看寄存器和内存
学习要点: - 远程调试配置 - OpenOCD使用 - 嵌入式调试技巧 - 硬件寄存器操作
项目4: 自动化调试脚本¶
难度: ⭐⭐⭐⭐ 目标: 编写GDB脚本实现自动化测试 任务: - 编写GDB命令脚本 - 使用Python扩展GDB - 实现自动化测试 - 生成测试报告
学习要点: - GDB脚本编写 - Python扩展 - 自动化测试 - 测试报告生成
常见问题FAQ¶
Q1: GDB和LLDB有什么区别?¶
A: - GDB: GNU项目,历史悠久,功能完整,支持广泛 - LLDB: LLVM项目,现代化设计,macOS默认调试器 - 建议: Linux用GDB,macOS可以用LLDB,命令类似
Q2: 如何调试没有源代码的程序?¶
A:
- 使用反汇编:disassemble
- 查看符号表:info functions
- 设置地址断点:break *0x400500
- 分析调用栈:backtrace
- 使用反编译工具辅助
Q3: GDB可以调试Python程序吗?¶
A: 可以,但需要特殊配置: - 安装python-dbg包 - 使用gdb的Python扩展 - 或使用pdb(Python调试器)
Q4: 如何在GDB中查看C++对象?¶
A:
# 打印对象
(gdb) print object
# 打印虚函数表
(gdb) info vtbl object
# 调用成员函数
(gdb) call object.method()
# 查看类型信息
(gdb) ptype ClassName
Q5: GDB支持哪些架构?¶
A: GDB支持众多架构: - x86/x86-64 - ARM/ARM64 - MIPS - PowerPC - RISC-V - AVR - 等等
Q6: 如何调试动态库?¶
A:
# 设置延迟断点
(gdb) set breakpoint pending on
(gdb) break library_function
# 查看已加载的库
(gdb) info sharedlibrary
# 加载符号
(gdb) add-symbol-file library.so 0x12345678
Q7: GDB可以调试内核吗?¶
A: 可以,使用KGDB: - 配置内核支持KGDB - 通过串口或网络连接 - 使用特殊的GDB命令 - 需要两台机器(调试机和目标机)
Q8: 如何提高GDB调试效率?¶
A: - 熟练使用命令缩写 - 配置.gdbinit文件 - 使用TUI模式 - 编写自定义脚本 - 使用条件断点减少停止次数 - 善用命令历史
参考资料¶
官方文档¶
教程和文章¶
- GDB Tutorial - 交互式教程
- Debugging with GDB - GNU官方教程
- GDB Cheat Sheet - 快速参考卡片
视频教程¶
- GDB Tutorial for Beginners - YouTube教程
- Advanced GDB Usage - 高级技巧
书籍推荐¶
- "The Art of Debugging with GDB, DDD, and Eclipse" - Norman Matloff
- "Debugging with GDB" - Richard Stallman
- "Advanced Linux Programming" - Mark Mitchell
在线资源¶
工具和插件¶
- GDB Dashboard: 增强的GDB界面
- GEF: GDB增强框架
- PEDA: Python Exploit Development Assistance
- pwndbg: CTF和漏洞分析工具
附录¶
附录A: GDB命令速查表¶
程序控制¶
| 命令 | 简写 | 说明 |
|---|---|---|
| run | r | 运行程序 |
| continue | c | 继续执行 |
| step | s | 单步进入 |
| next | n | 单步跳过 |
| finish | fin | 执行到返回 |
| until | u | 执行到指定位置 |
| jump | j | 跳转到指定位置 |
| quit | q | 退出GDB |
断点管理¶
| 命令 | 简写 | 说明 |
|---|---|---|
| break | b | 设置断点 |
| tbreak | tb | 临时断点 |
| watch | - | 设置观察点 |
| delete | d | 删除断点 |
| disable | - | 禁用断点 |
| enable | - | 启用断点 |
| info breakpoints | i b | 查看断点 |
数据查看¶
| 命令 | 简写 | 说明 |
|---|---|---|
| p | 打印变量 | |
| display | - | 自动显示 |
| x | - | 查看内存 |
| info registers | i r | 查看寄存器 |
| info locals | i locals | 查看局部变量 |
| info args | i args | 查看参数 |
栈帧操作¶
| 命令 | 简写 | 说明 |
|---|---|---|
| backtrace | bt | 查看调用栈 |
| frame | f | 选择栈帧 |
| up | - | 上一层栈帧 |
| down | - | 下一层栈帧 |
| info frame | i f | 查看栈帧信息 |
源代码¶
| 命令 | 简写 | 说明 |
|---|---|---|
| list | l | 显示源代码 |
| disassemble | disas | 反汇编 |
| info functions | i functions | 查看函数 |
| info variables | i variables | 查看变量 |
附录B: 常用格式说明¶
打印格式¶
/x- 十六进制/d- 十进制/u- 无符号十进制/o- 八进制/t- 二进制/a- 地址/c- 字符/f- 浮点数/s- 字符串
内存查看格式¶
b- 字节(1字节)h- 半字(2字节)w- 字(4字节)g- 双字(8字节)
附录C: .gdbinit配置示例¶
# 历史记录
set history save on
set history size 10000
set history filename ~/.gdb_history
# 输出美化
set print pretty on
set print array on
set print array-indexes on
set print elements 200
# 禁用分页
set pagination off
# 反汇编风格(Intel语法)
set disassembly-flavor intel
# 自动加载.gdbinit
set auto-load safe-path /
# 启动时显示信息
set verbose off
# 确认操作
set confirm off
# 线程调试
set non-stop on
set target-async on
# Python支持
python
import sys
sys.path.insert(0, '/path/to/gdb/scripts')
end
# 自定义命令
define hook-stop
info registers
x/5i $pc
end
反馈与支持: - 如果你在学习过程中遇到问题,欢迎在评论区留言 - 发现文档错误或有改进建议,请提交Issue - 想要分享你的GDB使用经验,欢迎投稿
版本历史: - v1.0 (2024-01-15): 初始版本发布
许可证: 本文档采用 CC BY-SA 4.0 许可协议