承接前文「C 語言開發環境從零開始」,本文提供 gcctest.c 經 GCC -E 與 -S 產出的 .i 與 .s 完整內容,並逐段解析關鍵區塊,幫助你建立原始碼到組語的具體對應認知。
gcctest.c#include
int main(void) {
printf("This is a gcc test!\n");
return 0;
}
完整 .i 超過 800 行(含 stdio.h 全文),此處僅列關鍵部分:
# 1 "gcctest.c"
# 1 ""
# 1 ""
# 1 "/usr/include/stdio.h" 1 3 4
...(stdio.h 完整內容:FILE 結構、printf 原型、_IO_stdin 等)...
# 1 "gcctest.c" 2
int main(void) {
printf("This is a gcc test!\n");
return 0;
}
# 行號 "檔名" 旗標:行號追蹤資訊,供除錯器/編譯器報錯定位。printf 已展開為 __printf_chk 等內建函式(若啟用 -D_FORTIFY_SOURCE)。
.file "gcctest.c"
.section .rodata
.LC0:
.string "This is a gcc test!"
.section .text.unlikely
.section .text.startup
.section .text.hot
.section .text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
endbr64
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
leaq .LC0(%rip), %rdi
call puts@PLT
movl $0, %eax
popq %rbp
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0"
.section .note.GNU-stack,"",@progbits
| 指令 | 說明 |
|---|---|
.file "gcctest.c" | 原始檔名,供除錯資訊使用。 |
.section .rodata | 切換至唯讀資料段,存放字串常數。 |
.LC0: .string "..." | 定義標號 .LC0 指向字串「This is a gcc test!」。 |
.globl main | 宣告 main 為全域符號,連結器可見。 |
pushq %rbp / movq %rsp, %rbp | 建立堆疊框架(Frame Pointer),方便除錯追蹤呼叫堆疊。 |
leaq .LC0(%rip), %rdi | RIP 相對定址載入字串位址至 %rdi(第一參數暫存器)。 |
call puts@PLT | 呼叫 puts(PLT 動態連結存根),輸出字串並換行。 |
movl $0, %eax | 回傳值 0 放入 %eax(回傳值暫存器)。 |
popq %rbp / ret | 還原堆疊框架並返回呼叫者。 |
.cfi_* | DWARF Call Frame Information,供例外處理/堆疊展開使用。 |
-O2 重新產出 .s,觀察 pushq %rbp 框架指令消失、puts 直接以 %rdi 傳參、尾端優化等差異。gcc -static -S gcctest.c 觀察 call puts 變為直接呼叫 puts 實體位址。aarch64-linux-gnu-gcc -S gcctest.c 對比 AArch64 指令集(str/bl/ret)。objdump -d gcctest.o 或 gdb ./gcctest -batch -ex 'disas main' 驗證最終機器碼。