nasm 與 masm語法區別


看到一篇文章,是介紹nasm語法的:http://blog.csdn.net/hitop0609/article/details/4329454

masm是微軟專門為windows下匯編而寫的,而nasm可以在windows、linux等系統下匯編,故而個人推薦使用nasm。

3.1 nasm 是區分大小寫

例如:符號 fooFOO 是兩個不同的標識符。

3.2 內存操作數表達式

3.2.1 在 nasm 語法里,對 memory 操作數需要加 [ ] 括號

下面的代碼:

foo    equ    1
bar    dw     2

    bits 32

    mov eax, foo
    mov ebx, bar

第 2 指令的意圖是:將 bar 內 的值賦給 ebx 寄存器。但這樣是錯誤的,nasm 只會把 bar 當作是 immediate 賦給 ebx

00000000  0200                            ; bar 變量
00000002  B801000000        mov eax,0x1
00000007  BB00000000         mov ebx,0x0  ; 將 bar 地址作為 immediate 賦給 ebx 

因此,需要將 bar[ ] 括起來

mov eax, foo
mov ebx, [bar]

nasm 就編譯出正確的代碼:

00000000  0200                                    ; bar
00000002  B801000000        mov eax,0x1
00000007  8B1D00000000       mov ebx,[dword 0x0]    ; bar 的內容賦給 ebx

3.2.2 給 memory 操作數提供一個 displacement 值

下面代碼展示了 [base + disp] 的尋址方式:

section .bss

buffer  resb 10


    section .text

    bits 32

    mov byte [buffer + 0x01] , 'a'
    mov ebx, buffer
    movzx eax, byte [ebx + 0x01] 

3.2.3 指明 memory 操作數的 operand size

下面展示了為 memory 操作數提供一個 size 情況:

mov byte [buffer + 0x01] , 'a'
mov ebx, buffer
movzx eax, byte [ebx + 0x01]

代碼中使用 byte 關鍵字對 memory 操作數進行了修飾,指明 memory 操作數的大小為 byte

在這方面,nasm 的語法與微軟的 masm 的語法(Intel 語法)有些不同,masm 的語法是:

mov byte ptr [buffer + 0x01] , 'a'
mov ebx, buffer
movzx eax, byte ptr [ebx + 0x01]

在 masm 語法中需配合 ptr 指示字。

3.2.4 提供一個 segment

大多數 指令/內存操作數 缺省的 segment 是 DS ,x86/x64 允許為 memory 操作數提供另一個 segment 進行 segment override

在 nasm 語法中,如下:

mov byte [es: buffer + 0x01] , 'a'
mov ebx, buffer
movzx eax, byte [es: ebx + 0x01]

nasm 語法中,在 [ ] 括號內提供 segment ,不能在 [ ] 括號外提供 segment

而 masm 的語法中是在 [ ] 括號外提供 segment,如下:

    mov byte ptr es: [buffer + 0x01] , 'a'
    mov ebx, buffer
    movzx eax, byte ptr es: [ebx + 0x01]

3.2.5 指明 memory 操作數的 address size

有些情況下必須指明 memory 操作數的 address size ,否則編譯結果可能不是你想要的結果。下面的例子說明,如何為 memory 操作數指明 address size

例 1:

    section .bss

    buffer resb 10


    section .text

    bits 64

    mov rax, [qword buffer]        ; 指明 64 位的 address size(絕對地址)
    mov rax, [buffer]              ; 使用 32 位的 address size(絕對地址)

在 nasm 中,對於 絕對地址 形式,缺省是 32 位的,因此,需要明確使用 qword 來指明 64 位的 address size

這段代碼編譯后為:

00000000  48A11400000000000000   mov rax,[qword 0x14]    ; 64 位地址
0000000A  488B042514000000       mov rax,[0x14]          ; 32 位地址

它們的區別就是一個使用了 64 位地址,一個使用了 32 位地址。

例 2:

    section .bss

    buffer resb 10


    section .text

    bits 16

    mov byte [es:dword buffer + 0x01], 'a'          ; 指明為 32 位地址
    mov ebx, buffer
    movzx eax, byte [es:ebx+0x01]           

上面代碼是 16 位代碼,使用了 dword 指明 memory 操作數是 32 位的地址。

它被編譯為:

