跳转至

ARM64(AArch64)


ARM64 寄存器

ARM64 有 31 个通用寄存器,编号为 x0 ~ x30

  • xN:64 位寄存器,如 x0
  • wN:对应的低 32 位寄存器,如 w0
  • wN 写入后,会把对应 xN 的高 32 位自动清零
64-bit 32-bit 常见用途
x0 w0 第 1 个参数 / 返回值
x1 w1 第 2 个参数 / 返回值
x2 w2 第 3 个参数
x3 w3 第 4 个参数
x4 w4 第 5 个参数
x5 w5 第 6 个参数
x6 w6 第 7 个参数
x7 w7 第 8 个参数
x8 w8 间接返回值地址 / 平台相关用途
x9 ~ x15 w9 ~ w15 临时寄存器
x16 w16 IP0,过程调用临时寄存器
x17 w17 IP1,过程调用临时寄存器
x18 w18 平台保留寄存器 / 平台相关
x19 ~ x28 w19 ~ w28 被调用者保存寄存器
x29 w29 帧指针 fp
x30 w30 链接寄存器 lr,保存返回地址

特殊寄存器

寄存器 说明 常见用途
sp 栈指针 指向当前栈顶 x31
pc 程序计数器 指向当前执行位置,AArch64 中通常不直接当普通寄存器使用
fp 帧指针 一般是 x29,用于访问栈帧
lr 链接寄存器 一般是 x30,保存返回地址
xzr / wzr 零寄存器 读取恒为 0,写入无效果
nzcv 标志寄存器 保存条件标志位
标志位 名称 含义
N Negative 结果为负
Z Zero 结果为 0
C Carry 进位 / 借位相关
V Overflow 有符号溢出

调用约定

前 8 个整数或指针参数通常使用 x0 ~ x7 寄存器,超过 8 个参数时,通常通过栈传递。

前 8 个浮点参数通常使用 v0 ~ v7 寄存器,超过 8 个时也通过栈传递。

类型 返回寄存器
整数 / 指针 x0
较大整数或复合返回值 x0 / x1 或间接返回
浮点数 / SIMD v0

寄存器保存规则

  • Caller-saved(调用者保存,调用后可能被改)

    • x0 ~ x18 中的大部分临时寄存器
  • Callee-saved(被调用者保存,函数若使用需先保存)

    • x19 ~ x28
    • x29fp
    • x30lr)若函数内部需要继续调用其他函数,通常也要保存

指令

ARM64 指令一般形如:

GAS
opcode  destination, source1, source2

0. 最常见指令表

指令 类型 简要说明
mov xd, xs 数据传送 复制寄存器值
ldr xt, [xn] 访存 从内存加载
str xt, [xn] 访存 向内存存储
ldp xt1, xt2, [xn] 访存 成对加载
stp xt1, xt2, [xn] 访存 成对存储
adr xd, label 地址计算 获取近地址
adrp xd, label 地址计算 获取页基址
add xd, xn, xm/#imm 算术运算 加法
sub xd, xn, xm/#imm 算术运算 减法
adds xd, xn, xm 算术运算 加法并更新标志位
subs xd, xn, xm 算术运算 减法并更新标志位
mul xd, xn, xm 算术运算 乘法
madd xd, xn, xm, xa 算术运算 乘加
sdiv xd, xn, xm 算术运算 有符号除法
udiv xd, xn, xm 算术运算 无符号除法
neg xd, xn 算术运算 取负
and xd, xn, xm 逻辑运算 按位与
orr xd, xn, xm 逻辑运算 按位或
eor xd, xn, xm 逻辑运算 按位异或
mvn xd, xn 逻辑运算 按位取反
tst xn, xm 测试 按位与并更新标志位
cmp xn, xm/#imm 比较 比较并更新标志位
lsl xd, xn, #imm 移位 逻辑左移
lsr xd, xn, #imm 移位 逻辑右移
asr xd, xn, #imm 移位 算术右移
ror xd, xn, #imm 移位 循环右移
b label 跳转 无条件跳转
b.eq label 条件跳转 相等时跳转
b.ne label 条件跳转 不等时跳转
b.hi label 条件跳转 无符号大于
b.lo label 条件跳转 无符号小于
b.gt label 条件跳转 有符号大于
b.lt label 条件跳转 有符号小于
cbz xt, label 条件跳转 为 0 时跳转
cbnz xt, label 条件跳转 非 0 时跳转
tbz xt, #bit, label 条件跳转 指定位为 0 时跳转
tbnz xt, #bit, label 条件跳转 指定位非 0 时跳转
bl label 流程控制 调用函数
blr xn 流程控制 间接调用
ret 流程控制 返回
nop 空操作 无实际操作

1. 数据传送类

指令 类型 简要说明
mov xd, xs 数据传送 将寄存器值复制到目标寄存器
ldr xt, [xn] 访存 从内存加载 64 位数据到寄存器
str xt, [xn] 访存 将 64 位寄存器值存到内存
ldp xt1, xt2, [xn] 访存 一次加载一对寄存器
stp xt1, xt2, [xn] 访存 一次存储一对寄存器
adr xd, label 地址计算 获取当前附近地址
adrp xd, label 地址计算 获取页对齐基址,常与 add / ldr 配合
nop 空操作 不执行有效操作

