跳轉到內容

x86 彙編/FASM 語法

來自華夏公益教科書

FASM,也稱為Flat Assembler,是針對 x86 架構的最佳化彙編器。FASM 用匯編語言編寫,因此它可以自彙編/自引導。它執行在各種作業系統上,包括 DOS、Linux、Unix 和 Windows。它支援 x86 和 x86-64 指令集,包括 SIMD 擴充套件 MMX、SSE - SSE4 和 AVX。

十六進位制數字

[編輯 | 編輯原始碼]

FASM 支援所有流行的用於定義十六進位制數字的語法

0xbadf00d ; C-Like Syntax
$badf00d  ; Pascal-Like Syntax
0badf00dh  ; h Syntax, requires leading zero to be valid at assembly time

FASM 支援幾種獨特的標籤功能。

匿名標籤

[編輯 | 編輯原始碼]

FASM 支援不使用識別符號或標籤名稱的標籤。

  • @@: 代表一個匿名標籤。可以定義任意數量的匿名標籤。
  • @b 指向在原始碼中向後查詢時遇到的最近的 @@。@r 和 @b 等效。
  • @f 指向在原始碼中向前查詢時遇到的最近的 @@。
@@:
    inc eax
    push eax
    jmp @b     ; This will result in a stack fault sooner or later
    jmp @f     ; This instruction will never be hit
@@:            ; if jmp @f was ever hit, the instruction pointer would be set to this anonymous label
    invoke ExitProcess, 0 ; Winasm only

區域性標籤

[編輯 | 編輯原始碼]

區域性標籤,以 . (句點)開頭。您可以在其全域性標籤父級的上下文中引用區域性標籤。

entry globallabel

globallabel:
    .locallabelone:
        jmp globallabel2.locallabelone
    .locallabeltwo:
 
globallabel2:
    .locallabelone:
    .locallabeltwo:
        jmp globallabel.locallabelone ; infinite loop

運算子

[編輯 | 編輯原始碼]

FASM 支援幾個獨特的運算子來簡化彙編程式碼。

$ 運算子

[編輯 | 編輯原始碼]

$ 描述了定址空間中的當前位置。它用於確定程式碼塊或資料塊的大小。 $ 在 MASM 中的等效項是 SIZEOF 運算子。

mystring db "This is my string", 0
mystring.length = $ - mystring

# 運算子

[編輯 | 編輯原始碼]

# 是符號連線運算子,用於將多個符號組合成一個。它只能在宏(如 rept 或自定義/使用者定義的宏)的主體內部使用,因為它會將宏引數的名稱替換為其值。

macro contrived value {
    some#value db 22
}
; ...
contrived 2

; assembles to...
some2 db 22

` 運算子

[編輯 | 編輯原始碼]

` 用於獲取傳遞給宏的符號的名稱,將其轉換為字串。