00000000  2667C6051D000000   mov byte [dword es:0x1d],0x61
         -61
00000009  66BB1C000000      mov ebx,0x1c
0000000F  2667660FB6830100  movzx eax,byte [es:ebx+0x1]
         -0000

16 位的 address size 被 override 為 32 位地址。

3.3 nasm 偽指令

偽指令不是 x86/x64 機器的真實指令,偽指令是用於給編譯器指示如何進行編譯。

3.3.1 nasm 定義的 7 種數據 size

  • byte8
  • word 16
  • dword 32
  • qword 64
  • tword80
  • oword 128
  • yword 256

oword 可以對應 Microsoft MASM 的 xmmword 類型,yword 對應 Microsoft MASM 的 ymmword 類型。

tword, oword 以及 yword 使用在 非整型 數據,使用在 float SSE 型數據。

3.3.2 定義初始化數據:db 家族

nasm 定義了用於初始化上面 7 種 size 的 db 家族,它們用於定義初化常量值。

  • db : define byte
  • dw :define word
  • dd :define doubleword
  • dq :define quadword
  • dt :define tword
  • do :define oword
  • dy :define yword

正如前面所說的:dt , do , dy 不接受整型數值常量,它們被使用在定義 float 或 SSE 數據常量。dt 可以定義 extended-precision float 數據,do 可以定義 quad-precision float,dy 可定義 ymm 數據。而 dq 可以定義 double-precision float 數據,dd 可以定義 single-precision float 數據。

