一、ARMv8寄存器之通用、状态、特殊寄存器

ARMV8核心寄存器数量是非常大的,为了更好的学习,可以划分为以下几大类:

**·**通用寄存器。这类寄存器主要是用来暂存数据和参与运算。通过load\store指令操作。
**·**状态寄存器。AArch64体系结构使用PSTATE寄存器表示当前处理器状态。
**·**特殊寄存器。有专门的用途,用于控制处理器的行为,或表示CPU的状态。
**·**系统寄存器。除了以上寄存器,ARMv8体系结构还定义了很多系统寄存器,用来完成对处理器不同功能的配置。对应ARMv7的cp15寄存器。

一、 通用寄存器

  1. ARMv8提供了31个通用寄存器 R0~R30;
  2. 在AArch32架构,通用寄存器w0~w30是32bit宽度;
  3. 在AArch64架构,通用寄存器x0~x30是64bit宽度;

X0-X7 用于参数传递

X9-X15 在子函数中使用这些寄存器时,直接使用即可, 无需save/restore. 在汇编代码中x9-x15出现的频率极低。在子函数中使用这些寄存器时,直接使用即可, 无需save/restore.。在汇编代码中x9-x15出现的频率极低。

X19-X29 在callee子函数中使用这些寄存器时,需要先save这些寄存器,在退出子函数时再resotre。在callee子函数中使用这些寄存器时,需要先save这些寄存器,在退出子函数时再resotre。

X8, X16-X18, X29, X30 这些都是特殊用途的寄存器。

-- X8: 用于返回结果

-- X16、X17 :进程内临时寄存器

-- X18 :resrved for ABI

-- X29 :FP(frame pointer register)

-- X30 :LR

在简单讲一下函数的局部变量和临时变量之间的区别:

作用域(Scope)不同: 局部变量(Local variables)是定义在函数内部的变量,它们的作用域仅限于该函数内部。临时变量(Temporary variables)通常是用于存储中间计算结果的变量,它们的作用域更加局限,可能只在某个代码块或表达式内部。

生命周期不同: 局部变量的生命周期与函数的执行周期相同,即在函数调用时创建,在函数返回时销毁。临时变量的生命周期则更短暂,通常仅在表达式或代码块的执行期间存在。

存储位置不同: 局部变量通常存储在函数的栈帧(Stack Frame)中,以便函数调用时能够访问。临时变量通常存储在寄存器或者栈上,以减少内存访问开销。

使用目的不同: 局部变量用于存储函数内部需要持久保存的数据,如参数、返回值、中间计算结果等。临时变量主要用于存储一些中间计算结果,在表达式或代码块结束后可以丢弃。

优化方式不同: 编译器通常会尽量将局部变量存储在寄存器中,以提高访问效率。对于临时变量,编译器则更加倾向于直接使用寄存器,避免不必要的内存访问。

什么是栈帧:

栈帧是函数调用时在内存栈上分配的一块区域,用于存储函数的局部变量和参数等信息。每当一个函数被调用时,CPU 会在内存栈上为该函数创建一个新的栈帧。栈帧通常包含以下几个部分:

返回地址: 当前函数返回时需要跳转的地址,通常是调用函数的下一条指令。返回地址通常是由调用指令自动压入栈的。

函数参数: 传递给当前函数的参数值,它们需要被保存在栈帧中。当函数返回时,这些参数值需要被恢复。

局部变量: 函数内部声明的局部变量,它们的生命周期仅限于函数的执行期间。这些变量需要被保存在栈帧中,以免被其他函数覆盖。

寄存器备份: 在函数调用过程中,需要保存一些重要的寄存器值,以便在返回时恢复。这些寄存器可能包括函数返回地址寄存器、帧指针寄存器等。

动态分配的内存: 函数在执行过程中动态分配的内存空间,需要在栈帧中保存相关信息。这些动态分配的内存会在函数返回时被自动释放。

二:状态寄存器PSTAE

在ARMv7架构中使用程序状态寄存器(Current Program Status Register,CPSR)来表示当前的处理器状态(processor stste),而在ARMv8里使用PSTATE寄存器来表示。下面我们来看一下AArch64中PSTATE各字段所代表的含义。

注意: PSTATE(process state)是一些状态位,一些位仅在aarch32 state下使用、一些位仅在aarch64 state下使用、一些位可以同时在aarch32/aarch64 state下使用;我们在这里仅分析AArch64。

