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