ARM体系现在已经有9个版本了,不同体系指令集架构(ISA)和ABI有区别,但一般是向前兼容的,本文记录ARMv6和ARMv8,前者为32位,而后者扩充到了64位,并且兼容32位,它使用AArch64和AArch32两个执行状态来分别运行相应的指令集(A64|A32/T32)。本文将由v6开始扩展到v8。下面显示32位:
ARMv6 32位
工作状态
Arm状态和Thumb状态,前者使用4字节定长指令,后者使用2字节定长指令,重置或异常时恢复为ARM状态,可通过BX/BLX指令转换状态,当目标地址的低位为1时表示转换到thumb状态,否则为arm状态,返回时根据LR恢复状态,在插桩时特别要小心这一点,可通过指令长度确定目标片段的代码类型。
工作模式
这里的工作模式是指当前运行的级别,在ARMv6下有如下七种:
| Mode | ModeCode | 说明 |
|---|---|---|
| User | 10000 | 用户模式,用户进程执行时处于该模式 |
| System | 11111 | 普通特权模式,如通常的系统内核执行时会进入该模式 |
| Supervisor | 10011 | 重置时的模式,软中断与系统调用(swi指令)时也进入该模式 |
| Undefined | 11011 | 获取到不能识别的指令时进入该模式,此时用户可通过软件仿真来模拟自定义的指令(类似中断服务例程) |
| IRQ | 10010 | 普通中断模式,一般硬件中断时会进入 |
| FIQ | 10001 | 快速中断模式,处理紧急中断/高速数据处理时 |
| Abort | 10111 | 访问异常触发保护机制时(如segment fault)会进入该模式 |
(在后来的版本中又增加了3种之后再补充。)
寄存器
一般只提Arm有37个可见的寄存器,但在某一时刻只有部分寄存器能使用,如下所示不同的模式可见的寄存器差不多,但是对应的物理寄存器不一定一致,例如User和Supervisor模式下的R0-R12指向同样的物理寄存器,而R13和R14在两种模式下实际对应的物理寄存器并不一致,所以一般我们在代码中只能看到10多个寄存器,而实际上有30多个:
| User | System | Supervisor | Abort | Undefined | IRQ | FIQ | 说明 |
|---|---|---|---|---|---|---|---|
| R0 | R0 | R0 | R0 | R0 | R0 | R0 | 这一组寄存器在所有模式下指向相同的物理寄存器,其中R0-R3通常用于传参,R0-R1通常用于传输返回值 |
| R1 | R1 | R1 | R1 | R1 | R1 | R1 | |
| R2 | R2 | R2 | R2 | R2 | R2 | R2 | |
| R3 | R3 | R3 | R3 | R3 | R3 | R3 | |
| R4 | R4 | R4 | R4 | R4 | R4 | R4 | |
| R5 | R5 | R5 | R5 | R5 | R5 | R5 | |
| R6 | R6 | R6 | R6 | R6 | R6 | R6 | |
| R7 | R7 | R7 | R7 | R7 | R7 | R7 | |
| R8 | R8 | R8 | R8 | R8 | R8 | R8_fiq | 这三个寄存器和R11,R12在thumb下不可见 |
| R9 | R9 | R9 | R9 | R9 | R9 | R9_fiq | |
| R10 | R10 | R10 | R10 | R10 | R10 | R10_fiq | |
| R11 | R11 | R11 | R11 | R11 | R11 | R11_fiq | 又叫FP(Frame Pointer)寄存器,通常用于指向栈桢 |
| R12 | R12 | R12 | R12 | R12 | R12 | R12_fiq | 又名IP(Intra Procedure call scratch)寄存器,它不会被保存也默认数据不可用,因此可在过程中直接写读 |
| R13 | R13 | R13_svc | R13_abt | R13_und | R13_irq | R13_fiq | 又名SP(Stack Pointer)寄存器,保存了栈顶指针 |
| R14 | R14 | R14_svc | R14_abt | R14_und | R14_irq | R14_fiq | 又名LR(Linked Register),保存了函数返回地址 |
| R15 | R15 | R15 | R15 | R15 | R15 | R15 | 又名PC(Program Counter)寄存器,在计组中它可以保存当前指令位置也可以保存下一指令位置,在3级流水线的ARM系列中(据传未验证)保存了下一条要读取的指令的地址,它的值是当前正在执行的指令的地址+8 |
| CPSR | CPSR | CPSR | CPSR | CPSR | CPSR | CPSR | Current Program State Register保存当前程序的状态 |
| SPSR_svc | SPSR_abt | SPSR_und | SPSR_irq | SPSR_fiq | Stored Program State Register用于备份CPSR,如出现异常时就会保存并在异常处理完后恢复 |
在这里面CPSR也是按位表示一些状态,它的结构如下(新版会使用更多位):
| Bit | 31 | 30 | 29 | 28 | --- | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| N | Z | C | V | --- | I | F | T | M4 | M3 | M2 | M1 | M0 | |
| Description | Negative | Zero | Carry | oVerflow | IRQ | FIQ | Thumb | Work Mode | |||||
一般在指令集手册里它们叫ArmRegister,除此之外还有很多不可见的寄存器(我猜的)和协处理器,协处理器用于处理特殊任务,如多媒体任务,算数任务,加解密任务等,它只会执行和它功能相关的指令而忽略其他指令。
指令
本段记录的是ARM指令,Thumb指令以后有需要再补充。ARM指令有如下特点:
- 几乎所有指令都有4位共16种条件标志位,当使用时表示满足条件才执行指令,在汇编上以两个字符的助记符表示
- 很多指令助记符通过S表示该操作会影响程序状态字
注:下面的翻译并不一定正确,我只是为了好记!
| 编码 | 条件助记符 | 标志位 | 含义 |
|---|---|---|---|
| 0000 | EQ | Z=1 | 相等 |
| 0001 | NE | Z=0 | 不相等 |
| 0010 | CS | C=1 | 无符号大于或等于 |
| 0011 | CC | C=0 | 无符号小于 |
| 0100 | MI | N=1 | 负值 |
| 0101 | PL | N=0 | 正值或 0 |
| 0110 | VS | V=1 | 溢出 |
| 0111 | VC | V=0 | 无溢出 |
| 1000 | HI | C=1 且 Z=0 | 无符号大于 |
| 1001 | LS | C=0 且 Z=1 | 无符号小于或等于 |
| 1010 | GE | N 和 V 相同 | 有符号大于或等于 |
| 1011 | LT | N 和 V 不相同 | 有符号小于 |
| 1100 | GT | Z=0 且 N 等于 V | 有符号大于 |
| 1101 | LE | Z=1 且 N 不等于 V | 有符号小于或等于 |
| 1110 | AL | 忽略 | 无条件执行(默认可省略) |
| 1111 | NV | 忽略 | 从不执行(系统保留) |
| 类型 | 指令 | 助记 | 说明 | 例子 |
|---|---|---|---|---|
| 跳转指令 | B/BL/BX/BLX | Branch{Link}{eXchange} | 跳转到其他位置,带L将把返回地址放到LR,带X会根据目标切换状态 | BLEQ main;Z标志置位时调用main函数 |
| 数据处理指令 | MOV/MVN | 数据传输指令与取反传输 | ||
| CMP/CVN/TST/TEQ | {算数比较|反值比较|逻辑位测|逻辑位相等} | |||
| ADD/ADC/SUB/SBC/RSB/RSC | 加减运算,C表示是否带进位,R表示逆向 | |||
| MUL/MULA/{S|U}MULL/{S|U}MUAL | 乘法与乘加指令,所谓乘加就是先乘再加,开头加S或U表示是否有符号,结束加L表示64位 | |||
| {L|A}S{L|R}/ROR/RRX | {逻辑|算数}{左|右}移,循环右移,带扩展的循环右移 | |||
| AND/ORR/EOR/BIC | 逻辑{与|或|异或|位清除} | |||
| PSR处理指令 | MSR/MRS | Move State from Register|... | 可通过该指令读写状态寄存器 | MSR CPSR, R0; 将R0的值赋给CPSR |
| 加载/存储指令 | {LD|ST}R{|B|H} | {LoaD|STore} Register{Doword|Byte|sHort} | 从加载一个{双字|字节|字}到寄存器或相反 | LDR R0, [SP]; R0=[SP] |
| {LD|ST}M | {LoaD|STore}Multi | 从寄存器组存储数据到内存或反向操作 | ||
| 协处理器指令 | CDP | Coprocessor Data Operation | 让指定编码的协处理器执行指定的指令 | CDP P3, 2, C12, C10, C3, 4; 让协处理器3执行2号指令,后面是它的源目的寄存器等参数 |
| LDC/STC | {LoaD|STore} Coprocessor | 让协处理器指定寄存器从指定内存加载/存储数据 | LDC P15, C4, [R0]; 从R0所指位置加载一个dword的数据到15号协处理器的C4寄存器 | |
| MCR/MRC | Move Coprocessor from Register|... | 协处理器与ARM寄存器间传送数据 | MCR P3, 3, R0, C4, C5, 6; 将R0的内容传送到P3的C4 C5中 | |
| 异常产生指令 | SWI | SoftWare Interrupt | 产生软中断,用于系统调用 | SWI 2; 调用编号为2的系统例程 |
| BKPT | BreaKPoinT | 断点 | BKPT 0x2233; |
寻址方式
1.立即数寻址:
ADD R0, R0, #0x10 ; R0=R0+0x10,立即数以#开头
2.寄存器寻址:
ADD R0, R1, R2 ; R0=R1+R2
3.寄存器间接寻址:
ADD R0, R1, [R2] ; R0=R1+[R2]
4.基址变址寻址:
ADD R0, [R1, #4] ; R0=[R1+4]
ADD R0, [R1, #4]! ; R0=[R1+4],R1+=4
ADD R0, [R1], #4 ; R0=[R1], R1+=4
ADD R0, [R1, R2] ; R0=[R1+R2]
5.多寄存器寻址:
LDMIA R0, {R1, R2, R3, R4} ; R1=[R0],R2=[R0+4]...
这里的后缀有如下四种,用于表示每执行一个寄存器复制操作后原地址如何变化:
| mode | Full Name | 说明 |
|---|---|---|
| IA | Increase After | 先传送再加 |
| DA | Decrease After | 先传送再减 |
| IB | Increase Before | 先加再传送 |
| DB | Decrease Before | 先减再传送 |
6.相对寻址:
BL main ; call main
7.堆栈寻址:
LDMFD SP!, {R1, R2, R3}; R1=[SP], SP-=4, R2=[SP], SP-=4, R3=[SP], SP-=4
这里的后缀有如下四种,用于表示当前栈指针所指位置是否存放了元素(如若指向的是最后压入的元素则为满栈)以及栈的增长方向(如递减表示由高地址到低地址增长):
| mode | Full Name | 说明 |
|---|---|---|
| FA | Full Ascending | 满递增栈 |
| FD | Full Descending | 满递减栈 |
| EA | Empty Ascending | 空递增栈 |
| ED | Empty Descending | 空递减栈 |
伪指令
伪指令是针对汇编器的,又汇编器转换成某特定结构,因此它大多和架构无关(但也有.thumb这种是特定于架构的),比如在GCC上那些伪指令在ARM上也能用,因此这里只记录一些在在IDA里常看到的:
| 类型 | 伪指令 | 助记 | 说明 | 例子 |
|---|---|---|---|---|
| 符号定义伪指令 | GBL<A|L|S> | GloBaL<Arithmetic|Logical|String> var | 定义全局<数字|逻辑|字符串>变量,默认值分别为<0|False|""> | GBLA x;定义一个全局数字变量x它的值为0 |
| LCL<A|L|S} | LoCaL<Arithmetic|Logical|String> var | 定义本地<数字|逻辑|字符串}变量,默认值分别为<0|False|""> | LCLA x;定义一个局部数字变量x它的值为0 | |
| SET<A|L|S} | SET<Arithmetic|Logical|String> var | 为<数字|逻辑|字符串>变量赋值 | SETA x 233;x=233 | |
| RLIST | Register LIST | 将某个寄存器列表定义为一个变量 | r0_5 RLIST {R0-R5};另R0-R5这个寄存器列表为r0_5 | |
| 数据定义伪指令 | DC<B|W|D|FD|FS|Q>{U} | DataContent<Byte|Word|Dowrd |FloatSingle|FloatDouble|Qword>{Unpad} | 定义各种长度的数据,U表示不对齐 | Str DCB "B3taMa0"; |
| SPACE | 定义连续的一片空间,初始化为0 | somespace Sapce 100;定义somespace为100字节长度的空间并初始为0 | ||
| MAP | 申明一个数据结构 | MAP 0x10, R0; 在R0+0x10处申明一个结构,之后用Field指令定义结构域 | ||
| FIELD | 定义结构里的域 | vtbl FIELD 0x04; 定义一个域名为vtbl长度为4字节 |
调用约定
在ARM中还有个概念易于和ISA混淆,且和ISA密切相关,那就是ABI,ARM支持多种ABI,移动和嵌入式上常见的是EABI,不过这不重要,我们需要关注的是ABI里的调用约定,这里面常用的调用约定是AAPCS(Procedure Call Standard for the Arm Architecture)和AAPCS64,它们分别对应32位和64位,这里介绍前者,在官方文档的调用约定里它的字表示32字节,太奇怪了,它应该叫双字!!!它的寄存器使用如下:
| Register | Synonym | Special | 说明 |
|---|---|---|---|
| r15 | PC | The Program Counter. | |
| r14 | LR | The Link Register. | |
| r13 | SP | The Stack Pointer. | |
| r12 | IP | The Intra-Procedure-call scratch register. | |
| r11 | v8 | FP | Frame Pointer or Variable-register 8. |
| r10 | v7 | 同v1-v5 | |
| r9 | v6 SB TR |
特定于平台,可做不同用途,如在PIC中当SB(Static Base),在TLS中当TR(Thread Register),也可以把它当作v6用。 | |
| r4-r8 | v1-v5 | 作为本地变量,需要函数执行前保存结束前恢复。 | |
| r0-r3 | a1-a4 | 依次传递参数,若参数大于32位可占用多个连续的寄存器,若4个寄存器不够用则通过栈传输,方向为从右到左入栈;也可保存返回值,返回值为32位保存在r0里,与参数类似若返回值大于32位依次存放 |
而它的栈使用满递减栈,使用时必须4字节对齐,关于字节序arm似乎可以采取大端和小端但默认是小端,根据官方文档可通过一个配置引脚修改,这是ABI的内容,现在一般遇到的其实都是小端序。
ARMv8 64位
ARMv8引进了很多新功能,这里只关注在aarch64下指令集的变化,此时它提供了31个64位通用寄存器X0~X30,当然若要使用32位也可用W0~W30来只取低32位(写入时高32位置零),
模式
ARMv8-A架构定义了两种主要的执行状态:AArch64和AArch32。 * AArch64: 64位执行状态,使用64位通用寄存器和A64指令集。 * AArch32: 32位执行状态,以向后兼容ARMv7-A。它使用32位通用寄存器并支持A32和T32(Thumb)指令集。
在AArch64状态下,架构引入了异常级别(Exception Levels, ELs)的概念来取代之前的工作模式。特权级别从EL0到EL3,数字越大,特权越高:
| 级别 | 名称 | 说明 |
|---|---|---|
| EL0 | User | 用户模式,用于运行普通应用程序。 |
| EL1 | Kernel | 操作系统内核模式,通常的中断会在此处理。 |
| EL2 | Hypervisor | 虚拟机监视器模式,为虚拟化提供支持。 |
| EL3 | Secure Monitor | 安全监视器模式,是最高权限级别,用于安全启动和在安全/非安全世界之间切换。 |
寄存器
AArch64提供了31个64位通用寄存器(X0-X30),以及一个根据上下文可以是堆栈指针(SP)或零寄存器(XZR)的特殊寄存器。当使用W0-W30访问时,表示只使用这些寄存器的低32位,下面是简要描述:
- 通用寄存器: X0-X30,X0-X7通常用于传递参数和返回值,当使用W0-W30访问时,表示只访问这些寄存器的低32位,并且写入W寄存器时会将高32位清零。
- 堆栈指针 (SP): SP寄存器,用于指向当前栈顶,存在4个,在不同的异常级别下可以使用不同的SP,如SP_EL0, SP_EL1。
- 程序计数器 (PC): 保存下一条要执行的指令的地址,不能直接访问。
- 链接寄存器 (LR): 即X30,保存函数返回地址。
- 帧指针 (FP): 即X29,通常用于指向栈帧。
- SIMD和浮点寄存器: 32个128位寄存器(V0-V31),可用于不同的精度(如D0-D31用于64位,S0-S31用于32位)。
- PSTATE: 包含条件标志(N, Z, C, V可在EL0下访问)和其他状态位。
- 零寄存器 (XZR/WZR): XZR/WZR是一个特殊的寄存器,读取时始终为0,写入时丢弃写入的值。
| 寄存器 | 说明 |
|---|---|
| X0-X7 | 用于传递参数和返回值。如果参数或返回值超过64位,会使用更多的寄存器。 |
| X8 | 通常用作间接结果位置寄存器(Indirect Result Location Register)。 |
| X9-X15 | 调用者保存的临时寄存器。 |
| X16-X17 | 过程内调用临时寄存器(IP0, IP1),可被链接器修改。 |
| X18 | 平台寄存器,特定于平台使用,否则为临时寄存器。 |
| X19-X28 | 被调用者保存的寄存器,函数必须在返回前恢复它们的值。 |
| X29 | 帧指针 (FP, Frame Pointer)。 |
| X30 | 链接寄存器 (LR, Link Register),保存函数返回地址。 |
| SP | 堆栈指针 (Stack Pointer)。 |
| PC | 程序计数器 (Program Counter)。 |
| PSTATE | 程序状态寄存器,包含 N, Z, C, V 等条件标志。 |
| V0-V31 | 128位向量/浮点寄存器,用于SIMD(Neon)和浮点运算。 |
特殊寄存器基本都是特权的,就不在这里提了,以后分析操作系统(中断/内存管理等)时再详细说明吧~
指令
AArch64使用A64指令集,这是一个定长的32位指令集。与ARMv6的主要区别在于条件执行的方式,A64不再对几乎所有指令提供条件执行选项,而是通过条件选择指令(如CSEL)来实现,条件标志位的含义保持不变。一些关键特性包括: * 64位能力: 支持超过4GB的内存寻址和更快的64位计算。 * 更多的寄存器: 31个通用寄存器,相比AArch32的15个有了显著增加。 * 标准化的SIMD/浮点: VFPv4和高级SIMD(Neon)成为标准配置。 * 加密指令: 增加了对AES, SHA-1/SHA-256的硬件支持。
| 编码 | 条件助记符 | 标志位 | 含义 |
|---|---|---|---|
| 0000 | EQ | Z=1 | 相等 |
| 0001 | NE | Z=0 | 不相等 |
| 0010 | CS/HS | C=1 | 无符号大于或等于 / 有进位 |
| 0011 | CC/LO | C=0 | 无符号小于 / 无进位 |
| 0100 | MI | N=1 | 负值 |
| 0101 | PL | N=0 | 正值或 0 |
| 0110 | VS | V=1 | 溢出 |
| 0111 | VC | V=0 | 无溢出 |
| 1000 | HI | C=1 且 Z=0 | 无符号大于 |
| 1001 | LS | C=0 或 Z=1 | 无符号小于或等于 |
| 1010 | GE | N 和 V 相同 | 有符号大于或等于 |
| 1011 | LT | N 和 V 不相同 | 有符号小于 |
| 1100 | GT | Z=0 且 N 等于 V | 有符号大于 |
| 1101 | LE | Z=1 或 N 不等于 V | 有符号小于或等于 |
| 1110 | AL | 忽略 | 无条件执行(默认可省略) |
| 1111 | NV | 忽略 | 从不执行(A64中通常不用) |
| 类型 | 指令 | 助记 | 说明 | 例子 |
|---|---|---|---|---|
| 跳转指令 | B/BL | Branch{Link} | 跳转到其他位置,带L将返回地址存到LR(X30) | B.EQ label; Z=1时跳转 |
| RET/BR/BLR | RETurn/Branch Register/Branch Link Register | RET是BR LR的别名,用于函数返回。BR/BLR跳转到寄存器指定的地址 | RET; 函数返回 | |
| 数据处理指令 | MOV/MVN | MOVe/Move Not | 数据传输与按位取反传输 | MOV X0, X1; X0 = X1 |
| ADD/SUB/ADC/SBC | ADD/SUBtract{with Carry} | 加减运算,带C表示带进位 | ADD X0, X1, #16; X0 = X1 + 16 | |
| MUL/MADD/MSUB/SDIV/UDIV | MULtiply/Multiply ADD/Multiply SUBtract/Signed DIVide/Unsigned DIVide | 乘法、乘加、乘减、有/无符号除法 | MADD X0, X1, X2, X3; X0 = X1*X2 + X3 | |
| AND/ORR/EOR/BIC | 逻辑{与|或|异或|位清除} | AND W0, W0, #0xFF; 清除W0的高24位 | ||
| 比较与测试 | CMP/CMN | CoMPare/CoMPare Negative | 比较/负值比较,设置条件标志位 | CMP X0, X1; 比较X0和X1 |
| TST | TeST bits | 位测试,执行AND操作并设置标志位 | TST W0, #1; 测试W0的最低位 | |
| 条件选择指令 | CSEL/CSET/CINC | Conditional SELect/SET/INCrement | 根据条件标志位选择、设置或增加寄存器的值 | CSEL X0, X1, X2, EQ; if EQ then X0=X1 else X0=X2 |
| 加载/存储指令 | LDR/STR | LoaD/STore Register | 从内存加载/存储一个寄存器(支持B/H/W/X后缀) | LDR X0, [SP, #16]; X0 = [SP+16] |
| LDP/STP | LoaD/STore Pair | 从内存加载/存储一对寄存器 | STP X29, X30, [SP, #-16]!; SP-=16, [SP]=X29, [SP+8]=X30 | |
| 系统指令 | MRS/MSR | Move to/from System Register | 读写系统寄存器(如PSTATE的标志位) | MRS X0, NZCV; 读取NZCV标志到X0 |
| 异常产生指令 | SVC/BRK | SuperVisor Call/BReaKpoint | 产生软中断用于系统调用/产生断点异常 | SVC #0; 产生0号系统调用 |
寻址方式
A64的寻址方式更加规整: 1.立即数寻址:
ADD X0, X0, #0x10 ; X0 = X0 + 16
2.寄存器寻址:
ADD X0, X1, X2; X0 = X1 + X2
ADD X0, X1, X2, LSL #3 ; X0 = X1 + (X2 << 3)
3.基址变址寻址:
LDR X0, [X1, #16]; 偏移寻址: X0 = [X1 + 16]
LDR X0, [X1, X2]; 寄存器变址: X0 = [X1 + X2]
LDR X0, [X1, #16]!; 前变址: X1 = X1 + 16, X0 = [X1]
LDR X0, [X1], #16 ; 后变址X0 = [X1], X1 = X1 + 16
4.PC相对寻址:
ADR X0, label ; 获label的地址到X0
LDR X0, label ;label地址加载数据到X0
5.多寄存器寻址 (Load/Store Pair):
STP X0, X1, [SP, #16] ;[SP+16]=X0, [SP+24]=X1
调用约定
上面提到了AAPCS,64位下的过程调用标准是AAPCS64:
1.参数传递时, 前8个整型/指针参数依次通过 X0-X7 传递,前8个浮点/向量参数依次通过 V0-V7 传递,更多参数通过栈传递。
2.对于返回值,整型/指针返回值通过 X0 传递。如果返回值大于64位,则使用 X0 和 X1,浮点/向量返回值通过 V0 传递。
3.栈指针(SP)必须始终保持16字节对齐,使用满递减栈。
| 寄存器 | 同义名 | 角色 | 说明 |
|---|---|---|---|
| X0-X7 | a1-a8 | 参数/结果 | 用于传递参数和返回结果。 |
| X8 | - | 间接结果 | 用于传递间接结果的地址。 |
| X9-X15 | - | 临时寄存器 | 调用者保存 (Caller-saved)。 |
| X16-X17 | IP0, IP1 | 临时寄存器 | 过程内调用暂存寄存器,调用者保存。 |
| X18 | - | 平台寄存器 | 若平台不使用,则为临时寄存器。 |
| X19-X28 | - | 被调用者保存 | 函数内若使用,必须在返回前恢复。 |
| X29 | FP | 帧指针 | 被调用者保存。 |
| X30 | LR | 链接寄存器 | 调用者保存(通过BL指令隐式保存)。 |
| SP | - | 堆栈指针 | - |
参考
- Whirlwind Tour of ARM Assembly
- arm-directives-reference
- ARM and Thumb Instructions
- 《Android 系统安全和反编译实战》-- 刘云[著];朱桂英[著]
- 《ARM处理器开发详解:基于ARM Cortex-A9处理器的开发设计》-- 秦山虎[著];刘洪涛[著]
- armv8/armv9架构入门指南