本文主要是介绍ARM - AArch64 - 通用寄存器,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
说明
- 在深入一点了解了系统调用以及非安全world(REE)/安全world(TEE)切换时参数传递和结果返回的实现原理(通过通用寄存器实现),对通用寄存器的使用有了一个全新的认识,对知识做个总结。
简介
- Arm AArch64状态下提供了31个64位通用寄存器:X0 ~ X30。
通用体现
- 不同于特殊寄存器(每一bit可能都有特殊作用),通用寄存器本身没有任何作用(就是一个64位存储器),其作用是由使用体现。
- 任意时间任意环境(任意特权等级,任意安全等级,用户态/内核态)都可以使用这些通用寄存器,没有任何访问限制。
- 应用层调用系统调用(应用态和内核态切换),参数传递和结果返回等都是使用通用寄存器实现。
- 应用层程序执行,函数调用时参数传递,也是使用通用寄存器实现。
- REE与TEE通信,需要做非安全world和安全world切换,异常等级切换等也是使用通用寄存器传递参数和运行结果。
- 任何异常等级中函数执行也是使用这些通用寄存器,切换异常等级也不会丢失。
- 重点:通用寄存器是参数传递,返回结果等一些通用行为使用的寄存器,在任何场景,环境下(用户态/内核态切换,安全world/非安全world切换)都是一样,没有其它寄存器。
32位形态
- X0 ~ X30是64位寄存器,也可以作为32位寄存器使用,其32位形态(W0 ~ W30)取自相应64位寄存器(X0 ~ X30)的低32位,例如:W0映射到X0的低32位,W1映射到X1的低32位。
- 读写其32位形态行为如下:
- 读取,从32位寄存器(W0-W30)读取时,忽略相应64位寄存器(X0-X30)高32位,并保持其它不变。
- 写入,将数据写入32位寄存器(W0-W30)时,会将其对应的64位寄存器(X0-X30)的高32位设置为零,例如:将0xFFFFFFFF写入W0会将X0设置为0x00000000FFFFFFFF。
用途
- 通用寄存器的用途更多的是一种使用规范,并没有什么强制限制。
- 不同使用场景会有些细微区别。
规范
- 根据用途,通用寄存器可以分为四组。
x0 ~ x7
- 传递参数
- 应用层函数调用,传递子函数的参数,超出的参数用堆栈传递。
- 应用层调用系统调用,传递系统调用的参数,系统调用只支持6个参数,因此只用到了x0 ~ x5。
- REE与TEE通信,SMC请求使用x0 ~ x7 传递参数。
- 传递返回值
- 应用层调用系统调用,函数调用,64位的返回结果保存在x0中。
- REE与TEE通信,ERET使用x0 ~ x3 传递TEE运行结果。
- 临时存储器
- 在函数中也可以用作临时寄存器,可以保存中间值或调用者的寄存器变量。
X9-X15:调用者寄存器
- 网上描述:如果调用者需要在调用另一个函数时保留一些寄存器中的任何值,则调用者必须将受影响的寄存器保存在自己的堆栈帧中。 也可以修改子例程,而不需要在返回调用函数时保存值到堆栈和从堆栈中恢复它们。
- 后续体会较深时,再修正。
X19-X29:被调用寄存器
- 网上描述:这些寄存器保存在被调用者内部。 只要在返回前保存并恢复,就可以在被调用的子程序中修改。
- 后续体会较深时,再修正。
X8, X16-X18, X29, X30:特殊目的寄存器
- X8 是间接结果寄存器。用于传递间接结果的地址位置,例如,函数返回大型结构的位置。
- x8 在系统调用(syscall)中用于传递系统调用编号。
- 网上描述:X16 和 X17 分别是 IP0 和 IP1,临时寄存器(intra-procedure-call )可以通过veneers或者类似代码使用,或者作为子程序调用之间的中间值的临时寄存器。它们可以被函数破坏。veneers代码是链接器自动插入的一小段代码,例如当分支(跳转)目标超出分支指令(支持的)范围时。
- X18 是平台寄存器,保留供平台 ABI 使用。这是平台上的一个附加临时寄存器,没有为其分配特殊含义。
- X29 是 Frame Pointer 寄存器,指向当前方法栈的底部。用于连接栈帧,使用时必须保存。
- X30 是链接寄存器,这个寄存器会记录着当前方法的调用方地址 ,即当前方法调用完成时应该返回的位置。我们进行函数调用的时候,会用X30记录当前调用地址,等调用函数执行完毕,会从X30取出地址进行返回。我们遇到 Crash 要获取方法堆栈,其本质就是不断的向上递归每一个 x30 寄存器的记录状态(也就是栈上 X30 寄存器的内容) 来找到上层调用方。
- 后续体会较深时,再修正。
实际使用
- 系统调用实现
// 标准C库(musl)syscall 实现
#define __asm_syscall(...) do { \__asm__ __volatile__ ( "svc 0" \: "=r"(x0) : __VA_ARGS__ : "memory", "cc"); \return x0; \ //使用 x0传递返回值} while (0)static inline long __syscall0(long n)
{ register long x8 __asm__("x8") = n; //使用 x8传递系统编译编号register long x0 __asm__("x0");__asm_syscall("r"(x8));
}
...
static inline long __syscall6(long n, long a, long b, long c, long d, long e, long f)
{register long x8 __asm__("x8") = n; register long x0 __asm__("x0") = a; //使用x0 ~ x5传递参数register long x1 __asm__("x1") = b;register long x2 __asm__("x2") = c;register long x3 __asm__("x3") = d;register long x4 __asm__("x4") = e;register long x5 __asm__("x5") = f;__asm_syscall("r"(x8), "0"(x0), "r"(x1), "r"(x2), "r"(x3), "r"(x4), "r"(x5));
}
这篇关于ARM - AArch64 - 通用寄存器的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!