资讯详情

资讯详情

A64指令集手册:arm64汇编的寄存器、寻址与避坑指南

简介面向 Armv8-A 架构开发者的 AArch64 汇编完整参考手册以 PDF 格式收录 Arm 官方 A64 Instruction Set for A-profile architecture 文档覆盖基础算术、逻辑、访存与分支指令寄存器组织、寻址模式、条件标志和指令编码格式涵盖加载/存储、整数与浮点运算、位操作、系统控制等分类每条指令均给出编码、语法与操作伪代码并深入讲解 SIMD、可伸缩向量扩展 SVE 等高级特性。资源共 1 个 PDF 文件压缩包约 9.7MB为原版英文技术文档便于离线查阅、按章节检索指令语义与异常处理机制。已有 2978 人学习下载。适合嵌入式开发、系统底层优化、移动端与服务器迁移场景中的开发者以及希望系统掌握 64 位 ARM 汇编的读者使用文档还涉及异常级别管理、二进制兼容、ABI 与工具链支持可帮助理解从常规计算到高性能并行应用的完整指令生态。1. 这份 A64 指令集手册arm64 开发者的案头基准干 arm64 汇编的人多少都有过这种尴尬网上教程把一条指令讲得头头是道真到反汇编核对时发现连编码格式都和权威手册对不上。aarch64 汇编指令的最终出处只有一份——面向 A-profile 架构的 A64 指令集手册也就是常被简称为 DDI 0596 的那本。它按字母序把从 ADC 到 LD64B 的全部基础指令摊开每条都带编码格式、别名关系、flag 影响和异常等级约束SIMD、SVE、RME、SME 这些扩展也都有对应条目。适合三类人读内核启动代码的人、写裸机程序的人、以及被隐晦崩溃折腾到失眠的逆向工程师。它解决的核心问题很简单你缺的不是「某条指令大概什么意思」而是一份能一字一句校验的权威底稿。2. 寄存器与基础指令X0-X30 的组织方式与条件标志传递规则2.1 寄存器组织的三条铁律AArch64 的通用寄存器一共 31 个编号 X0 到 X30X31 这个位置被拆成了两个身份上下文里当作基址或栈操作时它是 SP当作普通操作数时它是 ZR零寄存器。这个设计第一次见面会有点绕但优点是省了一条指令编码位——mov w0, wzr就是最常见的清零操作汇编器内部把它翻译成ORR w0, wzr, wzr并没有独立的 mov 编码。第二条铁律是 X30 承担链接寄存器 LR 的角色。BL指令把返回地址写进 X30叶子函数可以直接ret非叶子函数第一件事就是把 X30 压栈。这跟 x86 的 Call 指令把返回地址压栈完全不同写 A64 汇编时最容易忘的就是这个。第三条铁律是 W 寄存器规则W0 到 W30 是 X0 到 X30 的低 32 位视图但写 W 寄存器会同时把 X 寄存器的高 32 位清零。这是零扩展不是符号扩展。我见过有人从 AArch32 迁过来习惯性以为写 W0 只动低 32 位结果后续用 X0 做 64 位地址时拿到的全是清零后的值排查半天才发现是这条规则在起作用。寄存器类别编号范围位数职责通用寄存器X0-X30 / W0-W3064 / 32整数运算、地址、返回值X0栈指针SP64栈操作专用零寄存器ZR / WZR64 / 32读为 0写被丢弃链接寄存器X30LR64保存子程序返回地址程序计数器PC64不可直接读写只能通过 ADR 间接取向量寄存器V0-V31128SIMD、浮点、SVE 数据通路条件标志NZCV4PSTATE 中的标志位域条件标志是另一条重要规则。A64 里绝大多数基础指令默认不写标志ADD、SUB不会动 NZCV只有ADDS、SUBS、ANDS这类带 S 后缀的变体才更新标志CMP和CMN分别是SUBS、ADDS的别名。这让编译器少了很多依赖旧标志值的隐式约束也让手写汇编里的标志依赖全部显式化——查 bug 的时候不用猜上一条指令到底有没有碰 C 标志。2.2 算术逻辑指令立即数范围、别名关系与标志位显式化A64 的ADD、SUB、AND、EOR、ORR基本都有两种形式立即数形式和移位寄存器形式。立即数不是随意取的它是 12 位无符号数可选左移 0 位或 12 位。add x0, x0, #0x1000合法add x0, x0, #0x1FFF不合法因为 0x1FFF 超过 12 位必须先movz再add或者用mov的扩展形式。写汇编时碰到「立即数无法编码」的报错先别怀疑汇编器大概率就是这个上限撞上了。下面这段代码计算 1 到 N 的整数和N 通过 w0 传入结果通过 w0 返回完整演示了ADDS家族和条件分支的配合// 计算 12...N 并返回N 传入 w0 .globl sum_to_n sum_to_n: mov w1, wzr // w1 0累加器 mov w2, wzr // w2 0计数器 .loop: cmp w2, w0 // 比较计数器与 N等价于 SUBS w2, w2, w0 的别名 b.ge .done // 若 w2 w0 则结束 add w1, w1, w2 // 累加w1 w2 add w2, w2, #1 // 计数w2注意这里不置标志 b .loop .done: mov w0, w1 // 返回值放入 w0 ret逻辑说明cmp w2, w0内部是SUBS只更新 NZCV 标志不写目的寄存器b.ge读取 N 标志和 V 标志的异或结果判断有符号大于等于。两个add都不带 S 后缀所以循环体不会意外污染标志这是 A64 设计上比 AArch32 干净的地方。参数说明mov w1, wzr里的wzr就是 WZR 零寄存器写成mov w1, #0也可以但wzr写法在汇编器里更常见b.ge的.loop是局部标签gas 里用.loop这种点开头的标签可以避免和其他文件的符号冲突。再看移位寄存器形式lsl x0, x1, #3其实是LSLV的别名asr、lsr、ror同理。这在手册里标得很清楚查指令时看到「an alias of」字段说明该助记符没有独立编码它是某个更底层的指令经移位修饰后的展示形式。理解这一点读 objdump 反汇编输出时会少很多困惑——你写的是cmp反汇编出来却可能是subs不是工具坏了是别名机制在工作。3. 加载存储与寻址模式LDR/STR 的四种变址与原子操作边界3.1 四种寻址形式速查表与 literal 加载的隐藏开销A64 的普通访存指令有四种寻址形式无符号立即数偏移、寄存器偏移、预索引、后索引。它们解决不同的场景写内核代码时四种都会碰到。寻址形式语法示例行为典型场景立即数偏移ldr x0, [x1, #8]从 x18 处加载x1 不变访问结构体字段寄存器偏移ldr x0, [x1, x2]从 x1x2 处加载x2 可为 W 零扩展数组索引预索引ldr x0, [x1, #8]!先 x1 8再加载栈帧分配后立即使用后索引ldr x0, [x1], #8先加载再 x1 8游标遍历缓冲区注意 A64 没有 x86 那种隐式地址回写所有要更新基址的行为都必须显式写!或选择后索引形式。我见过有人在ldr x0, [x1, #16]之后直接复用 x1 取下一个字段拿到的一直是同一地址的数据这就是没搞清立即数偏移形式根本不动基址寄存器。literal 加载是另一个常见的坑。ldr x0, 0x1234567890ABCDEF0这种写法是汇编器提供的伪指令gas 遇到它有两种处理路径一是生成一条 PC-relative literal 加载数据放在当前函数的 literal pool 里二是展开成movzmovk序列。literal pool 的 PC-relative 范围是 ±1MB如果数据被放到池外或超出范围链接器会报 relocation truncated这时候我一般会手动展开成movzmovk// 把一个 64 位常量拆成四条 mov 指令避免 literal pool 依赖 movz x0, #0xDEF0 movk x0, #0xBC9A, lsl #16 movk x0, #0x7856, lsl #32 movk x0, #0x1234, lsl #48逻辑说明movz把立即数写入寄存器并清零其余位movk把立即数插入指定位置而保留其他位通过 lsl 移位量组合出任意 64 位常量。参数说明立即数范围是 0 到 65535超出就需要拆到下一轮 movklsl 的合法取值是 0、16、32、48。如果你看到编译产物里有连续四条 mov不要奇怪这是编译器在避免 literal 池访问。3.2 独占访存与内存屏障从 LDAXR/STLXR 到 DMB/DSB/ISBA64 的原子操作不是 x86 那种 lock 前缀而是独占访问协议。LDXR标记独占监视STXR尝试写回成功返回 0失败返回非 0。加上 Acquire 语义的LDAXR和 Release 语义的STLXR就构成了完整的锁原语。下面的自旋锁是 arm64 内核里最常见的小实现// 简单自旋锁0 表示空闲1 表示占用x0 为锁地址 spin_lock: mov w1, #1 1: ldxrb w2, [x0] // 独占读入旧值 cbnz w2, 1b // 非 0 说明被占用重试 stxrb w3, w1, [x0] // 尝试写入 1 cbnz w3, 1b // 写入冲突重试 ret逻辑说明ldxrb到stxrb之间构成一个独占事务任何其他处理器或中断干扰都会让stxrb失败返回非 0从而跳回重试。cbnz是「非零则跳转」1b 表示向后跳转到标签 1。参数说明stxrb第一个目的寄存器 w3 接收是否成功的标志第二个是源数据第三个是内存地址。这里有个重要约束独占访问之间不能穿插过多代码否则本地监视器可能因为事件而失效导致锁永远获取不到——这属于写出来的最简单、调起来最玄学的场景。内存屏障三兄弟在这个场景里必须分清楚。DMB只保证访问的排序不等待其他操作完成DSB会等待屏障之前的所有显式访存完成ISB冲刷流水线让已经取到的指令全部作废。写驱动时我一般只在需要上下文切换语义时用DSB普通外设寄存器访问DMB就够。手册里CLREX负责清独占监视CAS、CASP这类比较交换指令属于 v8.1-Atomics 扩展不是所有核心都支持。另外手册里还列了LD64B这种单拷贝 64 字节原子加载以及LDADD、LDCLR、LDEOR这些原子读改写指令用之前一定要查目标 CPU 的特性寄存器 ID_AA64ISAR0_EL1确认到位了再上真机否则就是非法指令。4. 数据并行与安全扩展SIMD、SVE 以及 RME/SME 的标注边界4.1 向量寄存器与 SIMD 指令后缀语法决定一切AArch64 的 SIMD 数据通路是 32 个 128 位向量寄存器 V0-V31。一个 V 寄存器可以被看作不同元素宽度的数组16 个字节、8 个半字、4 个单字或 2 个双字。指令后缀直接决定解释方式v0.4s表示把 v0 当成 4 个 32 位单精度通道v0.2d表示 2 个 64 位双精度通道。后缀写错不会报错只是结果完全不可读这是 SIMD 汇编最典型的踩坑来源。手册里浮点相关指令也在这章FDIV、FMLA、FADD、FMUL等。FMLA是乘加指令fmul v3.4s, v0.4s, v1.4s完成四路并行乘法fadd v3.4s, v3.4s, v2.4s再做加法。下面是两个数组的逐元素乘加示例// 计算 c[i] a[i] * b[i] c[i]每次处理 4 个单精度元素 // x0 a 地址x1 b 地址x2 c 地址 loop: ld1 {v0.4s}, [x0], #16 // 载入 4 个 float地址后移 16 字节 ld1 {v1.4s}, [x1], #16 ld1 {v2.4s}, [x2] fmul v3.4s, v0.4s, v1.4s // 四路并行乘法 fadd v2.4s, v2.4s, v3.4s // 累加 st1 {v2.4s}, [x2], #16 // 写回并后移 subs x3, x3, #4 // 剩余元素计数减 4 b.gt loop逻辑说明ld1是 SIMD 载入指令花括号里的{v0.4s}表示向量寄存器组后索引[x0], #16每轮自动把地址推进 16 字节正好是 4 个 float 的体积。参数说明subs带 S 后缀更新标志b.gt根据 N、Z、C、V 的组合判断大于继续循环。SVE 出现之前这是 arm64 上做数据并行的主力写法但现在新平台我更推荐直接考虑 SVE。4.2 SVE 可伸缩向量与 MTE 标签指令先查特性位再用SVE 的核心思想是向量长度由实现决定而不是由指令集写死。手册里的说法是向量长度可以在 128 到 2048 位之间按实现取定程序通过读 VL 相关寄存器获知实际值。这意味着同样一份二进制在支持 512 位向量的 CPU 上会一次处理更多数据不用重编译。SVE 的谓词寄存器 p0-p15 控制每个通道是否参与运算WHILELO生成循环谓词// SVE 向量加法z0 z1 z2处理 x0 指定数量的 32 位元素 whilelo p0.s, xzr, x0 // 生成谓词通道编号小于 x0 的位置为真 loop: ld1w z0.s, p0/z, [x1] // 谓词控制的载入0 通道清零 ld1w z1.s, p0/z, [x2] add z0.s, z0.s, z1.s // 向量加法 st1w z0.s, p0/z, [x1] incw x1, p0.s // 地址按活性通道数推进 incw x2, p0.s whilelo p0.s, x1, x0 // 重新生成谓词剩余通道继续 b.ne loop逻辑说明whilelo比较两个通用寄存器的值生成一个每通道一比特的谓词掩码ld1w后的/z表示 zeroing未激活通道读成 0还有/m表示 merging 保留旧值。参数说明p0.s里的.s是通道描述符对应 32 位元素宽度incw把通用寄存器按当前激活通道数递增而不是固定 4这正是可伸缩向量和固定宽度 SIMD 的本质区别。手册里还有一类和内存安全相关的指令比如ADDG、LDG、STG、GMI、IRG它们属于内存标签扩展MTE体系用来做内存越界和释放后使用的硬件检测。这类指令同样不是所有核心都有。另外手册把 RME、SME 这类较新特性明确标为 Beta 质量甚至有个性能监视器接口标为 Alpha——意思是规范里大部分内容已经确定但细节可能还有调整。我一般看到标注 Beta 的条目会先去查 CPU 的 ID_AA64PFR0_EL1 和 ID_AA64SMFR0_EL1 特征位确认支持再写进代码不留遗憾。5. 避坑指南查手册与写汇编时最常见的五个翻车点5.1 别名指令找不到CMP 其实没有独立编码现象用 objdump 反汇编自己写的汇编程序发现里面根本没有CMP、CSET、CMN这些助记符全是SUBS、CSINC、ADDS。原因手册里明确写着CMP是SUBS的别名CMN是ADDS的别名CSET是CSINC的别名。这些助记符为了可读性存在但编码层没有自己的位模式。反汇编器默认展示真名于是你写的cmp w2, w0变成subs wzr, w2, w0。解决查手册时看到指令条目里有「an alias of」字样立即意识到它在真名底下。调试时如果找不到某条指令试试它的别名真名这能省下大量搜索时间。5.2 literal 池溢出PC-relative 距离超过 ±1MB现象编译链接时报relocation truncated to fit指向一条ldr x0, ...伪指令。原因ldrliteral 形式编码里的立即数是 19 位带符号偏移范围只有 ±1MB。当伪指令对应的常量被放在 literal pool 里且距离超过这个范围链接器直接拒绝。解决换成movzmovk组合手动构造常量或者用adrp 加偏移的方式取符号地址。adrp按 4KB 页对齐取地址配合:lo12:重定位可覆盖整个寻址空间adrp x0, my_data // 取 my_data 所在页的基地址 add x0, x0, :lo12:my_data // 加上页内偏移5.3 AArch32 习惯迁移失败LDM/STM 在 A64 不存在现象把老的 arm32 汇编代码原样搬过来LDMIA、STMDB全部报错「instruction not supported」。原因A64 没有一次加载/存储多个寄存器的指令只有LDP/STP一次处理两个寄存器而且要求成对。解决改用LDP/STP。函数入口保存 LR 和 FP 的典型写法是stp x29, x30, [sp, #-16]! // 先减栈再存储 add x29, sp, #0 // 建立帧指针5.4 标志位玄学ADD 不置位读 C 前必须用 ADDS现象做无符号加法溢出判断写完add x0, x0, x1后紧跟b.cs溢出分支结果从来没跳进去。原因A64 里ADD不更新 NZCVb.cs读取的 C 标志是上一条会写标志的指令留下的旧值溢出信号被吞了。解决改用ADDS x0, x0, x1再b.cs判断进位。带 S 后缀的版本才写标志。同理要判断借位就用SUBS。5.5 新扩展指令跑出 UNDEF特性位没查就上真机现象在较老的 v8.0 平台上跑CASP、SVE 或 MTE 相关指令直接触发未定义指令异常程序秒挂。原因这些指令属于后续扩展老核心的译码器根本不认识。手册里虽然按字母序列出了它们但并不代表每个核都支持。解决先用一个简单程序读取ID_AA64ISAR0_EL1检查 Atomics 字段、读ID_AA64PFR0_EL1检查 SVE 字段确认特性位后再使用对应指令。汇编阶段也要对版本做约束gas 里写.arch armv8.2-asve能保证指令集匹配。6. 进阶用法用编码表和反汇编器互证把手册当调试器用6.1 一条指令从手册到机器码的闭环验证手册每条指令的 Encoding 章节都给出了位模式比如 ADD (immediate) 的布局就能在「sf 00 100010 shift imm12 Rn Rd」里找到。不过手工按位拼编码容易出错更好的做法是让工具生成机器码再拿手册核对形成闭环。我一般用 LLVM 的集成汇编器做这件事printf add x0, x1, #0x10\n | llvm-mc -tripleaarch64 -show-encoding -show-inst逻辑说明-show-encoding输出每条指令的机器码字节-show-inst输出解析后的指令表示。得到字节序列后把手册中该指令的编码片段和输出对照imm12 字段、Rn 字段、Rd 字段是否符合预期。反过来也可以做反汇编验证printf 0x91004020\n | llvm-mc -tripleaarch64 -disassemble参数说明这里的字节串可以用上面汇编得到的实际输出替换-disassemble会把机器码还原成助记符。如果还原出的指令和你写的完全一致说明你对编码结构的理解没有偏差。这个方法排查「为什么这条指令行为不对」特别有效——先把指令从二进制里还原出来看它到底是不是你以为的那条。查指令时还有一个小技巧手册里每条指令末尾都有 See also 字段指向相关的其他条目。顺着CMP的别名链跳到SUBS再顺SUBS跳到ADDS和ADD你会发现同一族指令的编码差异就在一个 S 位上。以手册为准、工具为辅这个习惯能避免大多数汇编层的低级失误。从那以后我每接触一个新的 arm64 平台都会强制走一遍「查编码、写汇编、反汇编核对」的循环希本文还有配套的精品资源点击获取
觉得有用,分享给同行:

为您的企业打造数字门面

稳重轻奢商务风格,端正雅致视觉,长效耐看不易过时。

立即咨询 →