前言
该文章写作目的:帮助自己以及有一定汇编基础的同学,尽快回想起汇编相关知识。如果不想本地生成代码,推荐在线编译器compiler expolorer。下文所有就是基于x86-64架构,gcc 11.2编译器介绍。
compiler explorer
很好用,左边是代码输入,可以选择很多语言,右边可以选择编译器以及填入编译选项。还可以生成可执行文件,直接在页面查看运行结果,以及编译后的真实内存地址。不同色块可以帮助我们定位单条语句生成的汇编代码。
常用寄存器寄存器
- %rax 用于函数返回值传参
- %rdi,%rsi 等用于函数传参 rdi为第一个传入参数,x86-64下寄存器大小为64bit。rsi为第二个传入参数还有其他四个这里就不记录了。用于函数传参的寄存器总共有6个,如果函数传入参数超过6个会将多余的传入参数保存在函数栈(内存)中。可见函数形参多于6个会一定程度上影响效率。
- 状态寄存器 有SF(符号寄存器),OF(溢出寄存器),CF等,这里寄存器的使用,往往有特定的指令。如我们使用cmp会比较两个值,会修改ZF和CF寄存器的值。je(为jup族指令),它会读取ZF和CF的状态,如果满足 “==" 则跳转。
- %rbp :64bit, 栈底指针,标记函数栈的底部
- %rsp :64bit, 栈顶指针,标记函数栈的顶部
- %rip : 32bit pc指针,存放下一条运行的指令
常识
栈空间扩展方向是从高地址到低地址的。
栈空间分配是编译时决定,函数内局部变量数量不会怎加可重定向文件中的符号数,即不会增加exe的大小。
常用汇编指令
汇编指令往往是一个族。比如mov指令,有movq,movl 等,movq作用是mov4个字节。
1、mov
将一定字节数数据,从内存读到寄存器,或者从寄存器写入内存。
mov des,src
2 、cmp
cmp A, B
通过做差,比较A和B,会修改状态寄存器的值
3、jmp
jmp Lable
跳转到Lable标签处
【注】%reg代表某个寄存器
4、Push
Push %reg
将寄存器保存到当前函数栈中,可以分为两步:(1)利用sub指令使栈顶寄存器%rsp 扩展%reg大小(2)利用mov指令将%reg中的值写入内存(内存地址为%rsp中的值)
5 、Pop
Push %reg
将寄存器保存到当前函数栈中,分两步 (1)使用mov以栈顶指针为地址读取%reg大小的字节数到%reg中 (2)利用add 将%rsp收缩%reg大小字节数。
6、call
call Address用于调用函数
分两步(1)Push %rip 保存当先函数中下一条指令(2)jmp跳转到Address处开始执行代码
7 、ret
用于函数返回
分两步:(1)修改%rsp为%rbp中的值 ,这一步即释放资源
(2) Pop %rip:从内存中加载调用call时保存的下一条指令到%rip
8、lea
获取内存地址。对应c代码&