ARM 汇编语言语法小解(2026实用速成版,重点 AArch64 / ARMv8-A)
ARM 汇编在2026年主流是 AArch64(64位,ARMv8-A 及以上),手机/服务器/苹果M系列/高通/麒麟/嵌入式高端基本都用这个。旧的 ARMv7(32位 AArch32)还在低端 MCU 用,但新项目 90%+ 选 AArch64。
我们用最简洁的结构过一遍语法核心,带例子、对比、常见坑。
1. 两种主流语法风格(必须先搞清楚!)
| 风格 | 叫法 | 寄存器写法 | 立即数前缀 | 主流汇编器 | 谁在用 | 推荐新手选哪个 |
|---|---|---|---|---|---|---|
| 统一汇编 | Unified Assembler Language (UAL) | x0 / w0 | # | gas (GNU as) 默认 | Linux内核、Android NDK、Clang | 新手首选 |
| 旧版/传统 | 分 legacy / thumb | r0 / r1 | # 或 无 | armasm (armcc) | Keil MDK、IAR、老嵌入式项目 | 少用 |
2026结论:写新代码统一用 GNU gas 语法(.s 文件默认),寄存器写 x0/w0,立即数加 #。
2. 基本语句结构(每行长这样)
[全局标签:] 指令助记符 操作数1, 操作数2, 操作数3 @ 注释
- 标签:以 : 结尾(如 main:),可省略
- 助记符:大写/小写都行(gas 不区分)
- 操作数:逗号分隔,顺序很重要
- 注释:@ 开头(gas 风格),或 //(现代 gas 支持)
例子(最经典 Hello World 骨架):
.section .text
.global _start @ 或 .globl main(Linux下常用 _start)
_start:
mov x0, #1 @ fd = 1 (stdout)
ldr x1, =msg @ 地址加载(伪指令)
mov x2, #13 @ 长度
mov x8, #64 @ syscall 号:write
svc #0 @ 软中断调用内核
mov x0, #0 @ 返回码
mov x8, #93 @ syscall 号:exit
svc #0
msg:
.ascii "Hello, ARM!\n"
3. 寄存器命名(AArch64 核心)
| 类型 | 数量 | 64位名字 | 32位名字 | 特殊用途(约定俗成) | 备注 |
|---|---|---|---|---|---|
| 通用寄存器 | 31 | x0~x30 | w0~w30 | x0~x7 参数/返回值,x8 间接结果,x16/x17 内联,x29 帧指针,x30 链接寄存器(lr) | x31 是零寄存器(XZR/WZR) |
| 栈指针 | 1 | sp | — | 栈指针 | 不能随便用作普通寄存器 |
| 程序计数器 | — | pc | — | 不可直接访问 | — |
小技巧:写代码时用 x 寄存器(64位),除非明确要 32 位运算才用 w。
4. 常见指令分类 & 语法例子(高频 Top 20)
| 类别 | 指令示例 | 语法格式 | 说明 / 常见坑 |
|---|---|---|---|
| 移动 | mov x0, #42 | mov Rd, #imm | 立即数 0~4095 直接 mov;更大用 movz/movk |
mov x1, x2 | mov Rd, Rn | 寄存器间移动(别用 add Rd, Rn, #0) | |
| 加减 | add x0, x1, x2 | add Rd, Rn, Rm / #imm | Rd = Rn + Rm / imm |
sub x3, x4, #100 | sub Rd, Rn, #imm12 | imm12 = 0~4095 | |
| 逻辑 | and x5, x6, #0xFF | and Rd, Rn, #imm | 位与 |
orr x7, xzr, x8, lsl #3 | orr Rd, Rn, Rm, shift | 带移位操作(lsl/asr/lsr/ror) | |
| 加载/存储 | ldr x9, [x10] | ldr Rt, [Rn] | 基址加载 |
ldr x11, [x12, #16] | ldr Rt, [Rn, #imm] | 偏移(pre/post/indexed 更多形式) | |
str w13, [sp, #-8]! | str Rt, [Rn, #imm]! | pre-decrement + writeback | |
| 地址计算 | adrp x14, label | adrp Rd, label | 页面地址(高21位) |
add x14, x14, :lo12:label | add Rd, Rd, :lo12:label | 补低12位 → PC相对寻址 | |
| 分支 | b label | b label | 无条件跳转 |
cbz x15, label | cbz Rt, label | 比较零跳转 | |
cmp x16, x17 + b.eq label | cmp Rn, Rm + 条件码 b.cond | 条件分支(eq/ne/gt/lt/ge/le/…) | |
| 系统调用 | mov x8, #93 + svc #0 | — | Linux syscall 号放 x8,参数 x0~x5 |
移位/扩展常见写法(ARM很强的一点):
add x0, x1, x2, lsl #4 @ x0 = x1 + (x2 << 4)
orr x3, xzr, w4, uxtb @ 零扩展 byte → 32位 → 64位
sxtw x5, w6 @ sign-extend word(32) to x(64)
5. 伪指令 / 汇编器指令(. 开头,高频)
| 伪指令 | 作用 | 示例 |
|---|---|---|
.global _start | 导出符号(链接器入口) | .global main |
.section .text | 代码段 | .section .data |
.ascii "str" | 字符串 | .asciz “Hello\0” (带\0) |
.byte / .word / .quad | 字节/字/8字节 | .quad 0x1234567890ABCDEF |
=value | 地址加载伪指令(ldr = ) | ldr x0, =0xDEADBEEF |
.align 4 | 对齐(常用于向量指令) | — |
6. 编译 & 运行快速上手(Linux / macOS / WSL)
# 汇编
aarch64-linux-gnu-as -o hello.o hello.s
# 链接(静态)
aarch64-linux-gnu-ld -o hello hello.o
# 或用 gcc 一步
aarch64-linux-gnu-gcc -nostdlib -static hello.s -o hello
# 运行(qemu 或真机)
qemu-aarch64 ./hello
macOS 本地(Apple Silicon 自带 aarch64):
as -o hello.o hello.s
ld -o hello hello.o -lSystem -syslibroot `xcrun -sdk macosx --show-sdk-path` -e _start -arch arm64
./hello
7. 常见新手坑 & 速记口诀
- 立即数别忘 # → mov x0, 42 错!必须 mov x0, #42
- ldr =label 是伪指令,不是真 ldr
- 条件码 在 cmp 后自动设置 NZCV 标志 → b.eq / b.ne / b.gt 等
- 栈操作 sp 必须 16 字节对齐(AArch64 规范)
- x31 永远是零寄存器,用 xzr / wzr 表示
一句话总结:
ARMv8-A (AArch64) 语法简洁、寄存器多、load/store 架构、移位/条件执行强大,但伪指令和寻址方式稍复杂。先从 mov/add/ldr/str/b 开始写 10 个小程序,内存图画出来,基本就入门了。
想继续深挖?可以告诉我具体方向:
- Hello World 完整版 + 调试(gdb / lldb)
- 函数调用 / 栈帧 / 参数传递(AAPCS64)
- NEON / SVE 向量指令入门
- 与 C 内联汇编混合写法
贴代码或问题,我继续帮你拆!