1. 程式人生 > >asm基礎——nasm使用簡介

asm基礎——nasm使用簡介

編譯命令:

nasm −f <format> <filename> [−o <output>]

引數介紹:

-f:用來指定編譯出來的.o檔案的格式。下面是nasm支援的格式,可以通過nasm -hf來檢視:


要檢視本機支援的格式,可以先進入nasm所在的目錄,並執行file nasm命令來檢視:


結合兩張圖,可以確定本機編譯時需要指定的格式是macho64。

-o:用來指定編譯後的檔案的名稱。如果不加引數,則使用原來的.asm的檔名,字尾則根據-f指定的檔案格式有所不同,windows系統是.obj,unix系統是.o。

-l:編譯時生成list檔案,裡面包含程式碼和對應的機器碼等內容。

-M:列印編譯時的依賴檔案,它有各種不同形式的-Mx,這裡不多做介紹,光是-M的話會直接打印出來。

-Ox:優化程式碼,x表示優化的級別,0表示不優化,1-n優化等級依次提高。

-d:定義一個巨集,比如:

nasm myfile.asm −dFOO=100
-u:取消巨集定義,比如:
nasm myfile.asm −dFOO=100 -uFOO

-E:不進行編譯,只是展開所有的巨集,如果沒有接引數來指定檔案,則展開後的程式碼直接打印出來,用-o可以指定展開後的檔案。

其它還有很多的引數,不一一介紹了,可以參考nasmdoc.pdf文件。

nasm中的分段

nasa中使用section關鍵字來分段,後面接的引數有:

1).data,用來定義常量;

2).bbs,用來存放變數;

3).text,用來存放程式碼;

另外,nasm通過global來指定入口。

nasm中的有效地址:

在nasm中,有效地址都需要用[]括起來以獲取其中的內容,下面是一個例子:

var dw 0x55AA
mov ax, [qword var]
對於64位的編譯系統,這裡必須要加上qword,否則編譯會報錯。在nasmdoc.pdf有如下的解釋:

The only instructions which take a full 64−bitdisplacementis loading or storing, using

MOV,AL,AX,EAXorRAX(but no other registers) to an absolute 64−bit address. Since this is a relatively rarely used instruction(64−bit code generally uses relative addressing), the programmer has to explicitly declare the displacement size asQWORD

這裡除了qword,也可以有byte、word等,表示的實際上是一種偏移,比如[byte eax],就是指大小為0的byte偏移。

nasm中的偽指令:

1)宣告已初始化的變數:

db 0x55
dw 0x55AA
dd 0x55AAAA55
2)宣告未初始化的變數:
buffer1: resb 64
buffer2: resw 32
buffer3: resq 16
使用nasm -f bin的格式編譯,得到的二進位制如下:


3)包含二進位制檔案的偽指令:incbin;

4)定義常量:equ:

message    db    'hello, world'
msglen     equ   $−message
5)重複執行指令或者宣告資料:times:

例如下面的程式碼:

times 16 db 0x5A
得到的結果如下:



常用預處理指令:

所有預處理指令都以%開頭。

1)單行巨集定義指令%define:

%define ctrl    0x1F &
%define param(a,b) ((a)+(a)*(b))

mov byte [param(2,ebx)], ctrl 'D'

使用%define會遇到一些問題,比如下面的例子:

%define TRUE 1
%define FALSE TRUE
%define TRUE 0

val1: db FALSE

%define TRUE 1
val2: db FALSE
使用nasm -f bin的格式進行編譯,得到的結果用vim -b開啟,並通過:%!xxd -g 1來檢視,得到的結果如下:


產生這結果的原因是nasm中單行的巨集只有在使用的時候才會展開,對於val1,FALSE的值等於TRUE,而此時TRUE的值是0,所以得到的值也是0;同樣的就得到了val2的值是1。

如果需要在單行巨集定義的時候就展開巨集,可以使用%xdefine這個偽指令,這裡的x就表示expend,“展開”。

同樣是上面的例子,使用%xdefine代替%define,得到的結果就是


使用%xdefine可以立即展開後面接的程式碼中的巨集。

還有一種方法可以得到相同的結果,就是使用%[xxx],它顯式地用來展開巨集。

