匯編:
x86指令集 CISC: RISC
向下兼容
變長指令
1-15字節,多為2-3字節長度
多種尋址方式
MIPS 32位
Load/Store 能訪問內存
指令格式只有3類
R(register) 從寄存器堆中讀取兩個源操作數,計算結果寫回寄存器堆
I(immediate) 使用一個16位的立即數作為源操作數
J(jump), 使用一個26位立即煩亂作為跳轉的目標地址
地址: 32位, 0000 --> 0004 ,差32位,4個字節
64位, 0000 --> 0008
Byte Ordering
Big Endian: Sun, PowerPC, Internet
低位字節(Least singificant byte,LSB)占據高地址
Little Endian: x86
低對低,高對高
0x123456
0x100 0x0101 0x102
56 34 12
帶符號數
最大 TMax : 0111...1 2^w -1
TMin : 100...0 -2^w 001
按位取反加1(符號位不動) 1*(-2^w)+ 101 -4+1 011 -3
何時采用無符號數
模運算
按位運算
sizeof 返回值是unsigned
101
2^6 2^5 2^2
x+y = -(x+y)
-x-1
浮點數:
float: 23位有效,
局限性: 只能精確表示x/2^k這類形式的數據
數字形式:
(-1)^s M 2^E
符號: s
尾數: M
階碼: E
編碼:
s exp frac
E M
單數度: exp: 8bits fract: 23
雙 11bits 52
規格化的浮點數 (Normalized)
exp != 0000...0 111...1
E = Exp -Bias Exp(exp域所表示的無符號數值)
Bias偏置量
單精度: 127
雙 1023
Float F = 15213.0
15213 = 11101101101101 = 1.1101101101101 * 2^13
M = 1.1101101101101
frac = 1101101101101000000000 23位
E = 13
Bias = 127
Exp = 140 10001100
Hex: 4 6 6 D B 4 0 0
Binary: 0100 0110 0110 1101 1011 0100 0000 0000
140: 100 0110 0
15213: 0110 1101 1011 01
value binary Rounded Action
2 3/32 10.00011 10.00 down .5 100
2 3/16 10.00110 10.01 up
2 7/8 10.11100 11.00
2 5/8 10.10100 10.10
向偶數舍入(Round-To-Even)
保護模式 (段模式)
32位微處理器
8個通用寄存器
%eax : %ah %al
%edx
%ecx
%ebx
%esi
%edi
%esp
%ebp
指令寄存器擴展為32位, EIP
6個段寄存器 (CS DS SS ES FS GS)
段寄存器長度均為16位,其中13位代表內存段的一個編號,稱為“段選擇器”
ESP:棧指針寄存器(extended stack pointer),
其內存放着一個指針,該指針永遠指向系統棧最上面一個棧幀的棧頂。
EBP:基址指針寄存器(extended base pointer),
其內存放着一個指針,該指針永遠指向系統棧最上面一個棧幀的底部。
---- CPU ---------------
P Registers |
C Condition Codes |
-------------------------
指令寄存器 (PC Process Counter)
下一條指令的地址
EIP(x86-32) RIP(x86-64)
gcc -O2 -S add.c -m32 -fno-omit-frame-pointer
匯編語言數據格式
C聲明 Intel數據類型 匯編代碼后綴 大小
char 字節 b 1
short 字 w 2
int 雙字 l 4
long int 雙字 l 4
long long int --
char* 雙字 l 4
float 單精度 s 4
double 雙精度 l 8
long double 擴展精度 t 10/12
Source Dest
Reg movl $0x4,%eax temp = 0x4;
Imm Mem movl $-147,(%eax) *p = -147;
movl Reg Reg movl %eax,%edx temp2=temp1;
Mem movl %eax,(%edx) *p = temp;
Mem Reg movl (%eax),%edx temp = *p;
間接尋址 (R) Mem[Reg[R]]
movl (%ecx),%eax
基址+偏移量 D(R) Mem[Reg[R]+D]
movl 8(%ebp),%edx
變址尋址:
D(Rb,Ri,S) Mem[Reg[Rb]+S*reg[Ri]+D]
D:常量(地址偏移量)
Rb:基址寄存器:8個通用寄存器之一
Ri: 索引寄存器: %esp不作為索引寄存器
一般%ebp也不用做這個用途
S: 比例因子1,2,4,8
0x8(%edx) 0xf000 + 0x8 0xf008
(%edx,%ecx) 0xf000 + 0x100 0xf100
(%edx,%ecx,4) 0xf000+4*0x100 0xf400
0x80(,%edx,2) 2*0xf000+0x80 0x1e080
尋址模式實例:
movb
movw
movl
movs D <---符號擴展(S)
movsbw
movsbl
movswl
MOVZ S,D D <---零擴展(S)
movzbw
movzbl
movzwl
pushl
popl
地址計算指令:
leal Src,Dest
leal (%edx,%eax),%ecx # ecx = x+y
leal (%edx,%edx,2),%edx # edx = 3*y
雙操作數指令:
addl Src,Dest Dest = Dest + Src
subl Src,Dest Dest = Dest - Src
imull Src,Dest Dest = Dest * Src
sall Src,Dest Dest = Dest << Src 與shll等價
sarl Src,Dest Dest = Dest >> Src 算術右移
shrl Src,Dest Dest = Dest >> Src 邏輯右移
xorl Src,Dest Dest = Dest ^ Src
andl Src,Dest Dest = Dest & Src
orl Src,Dest Dest = Dest | Src
單操作數指令
incl Dest Dest = Dest + 1
decl Dest Dest = Dest -1
negl Dest Dest = - Dest
notl Dest Dest = ~ Dest
指針在32位是4bytes, 64位8bytes
-masm=intel Intel/Microsoft Format格式的匯編
64位:
16個通用寄存器
%rax %eax %r8 %r8d
%rdx %edx %r9 %r9d
%rcx %ecx %r10 %r10d
%rbx %ebx %r11 %r11d
%rsi %esi %r12 %r12d
%rdi %edi %r13 %r13d
%rsp %esp %r14 %r14d
%rbp %ebp %r15 %r15d
參數小於7個時
rdi,rsi,rdx,rcx,r8,r9, 多於7個的放於棧中
條件碼:
CF Carry Flag,進位
用於檢測無符號整數運算的溢出
ZF Zero : set if t == 0 set 1
SF Sign : set if t < 0
OF Overflow : set if 補碼運算溢出(即帶符號整數運算)
(a>0 && b>0 && t<0)
|| (a<0 && b<0 && t>0)
比較指令
cmpl Src2,Src1
ZF set if a==b
SF set if(a-b)<0
OF set if (a>0&&b<0 && (a-b)<0) ||
測試指令 and
ZF set when a&b == 0
SF set when a&b < 0
SetX指令
讀取當前的條件碼(或者某些條件碼的組合),並存入目的字節寄存器
SetX Condition Description
sete ZF Equal/Zero
setne ~ZF Not Equal/Not Zero
sets SF Negative
setns ~SF
setg ~(SF^OF)&~ZF Greter(Signed)
setge ~(SF^OF) Greater of Equal(Signed)
setl (SF^OF) Less(Signed)
setle (SF^OF)|ZF Less or Equal(Signed)
seta ~CF&~ZF Above(unsigned)
setb CF Below(unsigned)
32位: (movzbl 指令對目的寄存器進行"0"擴展)
int gt(int x,int y)
{
return x>y;
}
movl 12(%ebp),%eax # eax = y
cmpl %eax,8(%ebp) # compare x:y
setg %al # al = x>y
movzbl %al,%eax # zero rest of %eax
64位
int gt(long x,long y) long lgt(long x, long y)
{ {
return x>y; return x>y;
} {
assem:
xorl %eax,%eax # eax = 0
cmpg %rsi,%rdi # Compare x:y
setg %al # al = x>y
跳轉指令:
jX Condition Description
jmp 1 Unconditional
je ZF Equal/Zero
jne
js
jns
jg
jge
jl
jle
ja
jb
64:
cmovC stc,dest
如果條件C成立,將數據從src傳送至dest
-march=i686
int fact_goto(int x)
{
int result = 1;
loop:
result *= x;
x = x-1;
if(x>1)
goto loop;
return result;
}
fact_goto:
pushl %ebp
movl $1,%eax
movl 8(%ebp),%edx
L11:
imul %edx,%eax
decl %edx
cmpl $1,%edx
jg L11
jump-to-middle
條件跳轉指令會引起性能損失,Branck Prediction技術被引入來進行優化
switch :
表結構:
每個表項(及跳轉地址占4個字節)
基地址是.L62
jmp .L61
jmp *.L62(,%edx,4) *表明這是一個間接跳轉,即目標地址存於內存地址中,數即是地址
.section .rodata
.allign 4
.L62:
.long .L61 # x=0
.long .L56 # x=1
.long .L57 # x=2
.long .L58 # x=3
.long .L61 # x=4
.long .L59 # x=5
.long .L60 # x=6
12356, 0,4沒有就跳到default
64:
.L62
.quad .L55 # x=0
64位寄存器使用慣例:
%rax (Return Value) %r8 Argument #5
%rdx Callee Saved %r9 Argument #6
%rcx Argument #4 %r10 Callee Saved
%rbx Argument #3 %r11 Callee Saved
%rsi Argument #2 %r12 Callee Saved
%rdi Argument #1 %r13 Callee Saved
%rsp %esp %r14 Callee Saved
%rbp Callee Saved %r15 Callee Saved
壓棧操作:
pushl Src
%esp = %esp -4
出棧操作:
popl Dest
%esp = %esp + 4
過程調用指令:
call label 將返回地址壓入棧,跳轉至label
過程返回指令:
ret 跳轉至棧頂的返回地址
64位:
一次性分配整個棧幀
將%rsp減去某個值(棧幀的大小) 128宏區
對於棧幀內容的訪問都是基於%rsp完成的
可以延遲分配
釋放簡單
數組訪問:
x86-32:
# %edx = z 數組起始地址
# %eax = dig 下標
movl (%edx,%eax,4),%eax # z[dig]
二維數組:
pgh[index][dig]
pgh + 20*index + 4*dig
# %ecx = dig
# %eax = index
leal 0(,%ecx,4),%edx # 4*dig
leal (%eax,%eax,4),%eax # 5*index
movl pgh(%edx,%eax,4),%eax # *(pgh+5*index+4*dig)
Multi-Level Array:
int get_univ_digit(int index,int dig){
return univ[index][dig];
}
leal 0(,%ecx,4),%edx # 4*index
movl univ(%edx),%edx # Mem[univ+4*index]
movl (%edx,%eax,4),%eax # Mem
"."開頭的行都是匯編指示(Directives) .file .def .text
其中.file和.def均用於調試(可忽略)
":"
gcc -O2 -mpreferred-stack-boundary=2
編譯參數:
as -o my-object-file.o hellowrold.s
-gstabs //產生帶調試信息的Object文件
--32
ld -o my-exe-file my-object-file.o
-m elf_i368
sudo apt install g++-multilib
.text
.globl _start
_start:
popl %ecx # argc
-- helloworld.s
.data
msg:
.ascii "Hello hhh world\n"
len = .-msg # "."表示當前地址,減msg表示長度
.text # 代碼段
.globl _start # 匯編程序的入口
_start:
movl $len,%edx
movl $msg,%ecx
movl $1,%ebx # 系統輸出(write系統調用)
movl $4,%eax # write系統調用編號是4
int $0x80 # 中斷指令
movl $0,%ebx # 程序退出 exit()中的參數0
movl $1,%eax # 1號系統調用,就是程序退出
int $0x80
傳給系統調用的功能號,存放到
ebx,ecx,edx,esi,edi中,再多就放入棧中
返回值在 eax 中獲得
部分系統調用列表:
exit-terminate current process:
In eax 1
ebx return code
Out
fork-create child process
In eax 2
Out eax 0 in the clone
process id of clone
read-read from file or device:
In eax 3
ebx file descriptor
ecx address of the buffer to read into
edx maximum number of bytes to read
Out eax number of bytes actually read
close
In eax 6
ebx file descriptor
Out eax zero for success | EBADF
x86-32 linux系統調用的參考資料
http://syscalls.kernelgrok.com/
處理命令行參數:
.text
.globl _start
_start:
popl %ecx # argc
vnext:
popl %ecx #argv
test %ecx,%ecx
jz exit #如果為空就退出
movl %ecx,%ebx # 參數列表的首地址
xorl %edx,%edx
strlen:
movb (%ebx),%al
inc %edx # 參數個數 記數
inc %ebx
test %al,%al #如果為0則表示結束了 // and ,常用於判斷是否為空
jnz strlen
movl $10,-1(%ebx) # 10是換行符
movl $4,%eax # 系統調用
movl $1,%ebx # 文件描述符
int $0x80
jmp vnext
exit:
movl $1,%eax
xorl %ebx,%ebx # 自己和自己異或,清零
int $0x80
匯編調用lib_c庫函數示例
.section .data
output:
.asciz "The process vendor id is '%s'\n" # z zero,補零
.section .bss ;可讀可寫且沒有初始化的數據區
.lcomm buffer,12
.section .text
.globl _start
_start:
movl $0,%eax
cpuid ; eax傳入0,返回值在ebx,edx,ecx
movl $buffer,%edi
movl %ebx,(%edi)
movl %edx,4(%edi)
movl %ecx,8(%edi)
push $buffer ; $表示取的是地址的值
push $output
call printf
addl $8,%esp ;調用者恢復
push $0 ;c函數exit的參數
call exit
as -o cpuid.o cpuid.s --32
ld -m elf_i386 -lc -dynamic-linker /lib/ld-linux.so.2 -o cpuid cpuid.o
數據段 .rodata
output:
.ascii "hello world." .byte
pi: .double
.float 3.14 .float
ages: .long 32位整數,和int相同
.int 20,10,30,40 .octa 16字節整數
.qual 8字節
.short 16位
.singlee 單精度浮點數
bss段
無需聲明特定的數據類型,只需聲明為所需目的的原始內存部分即可
.comm 聲明為未初始化全局內存區域
.lcomm 本地 , 外部模塊不能訪問他們
冪計算:
pushl $3
pushl $2
call power
addl $8,%esp # move the stack pointer back
pushl %eax # save the first anwser
pushl $2
pushl $5
call power
addl $8,$esp
popl %ebx
addl %eax,%ebx
movl $1,%eax
int $0x80
.equ
.equ factor,3 # 類似define
syscall 號碼:
/usr/include/asm/unistd_64.h
32位的匯編程序請查看/usr/include/asm/unistd_32.h