GDB 调试汇编(Debugging with GDB)
汇编程序调试主要观察寄存器、内存与指令级单步。用 GAS + gcc -g 可保留行号(gcc 驱动时配合 .file/.loc,纯 as 默认不带源码行,但可看符号与地址)。
进阶内容(core dump、反向调试、多线程、脚本化)见模块十九。
准备带调试信息的程序
gcc -g -fPIE -pie -o prog prog.s # 保留符号与(可能的)行信息
# 或用 as + ld 时也尽量带 -g
as -g prog.s -o prog.o
ld prog.o -o prog
阅读
gcc -S输出时,函数首尾的.cfi_startproc/.cfi_endproc/.cfi_def_cfa_offset等 CFI 伪指令是生成栈回溯(DWARF unwind)信息用的,手工写汇编可忽略。
常用 GDB 命令
| 命令 | 简写 | 作用 |
|---|---|---|
break _start |
b |
在符号/地址设断点 |
break *0x401000 |
— | 在绝对地址设断点 |
tbreak |
tb |
一次性断点(命中后自动删除) |
break *0x401011 if $rcx == 2 |
— | 条件断点(可按寄存器/标志位设条件) |
run |
r |
运行 |
stepi |
si |
单步一条机器指令 |
nexti |
ni |
单步(跳过函数调用) |
continue |
c |
继续运行 |
info registers |
i r |
显示所有寄存器 |
info registers rax rbx |
— | 显示指定寄存器 |
print/x $rax |
p/x |
以十六进制打印寄存器 |
x/10i $rip |
x |
反汇编从 rip 起 10 条指令 |
x/4xw $rsp |
— | 以 4 个 32 位字显示栈内容 |
x/s $rsi |
— | 以字符串查看 rsi 指向内容 |
x/16xb $rdi |
— | 16 字节十六进制查看 |
disassemble |
disas |
反汇编当前函数 |
disassemble /r |
— | 反汇编并显示原始机器码字节 |
disassemble /m |
— | 源码与汇编混合显示(需调试信息) |
disassemble 0x401000, 0x401020 |
— | 反汇编指定地址范围 |
layout asm |
— | TUI 汇编视图 |
layout regs |
— | TUI 汇编+寄存器视图 |
layout split |
— | TUI 源码+汇编分屏 |
watch *(int*)0x... |
— | 内存写监视 |
GDB 中寄存器写
$rax(不带%),立即数直接写;这与汇编源码的%rax不同。
x 命令完整格式
x/Nfu addr:N = 显示数量,f = 显示格式,u = 单位大小。
| 格式 f | 含义 | 单位 u | 含义 |
|---|---|---|---|
x |
十六进制 | b |
字节 |
d |
十进制 | h |
半字(2 字节) |
u |
无符号十进制 | w |
字(4 字节) |
o |
八进制 | g |
双字(8 字节) |
t |
二进制 | ||
c |
字符 | ||
s |
字符串(只看 f) | ||
i |
反汇编指令(只看 f) |
常用组合:x/8gx $rsp(栈上 8 个 64 位十六进制)、x/i $pc(当前指令)、x/2i $rip(下两条指令)、x/s 0x400200(字符串)。
查看标志位(EFLAGS)
p $eflags 直接给出置位标志的名字(GDB 15):
(gdb) p $eflags
$1 = [ PF ZF IF ] # 直观显示哪些标志被置位
(gdb) p/x $eflags
$2 = 0x246 # 需要时也可看十六进制原始值
常用标志位的位置与掩码(用于条件断点等):
| 标志 | 位 | 掩码 |
|---|---|---|
| CF | 0 | 0x1 |
| PF | 2 | 0x4 |
| AF | 4 | 0x10 |
| ZF | 6 | 0x40 |
| SF | 7 | 0x80 |
| IF | 9 | 0x200 |
| DF | 10 | 0x400 |
| OF | 11 | 0x800 |
(gdb) p $eflags & 0x40 # 判 ZF 是否置位
(gdb) break *0x401020 if $eflags & 0x1 # 进位时断下
TUI 操作
(gdb) tui enable # 进入 TUI(或直接 layout asm)
(gdb) layout asm / layout regs / layout split / layout next
(gdb) focus cmd # 焦点在命令窗口 / asm / regs(可用方向键滚动)
(gdb) tui disable # 退出 TUI
反汇编风格切换
GDB 默认 AT&T 语法,可切到 Intel:
set disassembly-flavor intel
disassemble _start
示例会话
$ gdb ./prog
(gdb) break _start
(gdb) run
(gdb) layout asm # 可视化汇编
(gdb) stepi # 单步
(gdb) info registers rax
(gdb) x/8xw $rsp # 看栈顶
(gdb) print/x $rip
检查系统调用与内存
(gdb) catch syscall # 在每次系统调用处中断
(gdb) x/s $rsi # 以字符串查看 rsi 指向内容
(gdb) x/16xb $rdi # 16 字节十六进制查看
常见技巧
-
用
display/i $pc让每次停下都自动显示下一条要执行的指令:(gdb) display/i $pc -
每步都显示下一条指令(不弹 TUI 时方便):
(gdb) set disassemble-next-line on -
条件断点:
break loop if $rcx == 0。 -
修改寄存器试验:
set $rax = 5。 -
修改内存:
set *(int*)0x600000 = 10。 -
在源码里"硬编码"断点:插入
int3(机器码0xcc,也可写.byte 0xcc),程序运行到此处自动陷入 GDB——适合调试难以命中的路径:# ... 可疑代码 ... int3 # 相当于在此处下了断点
配合其他工具
strace ./prog:跟踪所有系统调用与信号。objdump -d -M intel prog:静态反汇编查看机器码。readelf -hS prog:查看 ELF 头与节区布局。nm prog:查看符号表。