透過 gcc -S 產生的組合語言,可窺探 C 程式的底層執行機制:變數如何映射到記憶體、函數呼叫慣例、堆疊幀建立與拆除等。本文以經典的 hello world 為例,逐步剖析 GCC (AT&T 語法) 輸出的組合語言。
| 寄存器 | 慣用用途 |
|---|---|
| %eax | 累加器、函數回傳值 |
| %ebx | 基址暫存器 |
| %ecx | 計數器 |
| %edx | 資料暫存器 |
| %esp | 堆疊指標 |
| %ebp | 基址指標(堆疊幀指標) |
| %esi | 來源變址 |
| %edi | 目的變址 |
AT&T 語法中寄存器前加 %,立即數前加 $。
字串常數 "hello,world" 放在 .rodata 區,標記為 .LC0:
.section .rodata
.LC0:
.string "hello,world"
程式啟動時已分配,位址固定。
區域變數、函數參數、回傳位址、暫存寄存器值皆放於堆疊。堆疊向低位址增長,%esp 永遠指向棧頂(下一個可用位置)。
常用指令:
pushl %reg / pushl $imm:壓入堆疊,%esp -= 4popl %reg:彈出堆疊,%esp += 4.text
.p2align 4,,15
.globl main
.type main, @function
main:
leal 4(%esp), %ecx # 保存 argc 位址
andl $-16, %esp # 對齊堆疊到 16 位元組邊界
pushl -4(%ecx) # 壓入 argc
pushl %ebp # 保存舊基址指標
movl %esp, %ebp # 建立新堆疊幀
pushl %ecx # 保存 argc 位址
subl $4, %esp # 分配區域變數空間 (對齊用)
movl $.LC0, (%esp) # 將字串位址放到堆疊頂 (puts 參數)
call puts # 呼叫 puts
movl $0, %eax # 回傳值 0
addl $4, %esp # 釋放區域空間
popl %ecx # 還原 argc 位址
popl %ebp # 還原基址指標
leal -4(%ecx), %esp # 還原堆疊指標
ret # 返回呼叫者
andl $-16, %esp 確保 SSE 指令所需的 16-byte 對齊。puts 只有一個參數,直接放在 (%esp)。pushl %ebp/popl %ebp 維護堆疊幀鏈,便於除錯回溯。%eax。movl src, dst:複製資料(注意 AT&T 語法來源在前)addl src, dst:dst += srcsubl src, dst:dst -= src範例:將變數 myvalue 加 100 再減 20:
movl myvalue, %ebx
addl $100, %ebx
subl $20, %ebx
movl %ebx, myvalue
.data/.rodata。%ebp/%esp 管理。