%define TRUE 1
%define FALSE %[TRUE]   ; 注意這裡
%define TRUE 0

val1: db FALSE

%define TRUE 1
val2: db FALSE
它跟使用上例使用%xdefine產生相同的結果。

無論是%define還是%xdefine都有一個加i的版本:%idefine和%ixdefine,這裡的i表示case insensitive,不區分大小寫。

2)用於連線巨集字串和引數的%+,下面是一個例子:

%define BDASTART 400h
%define BDA(x) BDASTART + tBIOSDA. %+ x

struc tBIOSDA
   .COM1addr resw 1
   .COM2addr resw 1
endstruc

mov ax, BDASTART + tBIOSDA.COM1addr
mov bx, BDA(COM1addr)
兩句mov指令傳遞的資料是一致的,後者使用了巨集定義,看起來更清晰,這就是因為使用了%+的緣故。下面是是巨集展開的結果:


需要注意一點,這裡的%+之後有一個空格。

3)表示巨集名字的%?和%??。

4)%undef,用來取消巨集。

5)%assign與%define相似,用來處理單行的巨集,但要求巨集不帶引數,值是數值。

6)對於多行的巨集,使用%macro,下面是一個例子:

%macro prologue
  push rax
  push rbx
%endmacro

section .text
  prologue
巨集展開後的結果:


從結果看展開是沒有問題的,但是有一個報錯,這是因為nasm中使用%macro時需要指定引數個數,上例中沒有引數,那麼就是0,正確的程式碼應該是這樣的:

%macro prologue 0
	push rax
	push rbx
%endmacro

section .text
	prologue
當引數個數不為0,則在巨集內部使用%1、%2等來訪問引數,下面是一個例子:
%macro prologue 2
	push %1
	push %2
%endmacro

section .text
	prologue rax, rbx

兩個prologue相當於是一個過載。預設的指令似乎也可以過載,但是最好不要。

上例中一是要注意%1%2等使用,另外還需要注意rax,rbx作為引數的傳遞,兩個引數之間使用逗號分隔。但是存在一種情況是單個引數之間本身就包含逗號,這個時候就可以通過用{}將引數包圍起來的方法,下面是一個例子:
%macro silly 2
	%2: db %1
%endmacro

section .data
	silly {0xd, 0xa}, clrf
這裡定義了一個回車,第一個引數包含兩個值。

上面的程式碼還有另一種寫法,這種寫法使用了"Greedy Parameters",它類似於c語言中的不定引數,修改上面的程式碼:

%macro silly 2+
	%1: db %2
%endmacro

section .data
	silly clrf, 0xd, 0xa
這裡的2+就是"Greedy Parameters"的宣告方式。上例還需要了一下引數的位置,因為"Greedy Parameters"需要放在最後。

在多行巨集裡面可以新增標籤,且這個標籤只在當前的巨集中有效,因此多次呼叫該巨集不會因為標籤問題受到影響。

使用%%xx來定義巨集中的標籤,xx是標籤名,下面是一個例子:

%macro retz 0
		jnz %%skip
		ret 
	%%skip:
%endmacro

section .text
	retz
	retz
展開後就可以看出為什麼標籤不會衝突了:


nasm中的巨集還可以指定預設引數,格式如下:

%macro prologue 0-1 rax
	push %1
%endmacro

section .text
	prologue
上例中的0-1表示巨集可以有0個或者1個引數,如果是0個,則使用後接的預設的rax。

上例還比較簡單,下面可能稍微複雜一點:

%macro prologue 1-3 rax, rbx
上面的程式碼表示,prologue必須要帶至少一個引數,最多3個引數,如果第2、3個引數不存在,則分別由rax和rbx代替。

還有幾個特殊的引數可以在多行巨集中使用:

a. %0表示引數的個數;

b. %rotate類似shell指令碼中的shift,用來遍歷引數;

最後,%unmacro用來取消多行巨集定義。

7)nasm中可以使用的條件判斷:

%if<condition>
         ; some code which only appears if <condition> is met
%elif<condition2>
         ; only appears if <condition> is not met but <condition2> is
%else
         ; this appears if neither <condition> nor <condition2> was met
%endif