编译器后端汇编代码篇

94 阅读2分钟

下面这张图片是关于基本的CPU和内存布局:

c.png

计算机的处理器有很多不同的架构, 比如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;
}

asm.png

  • 伪指令, 伪指令是辅助性的,汇编器在生成目标文件时候会用到这些信息
  • 标签用冒号进行结尾, 这种标签类似于C语言中的break语句。
  • 指令是用来给CPU执行的, 指令后面可以跟随操作数的位数

常见指令以及对应的操作数

mov 寄存器|内存|立即数, 寄存器|内存
mov %rax, %rax #将一个

lea 源, 目的地址
比如我们可以将字符串的地址加载到寄存器中
lea L_str(%rip), %rdi

add 指令是做加法运算
add 立即数|寄存器


call标签或地址
ret从栈中返回地址, 并跳转过去

jmp, 跳转到某个位置