閱讀741 返回首頁    go 阿裏雲 go 技術社區[雲棲]


學習 nasm 語言

學習 nasm 語言

返 回

3. nasm assembly 語法

 

3.1 nasm 是區分大小寫

例如:符號 foo 與 FOO 是兩個不同的標識符。

 

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

  • byte : 
  • word : 16 位
  • dword  32 位
  • qword : 64 位
  • tword : 80 
  • 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

正如前麵所說的:dtdody 不接受整型數值常量,它們被使用在定義 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 八進製數表示方法

八進製的前綴可以是:0o 或 0q 後綴可以是:o 或 q

        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 浮點數常量

浮點數變量可以使用 DBDWDDDQDT 以及 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 所支持的算術運算符

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

 

上一節   目 錄   下一節

版權 mik 所有,轉載請注明出處!

 

最後更新:2017-04-04 07:03:16

  上一篇:go HDU1398 DP
  下一篇:go 【iPhone 編程】alloc, retain, release 和 copy 的概念