x86汇编指令集包括x86-64(intel-64,amd64, emt64), x86-32, x86-16
内存模型
通用寄存器
X86-32
EAX 累加器(Accumulator), 用于乘、除、输入/输出等操作
EBX 基地址寄存器(Base Register), 作为存储器指针来使用
ECX 计数寄存器(Count Register), 在循环和字符串操作时,要用它来控制循环次数;在位操作中,当移多位时,要用CL来指明移位的位数EDX 数据寄存器(Data Register), 在进行乘、除运算时,它可作为默认的操作数参与运算,也可用于存放I/O的端口地址
EDI 目的变址寄存器(Destination Index)
ESI 源变址寄存器(Source Index)
用于存放存储单元在段内的偏移量,用它们可实现多种存储器操作数的寻址方式,为以不同的地址形式访问存储单元提供方便EBP 基址指针寄存器(Base Pointer)
ESP 堆栈指针寄存器(Stack Pointer)
用于存放堆栈内存储单元的偏移量,用它们可实现多种存储器操作数的寻址方式,为以不同的地址形式访问存储单元提供方便
X86-64
x86-32 CPU指令
push %x
前置操作:取出 ESP 寄存器里面的地址,将其减去4个字节(如果x是int),然后将新地址写入 ESP 寄存器
将x压入stack
call func
调用函数func
move %src, %dst
用于将一个src值写入某个寄存器dst
add %x, %y
用于将两个运算子x,y相加,并将结果写入第一个运算子x
pop %x
用于取出 Stack 最近一个写入的值(即最低位地址的值),并将这个值写入运算子指定的位置x, 然后会将 ESP 寄存器里面的地址加4 (如果是int)
ret
用于终止当前函数的执行,将运行权交还给上层函数
X86-64 CPU指令
寻址模式
指令后缀含义
全局值(全局变量和函数)的引用直接使用名字,例如x或者printf;
常数使用带有美元符号的立即数,例如$56;
寄存器值的引用使用寄存器的名称,例如 %rbx;
间接寻址则是使用与寄存器中保存的地址值对应的内存中的值,例如,(%rsp) 表示%rsp指向的内存中的值;
相对基址寻址,则是把一个常数加到寄存器值上,例如 -16(%rcx)表示把%rcx指向的地址前移16个字节后对应的内存值;
相对基址寻址有很多复杂变种,例如-16(%rbx,%rcx,8)表示-16+%rbx+%rcx*8对应的地址的内存值,这种寻址模式在访问元素大小特殊的数组时很有用;
基本算术指令
加ADD, 减SUB, 乘 IMUL 和 除IDIV
IMUL 指令稍有不同:它把%rax的值乘以操作数,把结果的低64位存在%rax,高64位放在%rdx (两个64位值相乘的结果是128位);
IDIV则相反,把128bit值(低64位在 %rax ,高64位在%rdx)除以指令中的操作数(为了正确处理负数,用CDQO 指令把%rax符号扩展到%rdx),商存储在%rax,余数在%rdx;
INC和DEC会把寄存器的值破坏掉,AND, OR, 和XOR以及NOT也会破坏寄存器的值
比较和跳转
JMP指令构造一个跳转,CMP指令比较两个不同的寄存器中的值,设置EFLAGS寄存器的比特位,记录下结果(大于,小于还是等于)。使用带条件的跳转指令根据EFLAGS完成相应跳转
JMP指令需要编译器生成目标标签(LABEL),标签必须唯一,并且是汇编文件内部私有,对外部不可见,除非有.globl指示。按C语言的说法,汇编中没有修饰的标签是static的,.globl修饰的标签是extern的
栈 stack
PUSH 、 POP,分别会把%rsp减去或者加上8
函数调用 Calling Functions
在大多数汇编程序中(X86-64不是),调用约定是简单的把每个参数都压栈,然后调用函数。被调用的函数从栈中获取参数,完成操作,把返回值保存到寄存器中并返回。调用方再把参数从栈pop出来(其实X86 32就是这样的)。
X86-64的调用方式有些不同,整个约定相当复杂,下面是简化版:
1. 整数参数(包含指针)依次放在%rdi, %rsi, %rdx, %rcx, %r8, 和 %r9 寄存器中。
2. 浮点参数依次放在寄存器%xmm0-%xmm7中。
3. 寄存器不够用时,参数放到栈中。
4. 可变参数哈函数(比如printf), 寄存器%eax需记录下浮点参数的个数。
5. 被调用的函数可以使用任何寄存器,但它必须保证%rbx, %rbp, %rsp, and %r12-%r15恢复到原来的值(如果它改变了它们的值)。
6. 返回值存储在 %eax中.
调用函数前,先要把参数放到寄存器中。然后,调用方要把寄存器%r10 和%r11的值保存到栈中。之后,执行CALL指令,把IP指针的值保存到栈中,并跳到函数的起始地址执行。从函数返回后,恢复%r10 和%r11,并从%eax获取返回值。
编译工具
gcc -S xxx.c 生成汇编指令
引用
http://www.ruanyifeng.com/blog/2018/01/assembly-language-primer.html
https://blog.csdn.net/pro_technician/article/details/78173777