macro print_contrived value {
    formatter db "%s\n"
    invoke printf, formatter, `value
}
; ...
print_contrived SOMEVALUE

; assembles to...
formatter db "%s\n"
invoke printf, formatter, "SOMEVALUE"

內建宏

[編輯 | 編輯原始碼]

FASM 有一些有用的內建宏,可以簡化彙編程式碼的編寫。

rept 指令用於將重複的彙編指令壓縮成一個塊。該指令以 rept 開頭,然後是一個數字或變數,指定緊隨其後的花括號內的彙編指令應重複的次數。計數器變數可以別名為符號,或用作 rept 塊內指令的一部分。

rept 2 {
    db "Hello World!", 0Ah, 0
}

; assembles to...
db "Hello World!", 0Ah, 0
db "Hello World!", 0Ah, 0

; and...
rept 2 helloNumber {
    hello#helloNumber db "Hello World!", 0Ah, 0 ; use the symbol concatenation operator '#' to create unique labels hello1 and hello2
}

; assembles to...
hello1 db "Hello World!", 0Ah, 0
hello2 db "Hello World!", 0Ah, 0

結構體

[編輯 | 編輯原始碼]

struc 指令允許將資料彙編成類似於具有成員的 C 結構體的格式。 struc 的定義使用區域性標籤來定義成員值。

struc 3dpoint x, y, z
{
    .x db x,
    .y db y,
    .z db z
}

some 3dpoint 1, 2, 3

; assembles to...
some:
    .x db 1
    .y db 2
    .z db 3

; access a member through some.x, some.y, or some.z for x, y, and z respectively

自定義宏

[編輯 | 編輯原始碼]

FASM 支援定義自定義宏,作為將多個指令或條件彙編組合成一個較大指令的方法。它們需要一個名稱,並且可以有一個可選的引數列表,用逗號隔開。

macro name arg1, arg2, ... {
   ; <macro body>
}

可變引數

[編輯 | 編輯原始碼]

宏可以透過方括號語法支援可變數量的引數。

macro name arg1, arg2, [varargs] {
   ; <macro body>
}

必需運算元

[編輯 | 編輯原始碼]

FASM 宏語法可以使用每個運算元後的 * 運算子來要求宏定義中的運算元。

; all operands required, will not assemble without
macro mov op1*, op2*, op3*
{
    mov op1, op2
    mov op2, op3
}

運算子過載

[編輯 | 編輯原始碼]

FASM 宏語法允許過載指令的語法,或者建立新的指令。下面,mov 指令被過載以支援第三個運算元。如果未提供第三個運算元,則會組裝常規的移動指令。否則,op2 中的資料將移動到 op1op2 將被替換為 op3

; not all operands required, though if op1 or op2 are not supplied
; assembly should fail
; could also be defined as 'macro mov op1*, op2*, op3' to force requirement of the first two arguments
macro mov op1, op2, op3
{
    if op3 eq
        mov op1, op2
    else
        mov op1, op2
        mov op2, op3
    end if
}

你好,世界

[編輯 | 編輯原始碼]

這是一個完整的 Win32 彙編程式示例,它將“Hello World!”列印到控制檯,然後等待使用者按任意鍵退出應用程式。

format PE console                            ; Win32 portable executable console format
entry _start                                 ; _start is the program's entry point

include 'win32a.inc'                         

section '.data' data readable writable       ; data definitions

hello db "Hello World!", 0
stringformat db "%s", 0ah, 0

section '.code' code readable executable     ; code

_start:
        invoke printf, stringformat, hello   ; call printf, defined in msvcrt.dll
        invoke getchar                       ; wait for any key
        invoke ExitProcess, 0                ; exit the process

section '.imports' import data readable      ; data imports

library kernel, 'kernel32.dll',\             ; link to kernel32.dll, msvcrt.dll
        msvcrt, 'msvcrt.dll'

import kernel, \                             ; import ExitProcess from kernel32.dll
       ExitProcess, 'ExitProcess'

import msvcrt, \                             ; import printf and getchar from msvcrt.dll
       printf, 'printf',\
       getchar, '_fgetchar'

這是一個 x86_64 GNU+Linux 的示例。

format ELF64 executable 3                 ;; ELF64 Format for GNU+Linux
segment readable executable               ;; Executable code section

;; Some definitions for readabilty purposes

define SYS_exit     60
define SYS_write    1

define stdout       1
define exit_success 0

_start:                                   ;; Entry point for our program
    mov eax, SYS_write                    ;; SYS_write(               // Call the write(2) syscall
    mov edi, stdout                       ;;     STDOUT_FILENO,       // Write to stdout
    mov esi, hello_world                  ;;     hello_world,         // Buffer to write to STDOUT_FILENO: hello_world
    mov edx, hello_world_length           ;;     hello_world_length,  // Buffer length
    syscall                               ;; );

    mov eax, SYS_exit                     ;; SYS_exit(                // Call the exit exit(2) syscall
    mov edi, exit_success                 ;;     EXIT_SUCCESS,        // Exit with success exit code, required if we don't want a segfault
    syscall                               ;; );

segment readable                          ;; Read-only constant data section
    hello_world: db "Hello world", 10     ;; const char *hello_world = "Hello world\n";
    hello_world_length = $ - hello_world  ;; const size_t hello_world_length = strlen(hello_world);
[編輯 | 編輯原始碼]
華夏公益教科書