在aarch64中,只可以通过MSR/MRS指令访问特殊寄存器(special-purpose)的方式读写PSTATE中的位。除了这些特殊寄存器中表示的位,PSTTAE的其它位都是不能访问的。

这些特殊的寄存器包含: NZCV、DAIF、CurrentEL、SPSel、PAN、UAO,可以使用MRS/MSR指令,直接读写这些寄存器。

例如我们可以使用MSR 指令直接写入 DAIFSET 或 DAIFCLR 指令操作(注:DAIFSET 和 DAIFCLR 是两个特殊的指令操作,用于设置和清除 DAIF 寄存器中的某些位。)

MSR DAIFSET, #0x2 // 设置 DAIF 寄存器的 I 位(IRQ 屏蔽位)

MSR DAIFCLR, #0x2 // 清除 DAIF 寄存器的 I 位(IRQ 屏蔽位)

上述指令实际上会修改DAIF寄存器的对应位

特殊寄存器 PSTATE 位

我们可以认为这些位其实还是都在PSTATE寄存器中,然后相关的位被抽象到了NZCV,DAIF、CurrentEL、SPSel、PAN、UAO 寄存器中,方便指令访问。

三:特殊寄存器
下面我们来看一下ARMv8中的特殊寄存器。

  1. 保存处理状态寄存器

当我们运行一个异常处理程序时,处理器的处理状态会保存到保存处理状态寄存器(Saved Program Status Register, SPSR)里。这个寄存器类似于ARMv7架构中的CPSR,当异常将要发生时,处理器会把PSTATE寄存器的值暂时保存到SPSR里;当异常处理完成并返回时,再把SPSR的值恢复到PSTATE寄存器。

★CurrentEL寄存器

该寄存器表示PSTATE寄存器中的EL字段,其中保存了当前异常等级。使用MRS指令可 以读取当前异常等级。

0: 表示 EL0

1: 表示 EL1

2: 表示 EL2

3: 表示 EL3

★DAIF寄存器

该寄存器表示PSTATE寄存器中的{D, A, I, F}字段。

★SPSel寄存器

该寄存器表示PSTATE寄存器中的SP字段,用于在SP_EL0和SP_ELn中选择SP寄存器。

★PAN寄存器

PAN寄存器表示PSTATE寄存器中的PAN (Privileged Access Never,特权禁止访问)字段。 可以通过MSR和MRS指令来设置PAN寄存器。当内核态拥有访问用户态内存或者执行用户态程序的能力时,攻击者就可以利用漏洞轻松地执行用户的恶意程序。为了修复这个漏洞, 在ARMV8.1中新增了 PAN特性,防止内核态恶意访问用户态内存。如果内核态需要访问用 户态内存,那么需要主动调用内核提供的接口,例如copy_from_user()或者copy_to_user() 函数。

PAN寄存器的值如下。

0:表示在内核态可以访问用户态内存。

1:表示在内核态访问用户态内存会触发一个访问权限异常。

★UAO寄存器

该寄存器表示PSTATE寄存器中的UAO (User Access Override,用户访问覆盖)字段。我 们可以通过MSR和MRS指令设置UAO寄存器。

特权指令:如 LDR、STR 等,可以访问任意内存区域,包括特权保护的区域。

非特权指令:如 LDTR、STTR 等,通常只能访问用户空间的内存区域,受到限制。

当 UAO = 0 时,非特权指令(如 LDTR、STTR)的行为与特权指令(如 LDR、STR)是不同的,受到内存访问权限的限制。

当 UAO = 1 时,非特权指令(如 LDTR、STTR)的行为与特权指令(如 LDR、STR)是一致的,即"用户访问被覆盖"。也就是说,即使是在用户模式(EL0)下执行非特权指令,也能够访问特权保护的内存区域,就像在特权模式(EL1/EL2)下执行特权指令一样。

使用场景:

UAO = 1 的机制通常用于支持一些特殊的安全需求,例如在特权模式下执行用户空间代码、处理敏感的内存区域等。

★NZCV寄存器

该寄存器表示PSTATE寄存器中的{N, Z, C, V}字段。

  1. 零寄存器

ARMv8体系结构提供两个零寄存器(zero register),这些寄存器的内容全是0,可以用作源寄存器,也可以用作目标寄存器。WZR是32位的零寄存器,XZR是64位的零寄存器。

  1. PC寄存器

PC寄存器(Program Counter)通常用来指向当前运行指令的下一条指令的地址,用于控制程序中指令的运行顺序,但是我们不能通过指令来直接访问它。

PC 寄存器的主要作用包括:

