C 语言汇编级调试与反汇编实战
更新时间:2026-08-26。本文是
languages/c/主题专家层文档。普通 gdb 调试看的是"源码行",汇编级调试看的是"寄存器和指令"——能直接验证调用约定、观察编译器生成的机器码、追查优化后的诡异行为。这是 C 程序员进阶到"看得懂机器"的最后一课。
本文要回答的问题
- gdb 怎么反汇编当前函数?怎么查看寄存器值?
- 怎么用寄存器验证"参数是怎么传的、返回值放哪"?
stepi和step有什么区别?什么时候要用汇编级单步?
一、gdb 汇编级调试命令
| 命令 | 作用 |
|---|---|
disassemble [函数] | 反汇编函数(看机器码) |
info registers [寄存器] | 查看寄存器值 |
stepi | 单步执行一条指令(而非一行源码) |
nexti | 单步一条指令(不进入函数) |
x/8x $rsp | 查看栈内存 |
set $寄存器 = 值 | 修改寄存器 |
二、实测:验证调用约定
调试一个 add(3, 4) 函数,断点停下后:
c
int add(int a, int b) { return a + b; }gdb 实测输出:
text
Breakpoint 1, add (a=3, b=4) at libtest.c:2
Dump of assembler code for function add:
0x400575 <+0>: push %rbp
0x400576 <+1>: mov %rsp,%rbp
0x400579 <+4>: mov %edi,-0x4(%rbp) ← 参数 a 存栈
0x40057c <+7>: mov %esi,-0x8(%rbp) ← 参数 b 存栈
=> 0x40057f <+10>: mov -0x8(%rbp),%eax
0x400582 <+13>: mov -0x4(%rbp),%edx
0x400585 <+16>: add %edx,%eax ← a + b
0x400588 <+19>: retq
rax 0xc 12 ← 返回值已在 rax
rdi 0x3 3 ← 第 1 个参数在 rdi
rsi 0x4 4 ← 第 2 个参数在 rsi这段实测直接印证了 System V AMD64 调用约定:
- 第 1 个参数
a=3在rdi,第 2 个参数b=4在rsi - 返回值
12在rax push rbp/mov rsp,rbp是标准的栈帧建立
三、stepi:指令级单步
step 一次走一行源码,stepi 一次走一条机器指令:
text
(gdb) stepi ← 执行一条指令
(gdb) stepi ← 再执行一条
(gdb) info registers rax rdi ← 随时看寄存器变化汇编级单步用于:
- 追查优化后代码的执行路径
- 观察每条指令对寄存器的修改
- 理解内联汇编、SIMD 指令的行为
四、objdump:离线反汇编
不用 gdb 也能反汇编,用 objdump:
bash
objdump -d ./app # 反汇编整个可执行文件
objdump -d -M intel ./app # Intel 语法(默认 AT&T)
objdump -S ./app # 混合源码 + 汇编(需 -g 编译)-M intel 把 AT&T 语法(mov %edi,%eax)换成 Intel 语法(mov eax,edi),两种语法各有拥趸,Intel 语法更接近多数人的直觉。
五、什么时候需要汇编级调试
| 场景 | 为什么 |
|---|---|
| 优化后行为异常 | -O2 下源码行对不上,要看汇编 |
| 验证调用约定 | 看寄存器确认参数/返回值 |
| 追查内联汇编 bug | 看自己的 asm 是否写对 |
| 理解性能热点 | 看 perf annotate 后的汇编,理解为什么慢 |
| 逆向/安全分析 | 分析二进制行为 |
六、与本站主线衔接
- 调用约定:寄存器传参的完整约定,见 内存布局与 ABI。
- perf annotate:反汇编看热点指令,见 perf 剖析 C 程序。
- 编译优化:-O2 下的汇编形态,见 编译优化行为。
- crash 排查:gdb 定位崩溃的完整流程,见 crash 排查线。
一句话总结
汇编级调试 = gdb 的 disassemble 反汇编 + info registers 看寄存器 + stepi 单步指令;实测里 rdi=3/rsi=4/rax=12 直接印证了调用约定——能看懂寄存器,才算真正"看得懂机器"。