通用目的寄存器(Generous Purpose Registers,GPRs)
GPRs 的作用
x86-64 指令架构要求至少有 16 个 64Bit 的通用寄存器。
寄存器 | 全称 | 用途 |
---|---|---|
%rax | register a extended | 存储过程调用返回值(return value) |
%rbx | register b extended | / |
%rcx | register c extended | 存储过程调用的第四个参数 |
%rdx | register d extended | 存储过程调用的第三个参数 |
%rbp | register base pointer | 存储当前栈帧的基地址 |
%rsp | register stack pointer | 存储栈顶地址 |
%rsi | register source index | 存储过程调用的第二个参数 |
%rdi | register destination index | 存储过程调用的第一个参数 |
%r8 | register 8 | 存储过程调用的第五个参数 |
%r9 | register 9 | 存储过程调用的第六个参数 |
%r10-%r11 | register 10 ~ register 11 | / |
%r12-%r15 | register 12 ~ register 15 | / |
再提一个特殊的寄存器:指令寄存器。在 x86-64 汇编中,用 %rip
表示。它永远指向下一条即将执行的地址。
GPRs 的访问方式
寄存器也能以另一种方式去访问特定位,总结如下。
- 用
e
替换前缀r
或者添加后缀d
,表示仅使用该寄存器的低 32Bit。如%eax
、%ebp
、%esp
、%r8d
、%r15d
。 - 移除前缀
r
或者添加后缀d
,表示仅使用该寄存器的低 16Bit。如%ax
、%bp
、%r8w
、%r15w
。 - 移除前缀
r
,并且用h
替换后缀x
,表示仅使用该寄存器的低 16Bit 中的高 8Bits。如%ah
、%bh
。 - 移除前缀
r
,并且用l
替换后缀x
(或者添加后缀l
),表示仅使用该寄存器的低。16Bit 中的高 8Bit。如:%al
、%bl
、%bpl
、%spl
、%sil
、%dil
、%r8b
、%r15b
。
图 1 General Purpose Registers in 64-Bit Mode
注意:
%rbp
、%rsp
、%rsi
、%rdi
、%r8 ~ %r15
的低 16Bit 中的高 8Bit 未提供符号用于单独访问。
过程调用(Procedure Call)
过程调用,可以简单理解为函数调用,涉及到:
- 传递控制:指令执行权的转移控制。
- 传递数据:过程参数的传入,过程结果的返回。
- 内存管理:过程中,局部内存的申请和释放。
传递控制
假设过程 P 调用 过程 Q,
- 进入过程 Q 前,P 必须将
%rip
设为 Q 的地址; - 然后 Q 执行结束时,必须将
%rip
设为 P 调用 Q 前的下一条地址。
寄存器 %rip
的值,仅能通过有限的几条指令更改。最常见的是无条件跳转指令 call
。
0100 Q:
// ......
01fe ret
1000 call Q
1005 mov %rax, -16(%rbp)
以上述汇编代码片段为例:执行到地址 0x1000
处,
call
指令执行完,并且还未开始执行下一条指令,%rip
等于0x0100
;- 然后等到 Q 过程执行到
0x01fe
处时,ret
指令执行完,并且还未开始执行下一条指令,%rip
等于0x1005
。
传递参数
寄存器用途约定
用于存储传入参数:%rdi
、%rsi
、%rdx
、%rcx
、%r8~%r9
,共计 6 个。
用于存放返回结果:%rax
。
如果参数个数超过 6 个,那必须借助栈来传递参数。这部分空间由调用者分配管理。
寄存器使用约定
因为寄存器是共用的,所以过程调用中,寄存器中的值可能被不同过程修改。
比如 P 过程调用 Q 过程,Q 过程执行过程中可能修改 P 过程中寄存器所存放的值,因此需要有一个约定让不同过程可以放心的操作寄存器。
这种分类方式下,寄存器可分为两组:
- 调用者保护寄存器:由调用者(caller)保护的寄存器。在被调用的过程执行结果后,调用者恢复这些寄存器的值,然后再使用。
- 被调用者保护寄存器:由被调用者(callee)保护的寄存器。在被调用的过程结束前,被调用者应当恢复这些寄存器原来的值(即进入调用前的初始值)。