前言
Aarch64过程调用标准(AAPCS64)描述了在ARM 64位体系结构下应用程序二进制接口(ABI)在过程调用中应遵循的调用约束,涉及寄存器使用规则、堆栈布局、函数参数传递以及返回值保存等内容。
函数调用过程
Aarch64架构定义了多种跳转指令,其中函数调用主要使用BL
指令,BL指令为分支与链接指令,链接的意思是包含了调用者的地址,以便子函数返回到正确的地址继续执行。与BL指令对应的返回指令是RET。
当调用过程执行BL指令时,处理器执行以下操作:
- 把当前程序执行的地址(PC寄存器值)加上4,保存到LR(X30寄存器)中;
- 加载被调用过程的指令地址到PC寄存器;
- 执行被调用过程。
被调用过程处理完成,调用RET指令返回,处理器执行以下操作:
- 把LR寄存器保存的返回地址加载到PC寄存器;
- 恢复调用过程执行。
这里可以和x86处理器的函数调用过程做个简单对比,x86处理器在执行函数调用时,会将返回地址等信息直接存放到堆栈中,然后从堆栈弹出返回地址到PC寄存器,完成函数调用返回;ARM则使用了独立的LR寄存器存储返回地址。
基本程序执行寄存器
ARM 64位处理器中包含了31个64位通用寄存器,依次为r0-r30:在64位上下文中,使用x0-x30的命名引用寄存器;在32位上下文中,则使用w0-w30的命名来使用寄存器。此外,处理器还提供了栈指针寄存器(SP)。对于所有基本程序执行寄存器在过程调用中的作用描述如下表所示:
寄存器名 | 功能描述 |
---|---|
SP | 栈指针寄存器,指向当前堆栈的栈顶 |
r30 | 链接寄存器,用于保存过程调用的返回地址,可使用LR 别名引用 |
r29 | 帧指针寄存器,存放当前过程调用栈帧的起始地址,可使用FP 别名引用 |
r19…r29 | 遵循被调用者保存原则 |
r18 | 平台预留寄存器。必要时可作为临时寄存器使用 |
r17 | IP1 ,内部过程调用寄存器,常用于动态链接中的plt寻址等指令 |
r16 | IP2 ,内部过程调用寄存器,常用于动态链接中的plt寻址等指令 |
r9…r15 | 临时寄存器,理论上遵循调用者保存原则 |
r8 | 间接结果寄存器,一般用来传递间接结果的地址 |
r0…r7 | 用于参数传递以及保存函数调用的返回值 |
运行时堆栈
对于大多数现代处理器架构,基本都是用堆栈来实现过程调用。堆栈是内存的连续区域,可用于存储过程调用中的寄存器状态、局部变量,并在没有足够的参数寄存器可用时传递多余的其它参数。如下是典型ARM程序运行时的堆栈布局:
参数传递
Aarch64同时支持使用寄存器或堆栈进行参数传递。Aarch64标准提供了8个通用寄存器(r0-r7)用于传递函数参数,依次对应于参数1、参数2、参数3…。一般来说,对于只带有少量参数的函数,仅使用寄存器就足够了;超过8个的参数会存放在堆栈中用于传递给子例程。
返回值
用于参数传递的寄存器同时也可用来保存函数的返回值,对于C语言这种函数返回类型基本都是整型数的,r0
寄存器足够使用。
过程调用示例
考虑一个简单的函数调用示例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int do_foo_func(int arg1, int arg2)
{
int result = 0;
int val1 = arg1, val2 = arg2;
result = val1 + val2;
printf("The result is %ld.\n", result);
return result;
}
int main(int argc, char *argv[])
{
int result = 0;
result = do_foo_func(4, 9);
return result;
}
在此,我们关注do_foo_func函数的栈帧形成,查看do_foo_func的反汇编实现:
顺着函数do_foo_func的汇编指令代码,我们可以总结出x86_64体系下过程调用实现的一些基本步骤:
- 将x29、x30寄存器的值备份到栈上,同时调整sp栈指针的位置,等价于预留栈空间,预留的大小由编译器进行计算。然后,设置帧指针的值为当前栈指针
400684: a9bd7bfd stp x29, x30, [sp, #-48]!
400688: 910003fd mov x29, sp
- 使用栈帧中保存的数据,继续后续指令的执行
- 恢复栈指针,完成当前过程调用栈帧的释放,并从栈中恢复x29、x30寄存器的值
4006cc: a8c37bfd ldp x29, x30, [sp], #48
- 过程调用返回
4006d0: d65f03c0 ret
相关参考
- 《Procedure Call Standard for the ARM 64-bit Architecture (AArch64)》