下面这张图片是关于基本的CPU和内存布局:
计算机的处理器有很多不同的架构, 比如x86-64、ARM、Power, 每一种处理器的指令集都不相同, 也就意味着汇编语言不同
在汇编代码中, 我们经常会使用各种以%开头的寄存器, 我们在代码中最长使用的是16个常用的通用寄存器
- %rax, 通常用于函数的返回值
- %rsp, 通常用于指向栈顶
- %rdi, %rsi, %rdx, %rcx, %r8, %r9给函数传递整型参数, 如果超过6个参数, 那么我们就会放到栈帧中
- 如果程序要使用%rsi, %rbp, %r12, %r13, %r14, %r15这几个寄存器, 是由被调用者Callee负责保护, 也就是写到栈中, 在返回的时候要恢复这些寄存器中原来的内容, 其它寄存器的内容, 则是由调用者Caller负责保护
上面这些寄存器都是64位的, 但是我们可以使用
除了通用寄存器以外, 我们还需要了解下面的寄存器和它们的用途
- 8个80位的x87寄存器, 用于浮点数的计算
- 8个64位的MMX寄存器, 用于MMX指令,
- 指令寄存器, rip, 保存指令地址, CPU总是根据这个寄存器来读取指令
- flags: 每一个位用来标识一个状态
下面我们使用一段C代码来生成汇编代码
#include <stdio.h>
int main(int argc, char* argv[])
{
printf("Hello, %s\n", "yinwang");
return 0;
}
- 伪指令, 伪指令是辅助性的,汇编器在生成目标文件时候会用到这些信息
- 标签用冒号进行结尾, 这种标签类似于C语言中的break语句。
- 指令是用来给CPU执行的, 指令后面可以跟随操作数的位数
常见指令以及对应的操作数
mov 寄存器|内存|立即数, 寄存器|内存
mov %rax, %rax #将一个
lea 源, 目的地址
比如我们可以将字符串的地址加载到寄存器中
lea L_str(%rip), %rdi
add 指令是做加法运算
add 立即数|寄存器
call标签或地址
ret从栈中返回地址, 并跳转过去
jmp, 跳转到某个位置