AArch64

AArch64 是定长指令集架构,其所有的指令在二进制层面长度都是 32 位。

Registers

在 AArch64 架构下,有 31 个通用寄存器。这些通用寄存器可以作为大部分指令的操作数参与运算。
有三套记号用于指代这 31 个通用寄存器:

  • r0r30
    一般用这套记号来指代这些寄存器本身。这些记号通常用于描述汇编指令行为,不会参与到汇编指令中。
  • x0x30
    一般用这套记号表示这些寄存器的 64 位部分。例如,x3 表示 r3 寄存器的 64 位部分。由于 AArch64 架构下的通用寄存器都是 64 位的,所以这套记号其实就代表这些寄存器的所有位。
  • w0w30
    一般用这套记号表示这些寄存器的低 32 位部分。例如,w3 表示 r3 寄存器的低 32 位部分。

零寄存器

寄存器 xzrwzr 被称为零寄存器。所谓零寄存器,就是指读取该寄存器的值,永远为 0;向该寄存器写入数值将无效,也就是说无法向该寄存器写入数值。其中 xzr 为 64 位的零寄存器,wzr 为 32 位的零寄存器。

LR寄存器

怎么实现在哪里调用就能返回到哪里
事实上,这里用到了一个特殊的通用寄存器:r30。相应的 x30 也被称作 LR 寄存器,即 Link Register。当我们使用 bl 调用函数的时候,LR 寄存器会被写入该函数的返回地址。因此,当我们在被调用函数内部使用 ret 指令时,实际上就是将 PC 设置为 LR 寄存器的值而已。

帧指针FP寄存器

刚刚我们提到,AArch64 调用约定规定,在函数栈上,除了 LR 寄存器之外,我们还需要存储 FP 寄存器。FP 寄存器,实际上就是 r29 寄存器。

  • 在函数开始的时候,同 LR 寄存器一样,我们需要把当前的 FP 寄存器的值存在栈上。随后,将当前栈顶指针 sp 的值赋给 FP。也就是说,此时 FP 寄存器的值,指向的就是之前 FP 寄存器值存储在栈上的地址。
  • 在函数返回的时候,将栈上 FP 的值再写回 FP 寄存器。

通过这种做法,FP 可以帮助我们做以下的工作:

  • 访问栈上变量
  • 回溯函数调用栈
    • 当我们分析程序,或者其他特殊情况的过程中,可以根据 FP 来回溯函数调用栈。这是什么意思呢?我们可以用一个 C 语言的结构体来解释,通过 FP,函数的栈帧可以粗略地理解成这样一个结构体:
    • 存储在栈上的 FP 的值可以看作指向前一个函数栈帧的指针,从而函数的栈帧成了一个链表。我们可以通过这个链表,回溯函数的调用栈。
struct StackFrame {
    unsigned long ret_addr;
    struct StackFrame *previous_stack_frame;
    char remain[];
};

PSTATE

  • Process State

  • 如果我们需要使用可以影响 PSTATE 的指令,则需要在后面加上一个 s

    • 例如,addsadd 的一个变种,可以影响 PSTATE。当结果相加为 0,则会设立 Z 位。
  • N 位

    • 1 表示结果为负,0 表示结果非负
  • Z 位

    • 1 表示结果为零,0 表示结果非零
  • C 位

    • 1 表示结果有进位,0 表示结果无进位
  • V 位

    • 1 表示结果有溢出,0 表示结果无溢出

Instructions

ldr

ldr register, =expression
 
ldr    w0, =0x114514

Conditionally Executed Instructions

b.{COND}
  • eqne
    • 表示是否相等。
    • eq 表示相等,判断 Z 位是否为 1:Z == 1
    • ne 表示不等,判断 Z 位是否为 0:Z == 0
  • hihslslo
    • 表示无符号整数的比较。
    • hi 表示大于,判断是否 C 位为 1 且 Z 位为 0:C == 1 && Z == 0
    • hs 表示大于等于,判断 C 位是否为 1:C == 1
    • ls 表示小于等于,判断是否 C 位为 0 或 Z 位为 1:!(C == 1 && Z == 0)
    • lo 表示小于,判断是否 C 位为 0:C == 0
  • gtgelelt
    • 表示有符号整数的比较。
    • gt 表示大于,判断是否 Z 位为 0 且 N 位与 V 位相等:Z == 0 && N == V
    • ge 表示大于等于,判断 N 位是否与 V 位相等:N == V
    • le 表示小于等于,判断是否 Z 位为 1 或者 N 位与 V 位不等:!(Z == 0 && N == V)
    • lt 表示小于,判断 N 位是否与 V 位不等:N != V
csel    dest_reg, src_reg1, src_reg2, {COND}
csel    w0, w1, w2, eq

这条语句的意思就是,如果当前 PSTATE 满足 eq 的条件(也就是 Z == 1),那么就将 w1 赋值给 w0,否则将 w2 赋值给 w0

Addressing

ldr    w1, [x0, #4]!
ldr    w1, [x0], #4
  • 第一种写法被称为前索引寻址,将 x0 的值加 4 赋值给 x0 后,将对应内存取值赋值给 w1
  • 第二种写法被称为后索引寻址,将 x0 对应的内存取值赋值给 w1 后,将 x0 自身值加 4 赋值给自身

Function

    .p2align 2
foo:
    ; do something
    ret

Prologue

在函数开头执行的操作被称为Prologue

Epilogue

在函数返回前执行的操作被称为Epilogue。