0%

ARM64/AArch64 Armv8架构栈操作

本文主要介绍Armv8-A架构的栈操作,涉及AArch64执行状态的关键寄存器、栈操作的ARM64汇编指令和一些编程约定。函数调用必然涉及到 出栈和入栈,理解了ARM64的栈操作,对更深入理解C语言的函数调用大有裨益;也能够协助debug一些诡异的Bug。

AArch64寄存器

通用寄存器

Armv8-A提供了31个64-bit通用寄存器,在所有的异常级别都可以访问。

AArch64执行状态,每个寄存器(X0-X30)都是64位宽的。每个64-bit通用寄存器(X0-X30) 都有一个32-bit形式(W0-W30)。32-bit的W寄存器是对应64-bit X 寄存器的低32位。 也就是说,W0组成X0的低32位,W1组成X1的低32位。如下图所示:

  • X0 ~ X30:访问寄存器的全部64位
  • W0 ~ W30:访问寄存器的低32位
  • 从W寄存器读取会忽略对应X寄存器的高32位,并且保持它们不变
  • 向W寄存器写入会讲X寄存器的高32位设置为0;所以写入0xFFFFFFFF到W0会将X0的值设置为0x00000000FFFFFFFF

特殊寄存器

除了31个通用寄存器X0~X30, Armv8还提供了几个特殊寄存器。

Name Size Description
WZR 32 bits 32-bit Zero register
XZR 64 bits 64-bit Zero register
WSP 32 bits 32-bit Current stack pointer
SP 64 bits 64-bit Current stack pointer
PC 64 bits Program counter

特别说明以下寄存器,这些寄存器是完成栈操作的关键寄存器。

  • SPStack Pointer,栈顶寄存器,保存当前栈顶地址;
  • FP(X29)Frame Pointer,栈基址寄存器,保存当前栈底地址;使用 X29 寄存器;
  • LR(X30)Link Register,保存跳转指令 bl 后面的下一条指令的地址;使用 X30 寄存器;
  • PC :保存将要执行的下一条指令的地址,不允许直接访问;

当在AArch64状态执行时,异常返回状态保存在以下专用寄存器中:

  • Exception Link Register(ELR):异常链接寄存器,保存在异常返回后要执行的指令地址;
  • Saved Processor State Register(SPSR):异常发生时,保存处理器执行状态;

每个异常级别都有各自的专用寄存器,如下表所示:

Registers EL0 EL1 EL2 EL3
SP SP_EL0 SP_EL1 SP_EL2 SP_EL3
ELR ELR_EL1 ELR_EL2 ELR_EL3
SPSR SPSR_EL1 SPSR_EL2 SPSR_EL3

AArch64过程调用标准

ARM规定了过程调用的标准,称作 Procedure Call Standard for the ARM 64-bit Architecture(AArch64),即AAPCS64。AAPCS64对一些寄存器在过程调用中扮演的角色进行了约定,且看下面的表格。

Register Special Role in the procedure call standard
SP Stack Pointer,栈顶指针
R30 LR Link Register,过程调用返回后要执行的指令地址
R29 FP Frame Pointer,栈帧指针,即栈底指针
R19…R28 Callee-saved registers,由被调用者保存
R18 Platform Register;否则作为临时寄存器
R17 IP1
R16 IP0
R9…R15 临时寄存器
R8 Indirect result location register,保存非直接返回结果的内存地址
R0…R7 Parameter registers,存放过程调用的前8个参数
R0 Result register,保存过程的直接返回值

注:这里的R泛指寄存器,包括X和W。

  • X0 ~ X7 分别用于存放方法调用的前 8 个参数;如果参数超过 8 个,多余的参数需要通过栈来传递;
  • X0 一般用于保存方法的直接返回值;
  • 如果方法返回一个大的数据结构,则将数据结构的地址保存在 X8 ;