常见寻址形式

形式 示例 说明
基址寻址 ldr x0, [x1] x1 指向的地址加载
基址 + 偏移 ldr x0, [x1, #8] x1 + 8 加载
预索引 ldr x0, [x1, #8]! x1 += 8,再访问
后索引 ldr x0, [x1], #8 先访问,再 x1 += 8

2. 算术运算类

指令 类型 简要说明
add xd, xn, xm 算术运算 xd = xn + xm
add xd, xn, #imm 算术运算 立即数加法
sub xd, xn, xm 算术运算 xd = xn - xm
sub xd, xn, #imm 算术运算 立即数减法
adds xd, xn, xm 算术运算 加法并更新标志位
subs xd, xn, xm 算术运算 减法并更新标志位
mul xd, xn, xm 算术运算 乘法
madd xd, xn, xm, xa 算术运算 乘加,xd = xn*xm + xa
msub xd, xn, xm, xa 算术运算 乘减,xd = xa - xn*xm
sdiv xd, xn, xm 算术运算 有符号除法
udiv xd, xn, xm 算术运算 无符号除法
neg xd, xn 算术运算 取相反数
cmp xn, xm 比较 本质常等价于 subs xzr, xn, xm
cmn xn, xm 比较 比较加法结果,类似“加法版 cmp”

3. 逻辑与位运算

指令 类型 简要说明
and xd, xn, xm 逻辑运算 按位与
orr xd, xn, xm 逻辑运算 按位或
eor xd, xn, xm 逻辑运算 按位异或
bic xd, xn, xm 逻辑运算 按位清零,xn & ~xm
mvn xd, xn 逻辑运算 按位取反
ands xd, xn, xm 逻辑运算 按位与并更新标志位
tst xn, xm 测试 本质类似 ands xzr, xn, xm
lsl xd, xn, #imm 移位 逻辑左移
lsr xd, xn, #imm 移位 逻辑右移
asr xd, xn, #imm 移位 算术右移
ror xd, xn, #imm 移位 循环右移

4. 比较与测试

指令 类型 简要说明
cmp xn, xm 比较 比较两个寄存器,更新标志位,不保存结果
cmp xn, #imm 比较 与立即数比较
cmn xn, xm 比较 做加法比较,仅更新标志位
tst xn, xm 测试 按位与并更新标志位,不保存结果

5. 跳转与流程控制

指令 类型 简要说明
b label 跳转 无条件跳转
bl label 函数调用 跳转并把返回地址写入 lr
br xn 间接跳转 跳转到寄存器指定地址
blr xn 间接调用 调用寄存器指定地址
ret 函数返回 返回到 lr 保存的地址
ret xn 函数返回 返回到指定寄存器地址
cbz xt, label 条件跳转 若寄存器值为 0,则跳转
cbnz xt, label 条件跳转 若寄存器值非 0,则跳转
tbz xt, #bit, label 条件跳转 若指定位为 0,则跳转
tbnz xt, #bit, label 条件跳转 若指定位非 0,则跳转

6. 栈操作

AArch64 没有通用的 push / pop 指令,通常使用:

  • stp
  • ldp

配合 sp 完成压栈和出栈。

指令 类型 简要说明
stp x29, x30, [sp, #-16]! 栈操作 先调整栈,再保存一对寄存器
ldp x29, x30, [sp], #16 栈操作 先恢复寄存器,再回收栈空间
str xt, [sp, #-16]! 栈操作 为单个寄存器分配栈空间后保存
ldr xt, [sp], #16 栈操作 恢复后回收栈空间

7. 条件跳转

指令 条件 含义
b.eq label Z = 1 相等时跳转
b.ne label Z = 0 不相等时跳转
b.hi label C=1Z=0 无符号大于
b.hs label C=1 无符号大于等于
b.lo label C=0 无符号小于
b.ls label C=0Z=1 无符号小于等于
b.gt label Z=0N=V 有符号大于
b.ge label N=V 有符号大于等于
b.lt label N≠V 有符号小于
b.le label Z=1N≠V 有符号小于等于
b.mi label N=1 负数时跳转
b.pl label N=0 非负数时跳转
b.vs label V=1 溢出时跳转
b.vc label V=0 未溢出时跳转
b.al label 无条件 总是跳转

常见函数栈帧

一个典型 ARM64 函数可能长这样:

GAS
1
2
3
4
5
6
7
stp x29, x30, [sp, #-16]!
mov x29, sp
sub sp, sp, #0x20
... 函数体 ...
add sp, sp, #0x20
ldp x29, x30, [sp], #16
ret

含义

  1. stp x29, x30, [sp, #-16]!

    • 保存旧的 fplr
    • 同时分配 16 字节栈空间
  2. mov x29, sp

    • 建立新的栈帧
  3. sub sp, sp, #0x20

    • 为局部变量继续分配栈空间
  4. add sp, sp, #0x20

    • 恢复栈空间,准备返回
  5. ldp x29, x30, [sp], #16

    • 恢复 fplr
    • 同时回收之前保存的栈空间
  6. ret

    • 恢复 fplr
    • 返回到调用者