跳转至

x86_64 汇编

寄存器

通用寄存器

常用的寄存器有 16 个,其中一些是通用的,另外的是有特定含义的。

在指令集的发展中很多寄存器经历了特定寄存器到通用寄存器的转变,现存留的特定功能的寄存器很少了。

  • %rbp, register base pointer, 基址地址,比如函数起始位置

  • %rsp, register stack pointer, 栈指针

  • %rsi, %rdi, 常被用来处理字符串

  • %rax, %rbx, %rcx, %rdx, 以及 %r9%r15 共 12 个通用寄存器

对于每个 64 位寄存器,还有特定的名称可以用来表示其中的 32位 / 16位 / 低8位 / 紧靠低8位的8位。 命名方式有两种:

64位 32位 16位 8位 offset+8的8位
%rax %eax %ax %al (low) %ah (high)
%r8 %r8d %r8w (word) %r8l (low) %r8h (high)
  • %rip 是指令寄存器,即程序运行的位置

浮点数寄存器

浮点数是用 x87 FPU 单独处理的,它有 8 个 80 位寄存器

  • r0r7, 用于处理浮点数

它也有自己一套独立的指令集

表示

写法 含义
x 全局变量 x
%rax 寄存器 %rax 中的值
$56 常量 56
(%rsp) 寄存器 %rsp 所指向地址中的值
-16(%rbp) *(%rbp - 16)
-16(%rbp, %rcx, 8) *(%rbp + %rcx * 8 - 16)

指令

指令 参数 功能 例子
MOV src, dst 拷贝 src 中的值到 dst MOV (%edx),%eax
ADD op1, op2_and_dst op1 + op2 并存到 op2 ADD %rbx,%rax
SUB op1, op2_and_dst op1 - op2 并存到 op2 SUB %rbx,%rax
IMUL op2 %rax * op2 并将高 64 位存到 %rdx,低 64 位存到 %rax IMUL %rbx
IDIV op2 (%rdx \<\< 64 + %rax) / op2 并将商存到 %rax,余数存到 %rdx
INC src_and_dst 自增 1
DEC src_and_dst 自减 1
JMP label 直接跳转到 label 的位置,相当于 goto JMP loop
CMP op1, op2 比较 op1 和 op2,结果会暂存,然后接条件 jump 语句 CMP $5,%rbx; JNE loop
PUSH src 将 src 的值压栈,注意栈是从高到低生长,%rsp 指向栈顶 PUSH %rax
POP dst 栈顶弹出值到 dst,如果不要结果,直接 ADD $8, %rsp 即可
CALL func 根据函数调用规则调用指定函数名 CALL printf
SHR cnt, val 将 val 中的值右移 cnt 中值的位数
SHL cnt, val 将 val 中的值左移 cnt 中值的位数
AND src, dst dst &= src
OR src, dst dst = src
XOR src, dst dst ^= src
NOT dst dst \~= dst

后缀

指令可以根据处理的位数添加后缀

➤ B:1 字节 ➤ W:2 字节 ➤ L:4 字节 ➤ Q:8 字节

如:MOVB 移动 1 个字节,MOVW 2字节,MOVL 4字节,MOVQ 8字节

条件 jump

  • JE, Jump if Equal
  • JNE, Jump if Not Equal
  • JL, Jump if Less than
  • JLE, Jump if Less or Equal
  • JG, Jump if Greater than
  • JGE, Jump if Greater or Equal

函数调用

x86_64 有着不同常规(常规是压栈传参)的函数调用方式

简化版规则如下:

  • 整数参数存到寄存器 %rdi, %rsi, %rdx, %rcx, %r8, %r9
  • 浮点数参数存到 %xmm0%xmm7
  • 多出来的参数压栈
  • 对于变长参数函数,用 %eax 存浮点参数个数
  • 函数退出的时候要保证恢复 %rbx, %rbp, %rsp, %r12%r15
  • 返回值存在 %eax