本文系統介紹 Linux 平台下組合語言開發的核心知識:AT&T 與 Intel 語法差異、開發工具鏈 (GAS/NASM, ld, GDB/ALD)、系統呼叫慣例 (int 0x80)、命令列參數存取、GCC 內嵌組語完整語法。配合 Hello World、參數處理、內嵌組語範例,是 Linux 組合語言入門與進階的綜合參考。
| 特性 | AT&T (GAS 預設) | Intel (NASM/MASM) |
|---|---|---|
| 暫存器前綴 | %eax | eax |
| 立即數前綴 | $1 | 1 |
| 操作數順序 | 來源, 目的 (addl $1, %eax) | 目的, 來源 (add eax, 1) |
| 運算長度後綴 | b/w/l (movb) | 前綴 byte/word/dword ptr |
| 間接跳轉/呼叫 | jmp *%eax | jmp eax |
| 遠跳轉/呼叫 | ljump $seg, $off | jmp far seg:off |
| 記憶體定址 | disp(base,index,scale) | [base+index*scale+disp] |
Linux 保護模式下採用 32 位元線性定址,段基址通常為 0,有效位址 = disp + base + index*scale。
# hello.s
.data
msg: .string "Hello, world!\n"
len = . - msg
.text
.global _start
_start:
movl $len, %edx
movl $msg, %ecx
movl $1, %ebx
movl $4, %eax
int $0x80
movl $0, %ebx
movl $1, %eax
int $0x80
; hello.asm
section .data
msg db "Hello, world!", 0xA
len equ $ - msg
section .text
global _start
_start:
mov edx, len
mov ecx, msg
mov ebx, 1
mov eax, 4
int 0x80
mov ebx, 0
mov eax, 1
int 0x80
兩者皆直接呼叫核心 sys_write (4) 和 sys_exit (1),無需 libc,體積極小、速度極快。
| 工具 | 指令範例 | 說明 |
|---|---|---|
| GAS | as --gstabs -o hello.o hello.s | GCC 後端組譯器,支援 AT&T,--gstabs 產生符號表供 GDB |
| NASM | nasm -f elf hello.asm | Intel 語法,快速、支援多格式 (ELF/COFF/Mach-O) |
| ld | ld -o hello hello.o | 標準連結器,-s 會剝離符號表 |
| GDB/DDD | gdb hello | 源碼級除錯,需符號表 |
| ALD | ald hello | 專用組合語言除錯器,指令集disassemble, break, run, next, register |
EAX (定義於 /usr/include/bits/syscall.h,SYS_write, SYS_exit 等)EBX, ECX, EDX, ESI, EDI (最多 5 個)EAXEBX 指向該區塊 (類似堆疊呼叫慣例,參數右至左入棧,EBX=ESP)範例:write(fd, buf, len) → EAX=SYS_write, EBX=fd, ECX=buf, EDX=len
程式啟動時棧佈局:
ESP → argc
argv[0] (程式名)
argv[1] ...
argv[argc-1]
0 (NULL 結束)
envp[0] ...
envp[envc-1]
0 (NULL 結束)
組合語言存取範例:
_start:
popl %ecx # argc
vnext:
popl %ecx # argv[i]
test %ecx,%ecx; jz exit
movl %ecx,%ebx; xorl %edx,%edx
strlen: movb (%ebx),%al; inc %edx; inc %ebx; test %al,%al; jnz strlen
movb $10,-1(%ebx) # 換行
movl $4,%eax; movl $1,%ebx; int $0x80
jmp vnext
exit: movl $1,%eax; xorl %ebx,%ebx; int $0x80
__asm__("asm statements" : outputs : inputs : clobbers);
%0, %1... 代表輸出/輸入變數,實體暫存器寫 %%eax"=r"(var),= 表示輸出,r 限定暫存器"r"(var),無 ="%eax",告知 GCC 此暫存器被修改int a=10, b=0;
__asm__ __volatile__(
"movl %1, %%eax\n\t"
"movl %%eax, %0"
: "=r"(b) /* 輸出 %0 */
: "r"(a) /* 輸入 %1 */
: "%eax" /* 損壞 eax */
);
printf("Result: %d, %d\n", a, b);
| 約束 | 含義 |
|---|---|
r | 任意通用暫存器 |
q | eax/ebx/ecx/edx |
a/b/c/d | 指定 eax/ebx/ecx/edx |
S/D | esi/edi |
I | 常數 0-31 |
m/v/o | 記憶體單元 |
i/h | 立即數 |
g | 任意 (暫存器/記憶體/立即數) |
Linux 組合語言開發雖不如 Windows (MASM32) 擁有統一 IDE,但工具鏈 (GAS/NASM, ld, GDB/ALD) 成熟完整,且直接對接核心系統呼叫,無 libc 依賴可產生極小極快執行檔。AT&T 與 Intel 雙語法共存滿足不同背景開發者。GCC 內嵌組語以強大的約束系統完美解決暫存器分配與 C 變數綁定難題,是效能關鍵路徑優化的利器。掌握本文核心知識點,即可在 Linux 系統程式開發、嵌入式優化、逆向工程、編譯器後端等領域自如運用組合語言威力。