汇编:
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