組譯器(assembler)
/ uh-SEM-bler /
編譯器思考完之後,並不會直接把原始數字交給機器。它先寫出組合語言:一種薄薄的、人類可讀的速記,對應處理器真正的指令,會有像 mov rax, 5 與 add rax, rbx 這樣的行。組譯器是那個小巧、相當機械化的程式,它接受這份組合語言文字,把每一行轉成處理器讀取的確切機器碼位元組。這是近乎一對一的翻譯,與其說是轉換,不如說是謄寫。
具體而言,組合語言是一串助憶符(像 mov、add、call、ret 這樣的短名)加上運算元(它們作用的暫存器、數字與位址)。組譯器查找每個助憶符,把它連同運算元編碼成晶片懂的數值 opcode,並依序排下那些位元組。當組合語言提及一個最終位址尚未知曉的名稱,例如定義在別處的函式,組譯器會留下一個帶標籤的空白以待稍後填入,並把該名稱記在符號表裡。它的輸出是目的檔,還不是可執行的程式。
為何要有獨立的一步,而不直接跳到位元組?大多是歷史與模組化的緣故:把組合語言保留為乾淨的文字格式,讓編譯器、手寫組合語言與各種工具都能餵給同一個組譯器,也給人類一個可讀的層次去檢視。日常程式設計中你很少手動執行組譯器;gcc 或 clang 驅動程式會在編譯與連結階段之間替你跑它。你可以用像 gcc -S 這樣的旗標要求看它的輸入,它會在產生 .s 組合語言檔後停下來。
; 編譯器發出的組合語言 組譯器產生的機器位元組 mov eax, 5 ; -> b8 05 00 00 00 ret ; -> c3
每個組合語言助憶符對應到一段固定的 opcode 位元組序列,由 CPU 執行。
組譯器產生的是目的檔,不是可執行檔;定義在其他檔案中的東西其位址仍是空白,要由連結器稍後修補進去。