兩類風格彙編語法對比

前言

  • 機器指令是用二進制代碼表示的CPU能直接識別和執行的一種指令系統的集合,不同的CPU架構有不同的機器指令。彙編指令是機器指令便於記憶的書寫格式,彙編指令編寫完成後通過彙編器將其翻譯成機器指令供CPU執行,因此,彙編器的功能是將彙編指令翻譯成機器指令。
  • 同一條機器指令,可以用不同的彙編指令表達,只要彙編器最終能正確無誤地翻譯就可以。不同的彙編指令格式衍生出不同的彙編語法,針對每種彙編語法都有一個與之對應的彙編器。沒有彙編器可以將所有類型的彙編語法都正確翻譯成機器指令。因此,隨着計算機的發展,不同廠家形成了自成一派的彙編語言,並有自己的彙編器。不同的彙編語言,實現相同的機器指令可能語法不一樣。
  • 常見的彙編器有GNU Assembler (GAS),Microsoft Macro Assembler (MASM),Netwide Assembler (NASM),Flat Assembler (FASM)等,不同彙編器有不同的彙編語言,GAS彙編器使用AT&T彙編語法,MASM使用Intel彙編語法,NASM使用的彙編語法和Intel彙編語法類似,但比Intel的簡單,每種彙編語法都有自己的手冊可以查閱。GASAT&T語法可以查詢GNU Tools ManualsNASM的語法可以查詢NASM官網
  • 本文對比的是AT&T彙編語法和NASM彙編語法,除此之外,還介紹了內聯彙編的基本語法

彙編(Assembly)

1.表達式

註釋

ATT:# 開頭
NASM:; 開頭

立即數

ATT: 十六進制 $0xff
NASM:十六進制 0ffh

寄存器引用

ATT:引用寄存器需要加%
NASM:引用寄存器什麼都不加

段寄存器引用

ATT:引用es:di中的值
%es:(%di)
NASM:引用es:di中的值
[es:di]

指定操作數長度

ATT:在指令後面加X字母
X可選值:
l - 32bit, w - 16bit, b - 8bit

NASM:在立即數或者其它不確定長度的內存地址前加單詞X
X可選值:
dword - 32bit, word - 16bit, byte - 8bit

標號定義

定義一個16bit數據
ATT:
wSectorNo:
	.word   0	
NASM:
	wSectorNo       dw  0

取標號處的值

將標號wSectorNo處的值讀取到ax中
ATT:
movw    wSectorNo, %ax	// 直接引用
NASM:
mov ax, [wSectorNo]

取標號處的地址

將標號2處的地址取出,保存到eax中
ATT:加'$'就是取地址
movl    $2f, %eax		//需要加$
2:
	xxxxxxx
NASM:標號本身就是地址
mov dword eax, .2
.2:
	xxxxxxx

2.數學指令

比較指令

比較標號bOdd處的1個字節是否爲1
ATT:
movb    bOdd, %al
cmpb    $1, %al

NASM:
cmp byte [bOdd], 1

邏輯右移

ax右移4bit
ATT:
shrw 	$4, %ax	
NASM:
shr 	ax, 4

3.數據傳輸

mov指令格式

ATT:
mov src,  dst
NASM:
mov dst,  src

4.執行流控制

相對跳轉

語法相同
jmp label

絕對跳轉

跳轉到9000:100地址處,BaseOfLoader=0x9000,OffsetOfLoader=0x100
ATT:
ljmp    $BaseOfLoader, $OffsetOfLoader
NASM:
jmp BaseOfLoader:OffsetOfLoader 

跳轉到標號處

ATT:
向前跳轉到標號1處,f表示forward
jmp 1f
1:
	xxxxxxx
向後跳轉到標號2處,b表示back
2:
	xxxxxxx
jmp 2b
NASM:
沒有前後之分
跳轉到標號.0處
.0:
    xxxxxxx
    jmp .0

長跳轉

ATT:
把SEG32_MODE32_CS裝入CS,0x12345裝入EIP,並跳轉到SEG32_MODE32_CS:$0x12345
ljmpl $SEG32_MODE32_CS, $0x12345
NASM:
把SelectorCode32裝入CS,把0裝入EIP,並跳轉到 SelectorCode32:0  處
jmp dword SelectorCode32:0

