本文主要介绍 Armv8/v9 指令集架构中常用部分,详细的还是要看 Arm architecture reference manual.
ARMv8 架构
ARMv8 架构支持3种指令集: T32, A32, A64
ARMv8 架构有两种执行状态: AArch32, AArch64
一个App 可以混合使用 T32 和 A32, 但是不能混合使用 A32 和 A64.
Registers in AArch64
The general purpose registers
该架构提供了 31 个通用寄存器。每个寄存器都可以用作 64 位 X 寄存器 (X0…X30),或用作 32 位 W 寄存器 (W0…W30)。这是查看同一寄存器的两种不同方式。比如这张寄存器图,W0是X0的低32位,W1是X1的低32位:
对于数据处理指令,选择X或W决定操作的大小。使用X寄存器将导致 64 位计算,使用 W 寄存器将导致 32 位计算。
此示例执行 32 位整数加法:ADD W0, W1, W2
此示例执行 64 位整数加法:ADD X0, X1, X2
写入寄存器时W,如上例所示,64 位寄存器的前 32 位被清零。
特殊寄存器
Zero register
零寄存器XZR和WZR始终读为 0 并忽略写入。
这个寄存器在编程中非常有用,因为它可以简化某些操作,例如:
- 当需要将一个寄存器清零时,可以将 zero register 直接作为源操作数,这样就能够避免使用额外的立即数或操作来实现清零。
- 在某些算术或逻辑操作中作为一个操作数使用,可以便捷地实现特定的功能。例如,如果仿效清零操作,可以通过将某个寄存器和 XZR/WZR 进行 OR 操作实现。
stack pointer
- ARMv8体系结构为每个异常等级都实现了一个栈指针SP_ELn,以异常等级为后缀
- 当PE在某个异常等级处理异常时,可配置使用如下2个栈指针之一(通过PSTATE.SP配置),
① 所处理异常的target exception level对应的SP_ELn(默认情况)
② SP_EL0(注意,EL0是不会作为target exception level的,所以这不是默认情况,而是需要特殊配置的)
说明1:配置使用SP_EL0是在PE已经切换到targe exception level上进行的
说明2:即使异常处理没有发生异常等级切换,也需要处理上述栈指针的配置
例如PE当前在EL1运行,并且使用SP_EL0。此时如果发生异常,并且target exception level也是EL1,虽然异常等级不会切换,但是栈指针也会自动切换为SP_EL1。此时如果想继续使用SP_EL0,则仍需要设置PSTATE.SP
t后缀表示thread,此时使用SP_EL0作为栈指针;h后缀表示handler,此时使用当前异常等级对应的栈指针。
3:在Linux内核中,当从EL0陷入EL1时,使用SP_EL1作为当前栈指针。此时SP_EL0可以作为一个临时寄存器使用,Linux内核会使用该寄存器存放进程的task_struct结构指针
在 ARMv8 架构中,实际上有两个堆栈指针,它们是:
- SP_EL0:用于应用程序的用户模式堆栈指针。
- SP_ELx:x 可以是 1, 2, 或 3,代表不同的异常级别。不同的异常级别对应不同的操作模式,比如操作系统的内核模式或中断处理模式,每个异常级别都有自己的堆栈。
当发生函数调用时,处理器通常会将当前的 PC (程序计数器) 寄存器值(即返回地址)和其他必要的寄存器保存在栈上,然后将 SP 寄存器减少一定量来为新的函数调用预留空间。相反,在函数返回时,处理器会使用 SP 寄存器的内容来恢复之前保存的寄存器值并跳回到调用点。
SP 寄存器的使用遵循调用约定和堆栈平衡的规则,这些规则确保了函数间参数传递、局部变量存储和控制流的正确性。
Saved Program Status Registers (SPSRs)(备份程序寄存器)
用于保存发生异常时的处理器状态
- ARMv8体系结构为每个可作为target exception level的异常等级都实现了一个SPSR_ELn,以异常等级为后缀
- 当异常发生时,处理器的状态将被保存在target exception level的SPSR寄存器;当异常返回时,将使用的SPSR寄存器中的值恢复PSTATE
- 保存异常发生时PSTATE状态的另一个作用,就是可以在异常处理时通过SPSR寄存器判断发生异常时的PE状态,例如发生异常时PE的异常等级和执行状态
SPSR寄存器保存的很多状态位与PSTATE中是相同的。这里特别说明一下M[4]和M[3:0]的含义
① M[4]:记录异常发生时的执行状态,即PSTATE.nRW位。0表示AArch64执行状态,1表示AArch32执行状态
② M[3:0]:记录异常发生时的异常等级及其使用的栈指针
- M[3:2]标识发生异常时的异常等级
- M[1]为保留位,需要置为0
- M[0]标识栈指针SP的选择,0表示使用SP_EL0,1表示使用与异常等级对应对应的SP_ELn
说明2:不像栈指针SP的使用可配置,异常发生时只能使用与targe exception level相应的SPSR_ELn
说明2:异常返回时,使用SPSR_ ELx的内容来恢复PSTATE
Exception Link Registers (ELRs))
- ARMv8体系结构为每个可作为target exception level的异常等级都实现了一个ELR_ELn,以异常等级为后缀
- 当异常发生时,异常返回地址将被保存在target exception level的ELR寄存器;当异常返回时,将使用的ELR寄存器中的值恢复到PC寄存器
说明:和SPSR寄存器一样,异常发生时也只能使用与targe exception level相应的ELR_EL
其中,对于保存什么异常返回地址,对于不同的异常规则不同。
首选的异常返回地址取决于异常的性质,如下所示:
l 对于异步异常,它是发生中断的指令边界之后的指令的地址。因此,它是由于中断而没有执行或没有完成执行的第一条指令的地址。
l 对于系统调用以外的同步异常,它是生成异常的指令的地址。
l 对于系统调用,它是系统调用指令后面的指令的地址。
n 注意:如果由于异常级别没有足够的权限执行指令而导致系统调用指令被捕获、禁用或未定义,则首选的异常返回地址为系统调用指令的地址。
n 系统调用是通过执行SVC、HVC或SMC指令生成的
Process state, PSTATE
在ARMv7体系结构中,使用CPSR寄存器来表示当前处理器的状态。在ARMv8中,将PE的状态抽象为PSTATE。在实现上,则是一组标志位的集合
ARMv8体系结构提供了一组特殊寄存器,用于访问PSTATE状态位
CurrentEL寄存器 | 表示PSTATE寄存器中的EL字段 |
---|---|
DAIF寄存器 | 表示PSTATE寄存器中的{D,A,I,F}字段 |
SPSel寄存器 | 表示PSTATE寄存器中的SP字段 |
PAN寄存器 | PSTATE寄存器中的PAN |
UAO寄存器 | UAO为1表示在EL1和EL2执行这非特权指令(例如LDTR、STTR)的效果与特权指令(例如LDR、STR)是一样的。 |
NZCV寄存器 |
说明:当内核态拥有访问用户态内存或者执行用户态程序的能力时,攻击者就可以利用漏洞轻松地执行用户的恶意程序。为了修复这个漏洞,在ARMv8.1中新增了PAN特性,防止内核态恶意访问用户态内存。如果内核态需要访问用户态内存,那么需要主动调用内核提供的接口,例如copy_from_user()或者copy_from_user()函数。(将内核态与用户态隔离的思想,在很多方面都提现了出来,比如内存管理中的内存隔离,内存映射等)
PAN寄存器的值如下。
0:表示在内核态可以访问用户态内存。
1:表示在内核态访问用户态内存会触发一个访问权限异常。
对于这些特殊寄存器的访问,只能通过MRS(读)/MSR(写)指令完成。
举例:仅设置C标志位为1
MRS R0,NZCV ;读NZCV到R0
ORR R0,R0,#0x200000000 ;设置R0的第29位
MSR NZCV,R0 ;写NZCV
Counter( PC)
Program Counter( PC)在A64中不是通用寄存器,不能和数据处理指令一起使用。可以使用以下方式读取 PC:ADR Xd, .
点 ( .) 表示“这里”,因此显示的指令返回其自身的地址。这相当于阅读PC.
系统寄存器
数据处理或加载/存储指令不能直接使用系统寄存器。相反,需要将系统寄存器的内容读入寄存器X,对其进行操作,然后写回系统寄存器。有两个用于访问系统寄存器的专用指令:
MRS Xd, 将系统寄存器读入Xd.
MSR , Xn 写入Xn系统寄存器。
系统寄存器由名称指定,例如SCTLR_EL1:MRS X0, SCTLR_EL1 读SCTLR_EL1入X0.
系统寄存器名称以_ELx. 指定_ELx访问寄存器所需的最低权限。