ARM - AArch64 - 通用寄存器

说明

  • 在深入一点了解了系统调用以及非安全world(REE)/安全world(TEE)切换时参数传递和结果返回的实现原理(通过通用寄存器实现),对通用寄存器的使用有了一个全新的认识,对知识做个总结。

简介

  • Arm AArch64状态下提供了31个64位通用寄存器:X0 ~ X30。

通用体现

  1. 不同于特殊寄存器(每一bit可能都有特殊作用),通用寄存器本身没有任何作用(就是一个64位存储器),其作用是由使用体现。
  2. 任意时间任意环境(任意特权等级,任意安全等级,用户态/内核态)都可以使用这些通用寄存器,没有任何访问限制。
    1. 应用层调用系统调用(应用态和内核态切换),参数传递和结果返回等都是使用通用寄存器实现。
    2. 应用层程序执行,函数调用时参数传递,也是使用通用寄存器实现。
    3. REE与TEE通信,需要做非安全world和安全world切换,异常等级切换等也是使用通用寄存器传递参数和运行结果。
    4. 任何异常等级中函数执行也是使用这些通用寄存器,切换异常等级也不会丢失。
  • 重点:通用寄存器是参数传递,返回结果等一些通用行为使用的寄存器,在任何场景,环境下(用户态/内核态切换,安全world/非安全world切换)都是一样,没有其它寄存器。

32位形态

  • X0 ~ X30是64位寄存器,也可以作为32位寄存器使用,其32位形态(W0 ~ W30)取自相应64位寄存器(X0 ~ X30)的低32位,例如:W0映射到X0的低32位,W1映射到X1的低32位。
  • 读写其32位形态行为如下:
  1. 读取,从32位寄存器(W0-W30)读取时,忽略相应64位寄存器(X0-X30)高32位,并保持其它不变。
  2. 写入,将数据写入32位寄存器(W0-W30)时,会将其对应的64位寄存器(X0-X30)的高32位设置为零,例如:将0xFFFFFFFF写入W0会将X0设置为0x00000000FFFFFFFF。

用途

  • 通用寄存器的用途更多的是一种使用规范,并没有什么强制限制。
  • 不同使用场景会有些细微区别。

规范

  • 根据用途,通用寄存器可以分为四组。

x0 ~ x7

  1. 传递参数
  • 应用层函数调用,传递子函数的参数,超出的参数用堆栈传递。
  • 应用层调用系统调用,传递系统调用的参数,系统调用只支持6个参数,因此只用到了x0 ~ x5。
  • REE与TEE通信,SMC请求使用x0 ~ x7 传递参数。
  1. 传递返回值
  • 应用层调用系统调用,函数调用,64位的返回结果保存在x0中。
  • REE与TEE通信,ERET使用x0 ~ x3 传递TEE运行结果。
  1. 临时存储器
  • 在函数中也可以用作临时寄存器,可以保存中间值或调用者的寄存器变量。

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开发)