NASM 將 Intel 語法的 x86 組合語言轉換為平面二進位檔或物件檔。平面二進位檔包含輸出的位元組,但沒有標準重新定位或符號中繼資料。ELF、Mach-O 或 COFF 等物件檔會保留區段、符號與重新定位資訊,使連結器能將其與其他物件及函式庫組合。
區段、標籤與符號 #
物件檔來源通常會分開可執行程式碼、已初始化資料與零初始化儲存空間:
global add_one
extern external_value
section .text
add_one:
mov eax, edi
add eax, 1
ret
section .rodata
message: db "hello", 10
message_size equ $ - message
section .bss
scratch: resq 8標籤定義目前的組譯位置。GLOBAL 將符號匯出給連結器,而 EXTERN 宣告另一個物件必須定義的符號。以 . 開頭的區域標籤會限制在前一個非區域標籤的範圍內。區段名稱與屬性取決於輸出格式;.text、.rodata、.data 與 .bss 是 ELF 慣例,不是 NASM 的通用關鍵字。
使用 -f bin 選取的平面二進位檔沒有連結器可指派虛擬位址。ORG 會變更位址計算使用的起始位置,但不會插入填充或將位元組載入該位址。開機磁區、韌體與嵌入式映像檔必須建立自己的配置與填充需求。
有效位址與位置獨立性 #
NASM 將目的運算元放在前面。方括號表示記憶體有效位址:
mov rax, value ; address/immediate determined by relocation rules
mov rbx, [value] ; load from memory
mov ecx, [base + index*4 + 8]
lea rdx, [base + index*4 + 8] ; calculate address; do not loadx86 位址允許基底、以 1、2、4 或 8 縮放的索引,以及位移,但受模式特定編碼規則約束。當沒有暫存器決定大小時,記憶體運算元可能需要 byte、dword 或 qword 等明確限定詞。
BITS 16、BITS 32 與 BITS 64 選取目標指令編碼模式,不會在執行時切換處理器。物件格式會設定適當預設值;-f bin 預設為 16 位元模式,因此平面 32 或 64 位元程式碼應明確指定模式。
在 64 位元模式中,DEFAULT REL 會讓無暫存器記憶體參照使用 RIP 相對位址,除非由 ABS 覆寫;FS 與 GS 覆寫具有 NASM 文件記載的特殊預設值。RIP 相對參照可以到達 ±2 GiB 內的程式碼或靜態資料,適合位置獨立程式碼。位置獨立性也取決於物件格式、重新定位類型、程式碼模型、符號可見性與連結器。單靠 DEFAULT REL 不能讓任意外部符號或絕對位址使用變成位置獨立。
定義、保留與對齊資料 #
DB、DW、DD、DQ、DT、DO、DY 與 DZ 會輸出大小遞增的已初始化值。常見定義包括:
bytes: db 0x7f, "NASM", 0
words: dw 1, 2, 3
value: dq 42
table: times 16 dd 0RESB、RESW、RESD 與 RESQ 會在支援的區段中保留未初始化儲存空間。EQU 定義組譯時常數,$ 表示目前位置。保留空間不一定會為物件檔增加位元組,因為類似 BSS 的區段記錄的是大小而不是已初始化內容。
ALIGN n 會前進至要求的邊界,並根據其引數與作用中區段填補間隙。物件中的對齊也取決於區段對齊與連結器配置,因此對齊不足區段內的對齊偏移並不充分。整個區段需要更強保證時,請使用 SECTION ... align=n 或格式特定機制。
前處理器巨集與 Include #
NASM 在組譯前執行以權杖為基礎的前處理器:
%define與%xdefine定義單行巨集,但展開時機不同。%macro與%endmacro定義多行巨集;%1、%2等名稱代表位置引數。%if、%ifdef、%elif與%endif有條件地選取來源。%include讀取另一個來源檔,而-I加入 include 搜尋路徑。%assign維護數值前處理器變數。
%ifndef WORD_SIZE
%define WORD_SIZE 8
%endif
%macro save_pair 2
push %1
push %2
%endmacro巨集產生的是來源,而不是可呼叫例程。它們可能重複程式碼、捕捉名稱或產生令人意外的診斷,因此檢查展開時,清單(-l file.lst)與反組譯很有用。當共用巨集檔案被多次 include 時,include guard 可防止多重定義。
針對 ABI 組譯與連結 #
輸出格式與 ABI 是分開的選擇。在 64 位元 Linux System V 目標上:
nasm -f elf64 -g -F dwarf -o add_one.o add_one.asm
cc -o demo main.c add_one.o在 32 位元 Linux 目標上,使用 -f elf32 與設定好必要 32 位元執行階段的連結器/編譯器,例如 cc -m32。Windows x64 使用 win64 與 Microsoft x64 呼叫慣例;macOS 使用 macho64 與 Mach-O 符號規則。物件檔無法跨這些格式攜帶。
手寫函式必須實作目標 ABI 的引數暫存器、堆疊對齊、回傳值、保留暫存器、展開中繼資料與符號命名。Linux syscall 序列遵循核心系統呼叫 ABI,而不是使用者空間函式 ABI。當需要 C 或 C++ 執行階段啟動、預設函式庫、位置獨立可執行檔政策或平台連結器選項時,請使用編譯器驅動程式連結。
NASM 的目前手冊定義指示詞與格式特定行為。作業系統 ABI 與處理器手冊定義組譯器之外的契約。
評論
在 GitHub 上閱讀或評論(發表評論需要登入)