5.宏定義

多行宏

ATT:
.macro name arg1, arg2, arg3...
宏定義內容
.endm
舉例:段描述符
# 代碼段/數據段描述符
# usage: Descriptor Base, Limit, Attr
#        Base:  .long   4字節長度
#        Limit: .long   4字節長度
#        Attr:  .short  2字節長度
.macro  Descriptor Base=0x0, Limit=0xffffffff, Attr
    .short  \Limit & 0xffff
    .short  \Base & 0xffff
    .byte   (\Base >> 16) & 0xff
    .short  ((\Limit >> 8) & 0xf00) | (\Attr & 0x0f0ff)
    .byte   (\Base >> 24) & 0xff
.endm
宏的參數可以設置默認值
宏的內部引用參數時,使用'\'表示引用的參數

NASM:
%macro name nargs
宏定義內容
%endmacro
舉例:段描述符
; 描述符
; usage: Descriptor Base, Limit, Attr
;        Base:  dd
;        Limit: dd (low 20 bits available)
;        Attr:  dw (lower 4 bits of higher byte are always 0)
%macro Descriptor 3
    dw  %2 & 0FFFFh             ; 段界限1
    dw  %1 & 0FFFFh             ; 段基址1
    db  (%1 >> 16) & 0FFh           ; 段基址2
    dw  ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh) ; 屬性1 + 段界限2 + 屬性2
    db  (%1 >> 24) & 0FFh           ; 段基址3
%endmacro ; 共 8 字節
宏的內部引用參數時,使用%n表示引用的參數

內聯彙編(Inline Assembly in Linux C)

example

查看cpu信息

/* cpuid.c */
/* gcc -o cpuid cpuid.c */
#include <stdio.h>
#include <stdlib.h>

static inline void cpuid(unsigned int index,
                         unsigned int *eax,
                         unsigned int *ebx,
                         unsigned int *ecx,
                         unsigned int *edx)
{
    asm("cpuid"
        : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx)
        : "0" (index));
}

void main(void)
{
    unsigned int eax, ebx, ecx, edx;
    cpuid(0, &eax, &ebx, &ecx, &edx);
    printf("eax = %x\n", eax);
    printf("ebx = %x\n", ebx);
    printf("ecx = %x\n", ecx);
    printf("edx = %x\n", edx);
}

運行結果
在這裏插入圖片描述
結果分析參考intel手冊volume 2,chapter 3,3.2章節中對CPUID指令的輸出介紹

基本格式

 asm [ volatile ] (  
         assembler template
         [ : output operands ]                /* optional */
         [ : input operands  ]                /* optional */
         [ : list of clobbered registers ]    /* optional */
         );
  • asm
    gcc關鍵字,表示接下來要嵌入彙編代碼。爲避免keyword asm與程序中其它部分產生命名衝突,gcc還支持__asm__關鍵字,與asm的作用等價
  • volatile
    可選,表示不需要gcc對下面的彙編代碼做任何優化。同樣出於避免命名衝突的原因,__volatile__也是gcc支持的與volatile等效的關鍵字
  • output operands
    可選,指明輸出操作數,典型格式:
    "=a" (output)
    其中,"=a"指定output operand的應遵守的約束(constraint),output爲存放指令結果的變量,通常是個C語言變量。"="是輸出操作特有約束,表示操作數是隻寫的(write-only),表達式的意思是先將命令執行結果輸入到eax寄存器中,然後再由寄存器eax更新位於內存中的output
  • input operands
    可選,指明輸出操作數,典型格式:
    "constraint" (input)
    其中,constraint指定input operand的應遵守的約束(constraint),input爲存放輸入的變量,通常是個C語言變量。比如:
    asm("cpuid" : "=a" (*eax), "=b" (*ebx), "=c" (*ecx), "=d" (*edx) : "0" (index));
    表達式的 input operands是"0" (index),"0"是關聯約束,在有些情況下,輸入和輸入需要用到同一個寄存器,就可以指定以matching constraint方式分配寄存器,此時,input 和 output共用一箇中轉寄存器。代碼中指定輸入匹配第0個輸出,則用eax作爲中轉寄存器,那麼index作爲輸入,在cpuid指令運行前,需要將其值寫入eax中。

常用約束

  • TODO
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章