作者:余家興
NASM是個可以在多個OS下組譯Intel IA-32組合語言的組譯器(assembler), 功能強大,本文件僅提供簡單的使用方法, 詳細的使用方法請見參考文件中的使用手冊。
如同在MS-DOS下使用MASM一般, 在Linux下組譯組合語言的流程是先用 組譯器將組合語言檔組譯成目的檔 (在MS-DOS下檔名習慣用.obj而在Linux/UNIX下習慣用.o)。 然後再用連結器(linker)將目的檔連結成可執行檔。
使用NASM組譯檔案的時候,我們必須指定欲組譯的組合語言檔, 欲產生目的檔的檔名和目的檔的格式。 在Linux作業系統上,我們使用的的目的檔格式為 ELF (Executable and Linkable Format)。 所以如果我們的compiler產生了一個檔名叫test.x.asm的組合語言檔, 而我們要使用NASM來將其組譯為可執行檔test.x.bin, 則我們首先在compiler中呼叫 nasm -f elf -o test.x.o test.x.asm 。 這一步可以產生目的檔test.x.o。
接下來第二步是要把產生的目的檔和C的函式庫(libc)連結以產生可執行檔, 我們使用gcc作為我們的連結器以簡化使用方法。 在compiler中呼叫 gcc -o test.x.bin test.x.o 。 如果一切順利,我們就得到了我們要的可執行檔了,很簡單吧。
底下是一個符合NASM語法的簡易組合語言檔範例(不含行號):
1: ;; 2: ;; 在NASM的語法中, 分號後面的部份為註解 3: ;; 4: GLOBAL main ; 宣告 main 為全域符號(symbol) 5: EXTERN printf,scanf ; 宣告 printf 和 scanf 為外部符號 6: 7: SECTION .data ; 從以下開始為data節區 8: pfmt DB '%d', 0x0A, 0 9: sfmt DB '%d', 0 10: 11: SECTION .bss ; 從以下開始為bss節區 12: buf RESD 1 13: 14: SECTION .text ; 從以下開始為text節區 15: main: ; 主函式 16: push ebp 17: mov ebp, esp 18: push buf 19: push sfmt 20: call scanf ; 呼叫 scanf 函式 21: add esp, 8 ; 清除堆疊中的參數 22: mov eax, DWORD [buf] 23: push eax 24: push pfmt 25: call printf ; 呼叫 printf 函式 26: add esp, 8 27: mov eax, 0 28: leave 29: ret ; 主函式結束
目的檔在和外部函式庫連結的時候必須告訴連結器本身有哪些符號是可以給別人使用, 以及本身要使用別人的哪些符號。符號包含函式名稱與變數名稱。 我們compiler產生的組合語言必須要告訴連結器這個檔案裡有main這個函式(第4行), 如此程式才會從這邊的main開始執行。 而我們的程式中有用到libc中的printf和scanf這兩個函式, 所以我們要將其宣告為外部符號(第5行)。
一個程式包含了一個或多個節區,上面的範例中有三個節區: data、bss和text。 data節區裡頭擺的是有初始值的變數,bss節區裡頭擺的是沒有初始值的變數, 而text節區裡頭擺的是程式碼。 範例中的第7,11,14行為宣告節區。
有初始值的變數擺在data節區,使用DB、DW或DD來宣告。使用方法同MASM。 範例中宣告了兩個變數pfmt和sfmt,分別用來傳給printf和scanf當作第一個參數。
沒有初始值的變數擺在bss節區,使用RESB、RESW或RESD來宣告。 後面接的數字表示你要保留幾個Byte/Word/DWord給這個變數。 範例中保留了一個DWord(32 bits)給buf。
其它重要資訊請參考NASM使用手冊的 Section 2.2。
在C語言裡頭可以使用 system("command line...") 來呼叫shell執行
所指定的內容。函式宣告在 stdlib.h 裡。
在C語言的規範中,參數的傳遞由最後一個開始,依相反順序放到堆疊中。 呼叫完後將堆疊清除。 函式的傳回值則放在暫存器eax裡。