下面是 NASM Manual 上的例子:

      db    0x55                ; just the byte 0x55
      db    0x55,0x56,0x57      ; three bytes in succession
      db    'a',0x55            ; character constants are OK
      db    'hello',13,10,'$'   ; so are string constants
      dw    0x1234              ; 0x34 0x12
      dw    'a'                 ; 0x61 0x00 (it's just a number)
      dw    'ab'                ; 0x61 0x62 (character constant)
      dw    'abc'               ; 0x61 0x62 0x63 0x00 (string)
      dd    0x12345678          ; 0x78 0x56 0x34 0x12
      dd    1.234567e20         ; floating-point constant
      dq    0x123456789abcdef0  ; eight byte constant
      dq    1.234567e20         ; double-precision float
      dt    1.234567e20         ; extended-precision float

3.3.3 定義非初始化數據:resb 家族

程序中使用到的非初始化數據通常放在 bss section 里,bss 代表 uninitialized storage space

nasm 使用了 resb (reserve byte) 家族來定義非初始化數據。

  • resb :reserve byte
  • resw :reserve word
  • resd :reserve doubword
  • resq :reserve quadword
  • rest :reserve tword
  • reso :reserve oword
  • resy :reserve yword

resb 相當於 Microsoft MASM 語法中的 db ?

下面是 NASM Manual 的例子:

buffer:         resb    64              ; reserve 64 bytes
wordvar:        resw    1               ; reserve a word
realarray       resq    10              ; array of ten reals
ymmval:         resy    1               ; one YMM register

3.3.4 包含 binary 文件

nasm 提供了一種包含 binary(二進制)文件的方法:使用 incbin 偽指令。incbin 偽指令包含的 binary 文件將直將寫入輸出文件中。此偽指令的作用是包含 graphics 以及 sound 這類數據文件。

    incbin  "file.dat"             ; include the whole file
    incbin  "file.dat",1024        ; skip the first 1024 bytes
    incbin  "file.dat",1024,512    ; skip the first 1024, and
                                   ; actually include at most 512

3.3.5 使用 equ 定義常量

equ 用來為標識符定義一個 整型 常量,它的作用類似 C 語言中的 #define

a  equ 0                          ; OK
b  equ 'abcd'                     ; OK! b = 0x64636261
c  equ 'abcdefghi'                ; warning! c = 0x6867666564636261
d  equ 1.2                        ; error!


    section .data

string db 'hello,word',0
len    equ $-string               ; OK! len = 0x0b

    section .text
textlen equ  _end - entry         ; OK! textlen = 0x05

_entry:
    mov ecx, textlen
   
_end: 

例子中: b 定義為常量 'abcd' 它將是字符串的 ASCII 碼序列,‘abcdefghi' 常量將會被截斷,整型常量最長為 quadword(8 bytes),而 d 企圖被定義為一個 float 常量,這產生會錯誤。len 和 textlen 被定義為編譯期確定的數值。

3.3.6 使用 times 重復寫數據或指令

times 是一個比較實用偽指令,用來重復定義數據或指令。

下面是一個經典的使用例子:

times 510-($-$$) db 0
 
 dw 0xaa55

這段代碼經常出現在 boot 磁盤 MBR 引導代碼中,目的是除了最后 2 個字節和 code 代碼外的區域全部寫 0 值。

times 還可以使用在重復寫某一條指令,如下:

times 10 nop

這段代碼結果是重復填入了 10 條 nop 指令:

00000000  90                nop
00000001  90                nop
00000002  90                nop
00000003  90                nop
00000004  90                nop
00000005  90                nop
00000006  90                nop
00000007  90                nop
00000008  90                nop
00000009  90                nop

3.4 常量值

nasm 下可以接受 4 種常量:整型常量字符常量字符串常量 以及浮點常量

3.4.1 整型常量

在 nasm 中,常用的整型進制有 4 種:

  • decimal :十進制數
  • hex    :十六進制
  • binary  :二進制數
  • octal  :八進制數

每一種進制都有前綴后綴 表示法。當數值無前綴和后綴時,它是十進制數。因為缺省是十進制。

3.4.1.1 十進制數表示方法

看一看,下面的例子:

        mov     ax,200          ; decimal
        mov     ax,0200         ; still decimal
        mov     ax,0200d        ; explicitly decimal
        mov     ax,0d200        ; also decimal 

十進制的前綴是:0d , 后綴是:d

3.4.1.2 十六進制數表示方法

十六進制使用 0123456789ABCDEF 來表示 16 個數值。類似地,它的前綴是:0h 0x (c/c++ 風格)以及 $0 (pascal 風格),后綴是:h

    mov ax, 0c8h         ; hex
    mov ax, 8h           ; hex

上面例子中的 hex 數,表明:當以 h 后綴結尾時,如果含有字母 ,必須要以 0 開頭。

 mov     ax,$0c8         ; hex again: the 0 is required 

上面是 pascal 風格的十六進制表示法,使用前綴 $0 (0 是必須的)

        mov     ax,0xc8         ; hex yet again
        mov     ax,0hc8         ; still hex 

上面是使用 0h 前綴和 C/C++ 風格的 0x 表示十六進制數。

3.4.1.3 八進制數表示方法

八進制的前綴可以是:0o0q 后綴可以是:oq

        mov     ax,310q         ; octal
        mov     ax,310o         ; octal again
        mov     ax,0o310        ; octal yet again
        mov     ax,0q310        ; octal yet again 

3.4.1.4 二進制數表示方法

類似地,二進制的前綴是:0b 后綴是:b

        mov     ax,11001000b    ; binary
        mov     ax,1100_1000b   ; same binary constant
        mov     ax,0b1100_1000  ; same binary constant yet again

3.4.2 字符常量

在 nasm 中,可以使用 3 種引號來提供字符

  • ' ...' (單引號)
  • " ..." (雙引號)
  • ` ...` (反引號)

如下示例,它們的結果是一樣的:

        db  'abcd'
        db  "abcd"
        db  `abcd`

3.4.2.1 提供字符常量

下面看看如何提供字符量:

          mov eax, 'a'                       ; eax = 0x61
          mov eax, 'abcd'                    ; eax = 0x64636261
          mov eax, 'abcdefghi'               ; eax = 0x64636261
          mov eax, `/x61/x62/x63/x64'        ; eax = 0x64636261   

第 3 條企圖賦超過 4 bytes 的字符常量給 eax, 編譯器會截斷為 4bytes 再賦給 eax, 而第 4 條是另一種字符常量表示法,使用轉義字符表示。

可見:字符常量是以 little-endian 排列

3.4.3 nasm 中的轉義字符

在 nasm 中使用 c 風格的轉義字符,在 / (反斜杠符)后面跟 轉義碼/ escape-code

轉義碼(escape-code)包括:字符轉義碼 , 八進制轉義碼 , 十六進制轉義碼

注意:nasm 中的轉義符必須要用 ` `(反引號)來引用

    db  `/x61`     ; right!  'a'
    db  '/x61'     ; wrong!  '/' , 'x' , '6', '1'

第 1 個用反引號來包含轉義符是正確的。而第 2 個用單引號來包含轉義符,nasm 卻視它為一般的字符串對待

3.4.3.1 字符轉義碼

下面是一些例子:

      /'          single quote (')
      /"          double quote (")
      /`          backquote (`)
      //          backslash (/)
      /?          question mark (?)
      /a          BEL (ASCII 7)
      /b          BS  (ASCII 8)
      /t          TAB (ASCII 9)
      /n          LF  (ASCII 10)
      /v          VT  (ASCII 11)
      /f          FF  (ASCII 12)
      /r          CR  (ASCII 13)
      /e          ESC (ASCII 27) 

3.4.3.2 八進制轉義碼

/ 后面最多可以跟着 3 位數字,構成八進制轉義碼,如下所示:

    /377        Up to 3 octal digits - literal byte
    /004        ; EOT
    /006        ; ACK
    /025        ; NAK
    /0          ; NULL

3.4.3.3 十六進制轉義碼

十六進制轉義碼以 x X 開頭,如下所示:

    db `/x61`                            ; 'a'
    db `/x61/x62/x63/x64`                 ; 'abcd'    

上面所示:十六進制轉義碼可以連串提供。

3.4.4 字符串常量

字符串是逐個逐個提供字符,看以下例子:

      db    'hello'               ; string constant
      db    'h','e','l','l','o'   ; equivalent character constants

提供字符串常量,是從左到右依次寫入到內存。

這里要注意的是:字符常量 與字符串常量 的區別 ,看一看下面的例子

buffer    db 'hello'                     ; 字符串常量
          ... ...

          mov eax, 'hello'               ; 字符常量       

字符常量與字符串常量最大區別就是:字符常以 littlen-endian 存儲,而字符串常量是從左到度

3.4.5 Unicode 字符串

nasm 定義了兩個操作數符來定義 Unicode 字符串:

  • __utf16__
  • __utf32__

下面是 nasm 里的例子:

%define u(x) __utf16__(x)
%define w(x) __utf32__(x)

      dw u('C:/WINDOWS'), 0       ; Pathname in UTF-16
      dd w(`A + B = /u206a`), 0   ; String in UTF-32

3.4.6 浮點數常量

浮點數變量 可以使用 DB , DW , DD , DQ , DT 以及 DO浮點數常量 使用 __float8__ , __float16__ , __float32__ , __float64__ , __float80m__ , __float80e__ , __float128l__ 以及 __float128h__ 來定義。

下面是 nasm 提供的例子:

      db    -0.2                    ; "Quarter precision"
      dw    -0.5                    ; IEEE 754r/SSE5 half precision
      dd    1.2                     ; an easy one
      dd    1.222_222_222           ; underscores are permitted
      dd    0x1p+2                  ; 1.0x2^2 = 4.0
      dq    0x1p+32                 ; 1.0x2^32 = 4 294 967 296.0
      dq    1.e10                   ; 10 000 000 000.0
      dq    1.e+10                  ; synonymous with 1.e10
      dq    1.e-10                  ; 0.000 000 000 1
      dt    3.141592653589793238462 ; pi
      do    1.e+4000                ; IEEE 754r quad precision

3.5 表達式

在 nasm 里的表達式很像 C 表達式,對於熟悉 C 表達式的人來說幾乎可以馬上上手。

3.5.1 $ 與 $$ 標號

$ 標號表示 nasm 編譯后當前指令位置

$$ 標號表示當前 section 起始位置

看看下面的例子:

bits 64

section .rdata
    dq 0
   
   
section .text
   
    mov rax, 0
    mov rax, $-$$

它的編譯結果是:

00000000  48B8000000000000  mov rax,0x0
         -0000
0000000A   48B80A0000000000  mov rax,0xa
         -0000
00000014  0000              add [rax],al
00000016  0000              add [rax],al
00000018  0000              add [rax],al
0000001A  0000              add [rax],al

3.5.2 位運算符

與 C 一樣,包括下面:

位運算符
描述
&
位與: AND
|
位或:OR
~
位非: NOT
^
位異或: XOR
<<
左移
>>
右移

 

3.5.3 算術運算符

下面是 nasm 所支持的算術運算符

算術運算符
描述
+
-
*
/
除(無符號數)
//
除(符號數)
%
取模(無符號數)
%%
取模(符號數)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM