X86汇编

x86汇编指令集包括x86-64(intel-64,amd64, emt64), x86-32, x86-16

内存模型

X86汇编_第1张图片

通用寄存器

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-64通用寄存器
X86汇编_第2张图片
通用寄存器发展图

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指令

寻址模式

指令后缀含义

X86汇编_第3张图片
单字母后缀决定自动多大数据

全局值(全局变量和函数)的引用直接使用名字,例如x或者printf;
常数使用带有美元符号的立即数,例如$56;
寄存器值的引用使用寄存器的名称,例如 %rbx;
间接寻址则是使用与寄存器中保存的地址值对应的内存中的值,例如,(%rsp) 表示%rsp指向的内存中的值;
相对基址寻址,则是把一个常数加到寄存器值上,例如 -16(%rcx)表示把%rcx指向的地址前移16个字节后对应的内存值;
相对基址寻址有很多复杂变种,例如-16(%rbx,%rcx,8)表示-16+%rbx+%rcx*8对应的地址的内存值,这种寻址模式在访问元素大小特殊的数组时很有用;

X86汇编_第4张图片
使用各种寻址模式加载一个64位值到%rax

基本算术指令

加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完成相应跳转

X86汇编_第5张图片
带条件的跳转指令

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中.

X86汇编_第6张图片

调用函数前,先要把参数放到寄存器中。然后,调用方要把寄存器%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

你可能感兴趣的:(X86汇编)