保存当前指令的地址:PC 寄存器会始终保存当前正在执行的指令的地址。每当执行一条指令,PC 寄存器的值都会自动加 4(或加 2,取决于指令宽度)以指向下一条要执行的指令。
支持分支跳转:当执行一条分支指令(如 B、BL 等)时,CPU 会将 PC 寄存器的值更新为分支目标地址,从而实现程序控制流的转移。
参与异常处理:当发生异常(如中断、系统调用等)时,硬件会自动保存当前 PC 的值到 ELR 寄存器,以便于事后恢复执行。

  1. SP寄存器

ARMv8体系结构支持4个异常等级,每一个异常等级都有一个专门的SP(Stack Pointer)寄存器SP_ELn, 如处理器运行在ELI时选择SP_EL1寄存器作为SP寄存器。

SP_EL0: ELO下的SP寄存器。
SP_EL1: ELI下的SP寄存器。
SP_EL2: EL2下的SP寄存器。
SP_EL3: EL3下的SP寄存器。
当处理器运行在比ELO高的异常等级时,处理器可以访问如下寄存器。

当前异常等级对应的SP寄存器SP_ELs
ELO对应的SP寄存器SP_ELO可以当作一个临时寄存器,如Linux内核使用该寄存器 存放进程中task_struct数据结构的指针。
注意:当处理器运行在EL0时,它只能访问SP_EL0,而不能访问其他高级的SP寄存器。
SP 寄存器的主要作用包括:

管理程序栈:SP 寄存器存放着当前程序栈的顶部地址。当执行压栈和出栈操作时,SP 寄存器的值会相应地增加或减少。这样可以确保程序栈的正确使用。
支持函数调用:在函数调用过程中,CPU 会自动保存返回地址、函数参数、局部变量等信息到程序栈中。SP 寄存器的值会随之动态变化,以管理这些栈帧。
参与中断/异常处理:当发生中断或异常时,CPU 会自动保存当前上下文(包括 SP 寄存器的值)到栈中,并切换到特定的异常处理模式。这样可以确保异常返回后能够恢复程序的执行状态。

5. ELR寄存器

ELR(Exception Link Register) 寄存器存放了异常返回的地址。

ELR寄存器的主要作用包括:

保存异常发生前的程序计数器(PC)值:当处理器进入异常模式(如中断、系统调用等)时,ELR 寄存器会自动保存异常发生前的 PC 值。这样在异常处理完成后,就可以从 ELR 中恢复 PC 值,继续执行异常前的指令序列。

支持异常处理的返回:在异常处理程序中,可以使用 ERET 指令从异常返回。ERET 指令会从 ELR 寄存器中恢复 PC 值,从而实现对异常的正确返回。

相关推荐
Crossoads18 小时前
【汇编语言】call 和 ret 指令(一) —— 探讨汇编中的ret和retf指令以及call指令及其多种转移方式
android·开发语言·javascript·汇编·人工智能·数据挖掘·c#
Crossoads2 天前
【汇编语言】转移指令的原理(三) —— 汇编跳转指南:jcxz、loop与位移的深度解读
android·汇编·人工智能·redis·单片机·深度学习·机器学习
zhuqiyua2 天前
深入解析Kernel32.dll与Msvcrt.dll
汇编·microsoft·windbg·二进制·dll
Crossoads4 天前
【汇编语言】数据处理的两个基本问题(三) —— 汇编语言的艺术:从div,dd,dup到结构化数据的访问
android·linux·运维·服务器·汇编·机器学习·数据挖掘
Crossoads4 天前
【汇编语言】数据处理的两个基本问题(二) —— 解密汇编语言:数据长度与寻址方式的综合应用
android·java·开发语言·javascript·汇编·数据挖掘·c#
Coding~5 天前
逆向攻防世界CTF系列38-xxxorrr
c语言·汇编·安全
Crossoads5 天前
【汇编语言】数据处理的两个基本问题 —— 汇编语言中的数据奥秘:数据位置与寻址方式总结
android·汇编·人工智能·redis·单片机·深度学习·机器学习
Crossoads6 天前
【汇编语言】更灵活的定位内存地址的方法(一)—— 字符操作:and与or指令、ASCII码及大小写转换
android·linux·运维·服务器·汇编·机器学习·数据挖掘
不会写算法的小沈6 天前
函数栈帧的创建与销毁
c语言·汇编·数据结构
zhuqiyua7 天前
windows二进制安全零基础(二)
汇编·安全·二进制