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:查看符号表。