GCC 工具鏈預設使用 AT&T 語法 (源自 AT&T Bell Labs),與 Intel 語法 (MSVC、NASM) 差異極大。閱讀編譯器輸出 (gcc -S) 或編寫內聯匯編時,掌握 AT&T 語法至關重要。
| 項目 | AT&T (GNU as) | Intel (NASM/MSVC) |
|---|---|---|
| 立即數 | $1234 (前綴 $) | 1234 |
| 暫存器 | %eax (前綴 %) | eax |
| 操作數順序 | 來源, 目的 (movl %eax, %ebx = eax → ebx) | 目的, 來源 (mov ebx, eax) |
| 運算元大小後綴 | b/w/l/q (byte/word/long/quad) movl = 32-bit | 指令隱含或用 ptr (mov eax, dword ptr [mem]) |
| 記憶體寻址 | disp(base, index, scale) -4(%rbp), (%rax,%rcx,4) | [base + index*scale + disp] [rbp-4], [rax+rcx*4] |
| 註解 | # comment | ; comment |
| 類別 | 暫存器 (64/32/16/8-bit) | 慣用用途 |
|---|---|---|
| 通用 | %rax/%eax/%ax/%al, %rbx/%ebx/%bx/%bl, %rcx/%ecx/%cx/%cl, %rdx/%edx/%dx/%dl | 運算、返回值 (%rax)、計數器 (%rcx) |
| 索引/基址 | %rsi/%esi/%si/%sil, %rdi/%edi/%di/%dil | 字串操作來源/目的、函數前 2 參數 |
| 棧/幀指針 | %rsp/%esp/%sp/%spl, %rbp/%ebp/%bp/%bpl | 棧頂、幀基址 |
| 擴展通用 | %r8~%r15 (及 d/w/b 子暫存器) | 額外參數 (第 3~6)、暫存 |
| 指令指針 | %rip/%eip/%ip | 下一條指令地址 (不可直接修改) |
| 旗標 | %rflags/%eflags | 條件碼 (ZF, CF, SF, OF...) |
# 資料傳送 (大小後綴 b/w/l/q)
movb $0x41, %al # 立即數 → 8-bit 暫存器
movw %ax, (%rdi) # 16-bit → 記憶體
movl %eax, -4(%rbp) # 32-bit → 記憶體 (區域變數)
movq %rax, %rbx # 64-bit 暫存器間傳送
# 算術/邏輯 (同樣有大小後綴)
addl %ecx, %eax # eax += ecx
subl $10, %esp # esp -= 10 (分配棧空間)
imull %edx # 有號乘法: edx:eax = eax * edx
idivq %rcx # 有號除法: rax=商, rdx=餘數
# 控制流
cmpl %eax, %ebx # 比較 ebx - eax (設置旗標)
je label # 相等跳轉 (ZF=1)
jl label # 有號小於跳轉 (SF!=OF)
jmp *%rax # 間接跳轉
# 棧操作
pushq %rax
popq %rbx
# 函數調用 (System V AMD64 ABI)
# 參數: %rdi, %rsi, %rdx, %rcx, %r8, %r9, 其餘壓棧
# 返回值: %rax (整數/指標), %xmm0 (浮點)
call printf
# 調用者清理棧 (若有棧參數)
通用形式:DISP(BASE, INDEX, SCALE) → 有效位址 = BASE + INDEX*SCALE + DISP
DISP:位移常數 (可為符號名)。BASE:基址暫存器 (可省略)。INDEX:索引暫存器 (可省略)。
SCALE:1, 2, 4, 8 (可省略,預設 1)。範例:
(%rax) # 間接寻址
8(%rax) # 基址 + 位移
(%rax,%rcx,4) # 基址 + 索引*4 (陣列索引)
-4(%rbp) # 幀相對 (區域變數)
array(,%rcx,8) # 純索引陣列 (64-bit 元素)
// C 代碼
void dummy() {
int a = 1234;
int b = a;
}
// gcc -S -O0 dummy.c 輸出 (精簡)
dummy:
pushq %rbp
movq %rsp, %rbp
movl $1234, -4(%rbp) # a = 1234
movl -4(%rbp), %eax # 載入 a
movl %eax, -8(%rbp) # b = a
popq %rbp
ret
int foo(int a, int b) {
int sum;
asm volatile (
"addl %%ebx, %%eax\n\t"
: "=a" (sum) // 輸出: %eax → sum
: "a" (a), "b" (b) // 輸入: a→%eax, b→%ebx
: "cc" // 破壞: 條件碼
);
return sum;
}
gcc -S 輸出即可熟練。objdump -d -M intel 可輸出 Intel 語法反組譯,對照學習極佳。