assembly-arm

Installation
SKILL.md

ARM / AArch64 汇编

用途

指导代理学习 AArch64(64 位)和 ARM(32 位 Thumb)汇编:寄存器、调用约定、内联汇编,以及 NEON/SVE SIMD 模式。

触发场景

  • "如何阅读 ARM64 汇编输出?"
  • "AArch64 的寄存器和调用约定是什么?"
  • "如何为 ARM 编写内联汇编?"
  • "AArch64 和 ARM Thumb 有什么区别?"
  • "如何使用 NEON 内联函数?"

工作流程

1. 生成 ARM 汇编

# AArch64(本机或交叉编译)
aarch64-linux-gnu-gcc -S -O2 foo.c -o foo.s

# 32 位 ARM Thumb
arm-linux-gnueabihf-gcc -S -O2 -mthumb foo.c -o foo.s

# 使用 objdump
aarch64-linux-gnu-objdump -d -S prog

# 在目标设备上使用 GDB
(gdb) disassemble /s main

2. AArch64 寄存器(AAPCS64)

寄存器 别名 用途
x0x7 参数 1–8 及返回值
x8 xr 间接结果位置(结构体返回)
x9x15 调用者保存的临时寄存器
x16x17 ip0, ip1 过程内调用临时寄存器(链接器使用)
x18 pr 平台寄存器(某些 OS 上保留)
x19x28 被调用者保存
x29 fp 帧指针(被调用者保存)
x30 lr 链接寄存器(返回地址)
sp 栈指针(调用时必须 16 字节对齐)
pc 程序计数器(不可直接访问)
xzr wzr 零寄存器(读取恒为 0,写入被丢弃)
v0v7 q0q7 FP/SIMD 参数和返回值
v8v15 被调用者保存的 SIMD(仅低 64 位)
v16v31 调用者保存的临时寄存器

宽度变体:x0(64 位)、w0(32 位,零扩展到 64 位)、h0(16 位)、b0(8 位)。

3. AAPCS64 调用约定

整数/指针参数: x0x7 浮点/SIMD 参数: v0v7 返回值: x0(整数)、x0+x1(128 位)、v0(浮点/SIMD) 被调用者保存: x19x28x29(fp)、x30(lr)、v8v15(低 64 位) 调用者保存: 其余所有寄存器

在任何 blblr 指令处,栈必须 16 字节对齐。

4. 常用 AArch64 指令

指令 效果
mov x0, x1 复制寄存器
mov x0, #42 加载立即数
movz x0, #0x1234, lsl #16 零扩展移位后载入
movk x0, #0xabcd 保留移位后载入(部分更新)
ldr x0, [x1] 从 x1 所指地址加载 64 位
ldr x0, [x1, #8] 从 x1+8 加载
str x0, [x1, #8] 将 x0 存储到 x1+8
ldp x0, x1, [sp, #16] 加载一对寄存器
stp x29, x30, [sp, #-16]! 存储一对寄存器,sp 预减
add x0, x1, x2 x0 = x1 + x2
add x0, x1, #8 x0 = x1 + 8
sub x0, x1, x2 x0 = x1 - x2
mul x0, x1, x2 x0 = x1 * x2
sdiv x0, x1, x2 有符号除法
udiv x0, x1, x2 无符号除法
cmp x0, x1 为 x0 - x1 设置标志位
cbz x0, label 若 x0 == 0 则跳转
cbnz x0, label 若 x0 != 0 则跳转
bl func 带链接跳转(调用)
blr x0 带链接跳转到 x0 中的地址
ret 返回(跳转到 x30)
ret x0 返回到 x0 中的地址
adrp x0, symbol PC 相对页地址
add x0, x0, :lo12:symbol 符号偏移的低 12 位

5. 典型函数序言/尾声

// 非叶子函数
stp  x29, x30, [sp, #-32]!   // 保存 fp、lr;分配 32 字节
mov  x29, sp                  // 设置帧指针
stp  x19, x20, [sp, #16]     // 保存被调用者保存的寄存器
// ... 函数体 ...
ldp  x19, x20, [sp, #16]     // 恢复
ldp  x29, x30, [sp], #32     // 恢复 fp、lr;释放栈帧
ret

// 叶子函数(无调用,无需保存被调用者保存寄存器)
// AArch64 没有红区(red zone)
sub  sp, sp, #16             // 分配局部变量空间
// ... 函数体 ...
add  sp, sp, #16
ret

6. 内联汇编(GCC/Clang)

// 内存屏障
__asm__ volatile ("dmb ish" ::: "memory");

// 带获取语义的加载(load acquire)
static inline int load_acquire(volatile int *p) {
    int val;
    __asm__ volatile ("ldar %w0, %1" : "=r"(val) : "Q"(*p));
    return val;
}

// 带释放语义的存储(store release)
static inline void store_release(volatile int *p, int val) {
    __asm__ volatile ("stlr %w1, %0" : "=Q"(*p) : "r"(val));
}

// 读取系统计数器
static inline uint64_t read_cntvct(void) {
    uint64_t val;
    __asm__ volatile ("mrs %0, cntvct_el0" : "=r"(val));
    return val;
}

AArch64 特定约束:

  • "Q" — 适合独占/获取/释放指令的内存操作数
  • "r" — 任意通用寄存器
  • "w" — 任意 FP/SIMD 寄存器

7. NEON SIMD 内联函数

#include <arm_neon.h>

// 同时对 4 个浮点数求和
float32x4_t a = vld1q_f32(arr_a);   // 加载 4 个浮点数
float32x4_t b = vld1q_f32(arr_b);
float32x4_t c = vaddq_f32(a, b);
vst1q_f32(result, c);

// 水平求和
float32x4_t sum = vpaddq_f32(c, c);
sum = vpaddq_f32(sum, sum);
float total = vgetq_lane_f32(sum, 0);

命名约定:v<op><q>_<type>

  • q 后缀:128 位(quad)向量
  • _f32:float32,_s32:int32,_u8:uint8,等等

关于寄存器参考,请参阅 references/reference.md

相关技能

  • 使用 skills/low-level-programming/assembly-x86 了解 x86-64 汇编
  • 使用 skills/compilers/cross-gcc 了解交叉编译工具链
  • 使用 skills/debuggers/gdb 配合 gdbserver 调试 ARM 代码
Related skills

More from killvxk/low-level-dev-skills-zh

Installs
1
First Seen
Mar 21, 2026