用 NASM 編寫代碼[轉(zhuǎn)載]
[點評:NASM使用方式與TASM/MASM相似,在Linux下面應用非常廣泛.在windows下面也有版本,強烈建議使用.]
本文主要為有匯編基礎, 而習慣了用 VC 的 inline ASM 寫代碼的朋友們而作.
為什么要使用 NASM?
使用 inline asm 固然方便, 但是卻不利于代碼的移植. 加上 VC 對新指令集(3D Now! ,XMM 等)的支持速度不夠, 使用起來很不方便, 所以我們往往采用外部匯編. 如果你以前熟悉 MASM 或是 TASM, 也不必更換, 否則云風推薦 NASM. NASM 及其文檔在
http://www.web-sites.co.uk/nasm 可以下載的到. 使用前最好通讀一遍文檔. 本文只會強調(diào)一些重點部分, 而補充一點遺漏, 而不會全盤復述. NASM 支持各種最新的指令集, 有相當強大的宏語言支持. 你將得到比 VC 的 inline asm 更為靈活的使用空間.
選一個好的編程環(huán)境
將 VC 當作 NASM 的編輯環(huán)境是完全可行的, 只需要在 Tools->Customize->Tools 下加入 NASM.exe 作為外部工具. 這里不詳細介紹. 云風推薦
Editplus 這款小巧實用的編輯器. 你可以指定各種關鍵字的顏色, 云風自己制作的語法文件在
這里, 寫的還不完備, 很多 nasm 的宏語句尚未加入, 另外還加了一些自己制作的宏. 所以請各位讀者酌情修改. 加入 Editplus 的方法是在 Tools-> Preferences->Files->Syntax 里增加一個語法文件. Editplus 支持 output 窗口信息捕獲, 在 Tools->Preferences->Tools->User Tools 里增加 NASM.exe, 并選 capture output 就可以了. 當然如果你想制作單獨的 win32/pe 文件, 而不是編譯成 obj 供 VC link, 那么還需要一個 link 程序. 最好再配合 makefile 使用.
如何編譯成可供 VC 連接的 obj
NASM 支持多種輸出格式, 雖然 VC 號稱是使用的 COFF 格式, 但實際上是有區(qū)別的. NASM 除了輸出 COFF 格式外, 另外也支持 VC 的目標文件格式, 叫做 win32. 我在 Editplus 的 user tools 里設置的是
-i I:\nasm\include\ -f win32 -o $(FileNameNoExt).obj $(FileName)
注: 這里的 -i 后是頭文件的路徑 將編譯出來的 obj 加入 VC 的 project 里就可以了. 一般來說, 目標文件有三個段, 分別是 text/data/bss 段. text 段放置代碼, 是只讀且可運行段
data 段放置靜態(tài)數(shù)據(jù), 這些數(shù)據(jù)會被放置入 exe 文件. 這個段是可讀寫, 但是不能運行的.
bss 段放置動態(tài)數(shù)據(jù), 這些數(shù)據(jù)不被放入 exe 文件, 在exe文件被加載入內(nèi)存后才分配的空間.
一個簡單的程序框架是這樣的:
[bits 32]
[section .text]
global _func
_func:
; 這里寫func 函數(shù)的代碼
[section .data]
[section .bss]
這里, 用 global 聲明了一個可被 C 調(diào)用的函數(shù) func (C 函數(shù)都有一個下劃線前綴) _func: 是這個函數(shù)的入口. 在對應的 C 代碼里想使用 func 這個函數(shù), 還需要用 extern 聲明 func 是外部函數(shù). 如果在 C++ 里使用則還需要將函數(shù)說明成 C 調(diào)用方式, 方法是用 extern "C" { } 說明
參數(shù)的傳入和處理
函數(shù)的參數(shù)處理和其調(diào)用方式有關, C 的缺省調(diào)用方式是 _cdel 調(diào)用方式, C++ 的非靜態(tài)成員函數(shù)采用的是 _thiscall, 關于各種調(diào)用方式的處理堆棧的方法, 在 MSDN 里可以查的到. 寫 inline asm 可能很少涉及這些, 但是這里卻必須搞清楚.
用純匯編寫 win32 程序等
寫 WIN32 下的 win32/pe EXE, 我建議使用 Borland 的 obj 格式, 這是因為導入 DLL 中的 API 比較方便. 但是需要注意一些要點:
NASM 缺省使用的segment 是 USE16 的, 而在 win32 下必須使用 USE32, 而且必須指定段的類型. 所以必須在源文件頭寫上這樣幾行:
[section .text class=code use32]
[section .data class=data use32]
[section .bss class=bss use32]
%define __SECT__
Borland 的 OBJ 文件里可以指定開始的標簽(..start), 一般可以寫成這樣:
[section .text]
..start
WinMain:
你的代碼將從 WinMain 這里開始運行.
和 C 不一樣, 你的主代碼沒有什么參數(shù)傳入, 所以你必須自己來得到必要的參數(shù). 比如:
push dword 0
call GetModuleHandle ; 獲得 hInstance
獲得 Instance 句柄(到 eax).
退出程序也不能直接用 ret, 因為你的程序是一個進程, 而不是一個函數(shù), 所以必須調(diào)用:
push dword 0 ; 退出碼
call ExitProcess
上面的 GetModuleHandle 是 kernel32.dll 里的 API: GetModuleHandleA, 所以需要在匯編源代碼中聲明導出:
import GetModuleHandle kernel32.dll GetModuleHandleA
extern GetModuleHandle
另外 ExitProcess 也是這樣:
import ExitProcess kernel32.dll
extern ExitProcess
import 的具體用法請參考 NASM 的文檔.
善于使用 NASM 強大的宏指令
這本是一個應該很豐富的 Section, 但是時間有限, 只舉一個簡單的例子, 定義下面這樣一個宏:
%imacro callapi 1-*
%define %%api %1
%rotate -1
%rep %0-1
%rotate -1
push dword %2
%endrep
call %%api
%endmacro
然后, 比如你需要調(diào)用 MessageBox, 就可以這樣寫
callapi MessageBox,[hWnd],lpText,lpCap,0
hWnd 是一個全局變量保存的窗口句柄, 當然也可以是 0
lpText 是文字的字符串指針
lpCap 是MessageBox 的標題字符串
另外我自己還做了許多諸如處理函數(shù)參數(shù), 保護堆棧等等方便使用的宏, 這里就不一一列出來了. 希望我引個頭, 能讓更多的朋友喜歡上 NASM