理解 C 代碼如何編譯為機器指令,是性能優化、逆向分析、嵌入式開發的核心技能。本文以簡單 sum 函數為例,演示 GCC 產生匯編、優化對比、GDB 反匯編、鏈接後地址重定位全流程。
code.cint accum = 0;
int sum(int x, int y) {
int t = x + y;
accum += t;
return t;
}
gcc -Sgcc -S code.c # 生成 code.s (AT&T 語法) gcc -O2 -S code.c # 開啟 O2 優化生成 code.s
_sum:
pushl %ebp
movl %esp, %ebp
subl $4, %esp # 為局部變量 t 分配棧空間
movl 12(%ebp), %eax # eax = y
addl 8(%ebp), %eax # eax = x + y
movl %eax, -4(%ebp) # t = x + y
movl -4(%ebp), %eax # eax = t
addl %eax, _accum # accum += t
movl -4(%ebp), %eax # 返回值放入 eax
leave
ret
_sum:
pushl %ebp
movl %esp, %ebp
movl 12(%ebp), %eax # eax = y
movl 8(%ebp), %edx # edx = x
popl %ebp # 早期恢復 ebp
addl %edx, %eax # eax = x + y
addl %eax, _accum # accum += x + y
ret # 直接返回,省略 leave/棧幀
優化點: 省略棧幀建立/銷毀、寄存器直通計算、指令重排對齊 (.p2align)。
gcc -c code.c -o code.o gdb code.o (gdb) x/19xb sum # 檢查 sum 函數 19 字節機器碼 (gdb) disassemble sum # 反匯編顯示
objdump -d code.o # 目標文件 objdump -d prog # 可執行文件 (鏈接後)
輸出顯示指令地址、機器碼、助記符。鏈接後全局變量 _accum 地址從 0x0 重定位為實際地址 (如 0x402010)。
// main.c
int main() { return sum(1, 2); }
gcc -O2 -o prog code.o main.c
objdump -d prog | grep -A20 "_sum:"
鏈接後代碼與優化版幾乎一致,唯一差別:全局變量地址已解析為具體虛擬地址。
| 目的 | 命令 | 輸出 |
|---|---|---|
| 生成匯編源碼 | gcc -S [-O2] file.c | file.s (文本) |
| 編譯不鏈接 | gcc -c file.c | file.o (目標文件) |
| 反匯編目標/可執行文件 | objdump -d file.o/prog | 指令+機器碼+地址 |
| GDB 動態反匯編 | gdb prog → disassemble func | 運行時上下文 |
| 查看機器碼十六進制 | gdb → x/Nxb addr | 原始字節 |