最近的文章可能会有较多修改,请关注博客哦
异常级别
ARMv8处理器支持4种异常等级(Exception Level,EL)。
- EL0 为非特权模式,用于运行应用程序,其他资源访问受限,权限不够。
- EL1 为特权模式,用于操作系统内核,可以访问系统的所有资源
- EL2 用于运行虚拟化管理管理程序(hypervisor)。
- EL3 用于运行安全世界中的管理程序,安全模式(secure monitor)。
这里的异常概念不同于程序中的异常概念。当作中断理解会好点。
执行状态
ARMv8 架构定义了两种执行状态, AArch64和AArch32。当处于 AArch64 状态时,处理器执行 A64 指令集。当处于 AArch32 状态时,处理器可以执行 A32(在早期版本的架构中称为 ARM)或 T32 (Thumb) 指令集。所以,ARMv8的设备可以运行老程序,它是向前兼容的。
注意,AArch64 OS 运行 AArch32 程序时,当应用程序调用(SVC)指令,或接收中断,会切换到 EL1 和 AArch64。
寄存器
AArch64执行状态提供了32个在任何时间任何特权级下都可访问的64位的通用寄存器。
每个寄存器都有64位宽,它们通常被称为寄存器X0-X30。
每个AArch64 64位通用寄存器(X0-X30)也具有32位(W0-W30)形式。
也就是说,64位模式下寄存器名字叫 Xn,32位模式下名字叫 Wn。这些东西与 CSAPP 里面讲的都是差不多的,只不过它讲的是 X86 架构。
每个寄存器的用途如下:
在aarch64中,没有x31或w31寄存器,但是在一些指令或软件编码中,经常将数字31作为XZR或SP
- X0-X7: 用于参数传递
- X9-X15: 调用方需要保存这些寄存器的值,因为被调用函数可能会修改这些寄存器 。
- X19-X29: 被调用方保存这些寄存器的值,退出时再恢复,因为函数返回后可能调用方还会用到这些寄存器的值 。
- X8, X16-X18, X29, X30: 这些都是特殊用途的寄存器
特殊寄存器
- X8: 用于储存间接结果的寄存器,比如返回一个结构体的时候,该寄存器储存的是结构体地址。
- X18: 不管
- sp: 保存栈顶地址
- fp: 保存栈底地址
- lr: 保存调用跳转指令
bl
指令的下一条指令的内存地址 - zr:
xzr/wzr
分别代表 64/32 位,可用于清零的操作,零寄存器 - pc: 保存将要执行的指令的地址
PSTATE
程序状态寄存器,程序中会存在很多分支,具体走哪条分支,是由状态寄存器决定的,比如一些比较指令执行时,需要根据状态寄存器的负数标志来决定是否进行逻辑分支跳转。
在aarch64中,只能可以通过MSR/MRS指令访问特殊寄存器(special-purpose)的方式读写这些位。除了这些特殊寄存器中表示的位,PSTATE的其它位都是不能访问的。
指令格式
ARM指令的基本格式如下:
xml
<opcode> {<cond>} {S} <Rd>, <Rn>{,<operand2>}
其中<>号内的项是必须的,{}号内的项是可选的。各项的说明如下:
- opcode:指令助记符
- cond:执行条件
- S:是否影响CPSR寄存器的值
- Rd:目标寄存器
- Rn:第一个操作数的寄存器
- operand2:第2个操作数
ARM指令格式举例:
- LDR R0, [R1]:读取R1地址上的存储单元内容,执行条件AL;
- BEQ DATAEVEN:条件执行分支指令,执行条件EQ,即相等则跳转到DATAEVEN;
- ADDS R2, R1, #1:加法指令,R2<-R1+1,影响CPSR寄存器;
- SUBNES R2, R1,#0x20:条件执行的减法运算,执行条件NE,R1-0x20->R2,影响CPSR寄存器
AArch64没有直接与ARMv7中的CPSR等价的寄存器,但是可以访问PSTATE中对应的字段。
寻址方式
我觉的 csapp 在这方面讲的是真的好,一个表格就让初学者对汇编了解很多。看一个例子:
这个表格就说清楚了各种寻址方式对应的汇编格式。
我们看看 arm64 的寻址方式。
立即寻址
立即寻址也叫立即数寻址,这是一种特殊的寻址方式,操作数本身就在指令中给出,只要取出指令也就取到了操作数,这个操作数被称为立即数,对应的方式也称为立即寻址
bash
ADD W0, W1, *#0x05*
寄存器寻址
寄存器寻址也就是利用寄存器中的数值作为操作数,这是一种常见的方式,也是效率比较高的寻址方式
sql
ADD W0, W1, W2
寄存器间接寻址
寄存器间接寻址就是以寄存器中值作为操作数的地址,而操作数本身存放在存储器中
css
ADD W0, W1, [W2]
LDR W3, [W0]
基址变址寻址
基址变址寻址就是将寄存器(该寄存器一般称作基址寄存器)的内容与指令中给出的地址偏移量相加,从而得到一个操作数的有效地址。变址寻址方式常用于访问某基地址附近的地址单元, 常见的有以下几种形式:
ini
LDR W0, [W1, #4] ;将寄存器w1中内容加上4作为地址,然后将该地址处的数据存入寄存器w0中
LDR W0, [W1, #4]! ;将寄存器w1中内容加上4作为地址,然后将该地址处的数据存入寄存器w0中,然后w1中的内容自增4
LDR W0, [W1], #4 ;将寄存器w1中的内容作为地址,然后将该地址处的数据存入w0中,并将w1中内容自增4
LDR W0, [W1, W2] ;将w1和w2中的内容相加作为地址,然后将该地址处的数据存入w0
后缀
上面有个例子:
bash
LDR W0, [W1, #4]!
汇编后面有个感叹号。ARM中的指令可以带后缀,从而丰富该指令的功能,常用的后缀有:
位数后缀
scss
B(byte):功能不变,操作长度变为8位(依赖CPU位数,以下相同)
H(Halfword):功能不变,操作长度变为16位
例如:ldr指令族:ldrb,ldrh,ldrsb ldrsh,从内存中加载指定长度的数据
S后缀
影响CPSR里的NZCV标识位。指令中使用"S"后缀时、指令执行后程序状态寄存器的条件标志位将被刷新。相当于有符号运算。
例如:
ini
SUB X1,X0,X3 ;X1=X0-X3 ,CPSR值不变
SUBS X1,X0,X3 ;X1=X0-X3 , 如果计算结果为负数,CPSR寄存器的N被置位
! 后缀
指令中地址表达式含有"!"后缀时,指令执行后,基址寄存器中的地址值将会发生变化。变化的结果是 (base+offset)。
例如:
ini
LDR X3,[X0,#4] //X3=X0+4
LDR X3,[X0,#4]! //X3=X0+4; X0+=4;
注意:"!"不能用于寄存器PC后面
条件后缀
ARM架构中,允许在指令后面添加条件后缀来完成指令条件执行的目的。指令条件执行就是说,指令根据CPSR中条件码的状态和指令的条件域有条件的执行。当指令的执行条件满足时,指令被执行,否则指令将被忽略。
例如:
bash
ADD X4,X2,#1 ; 无条件执行 X4=X2+1
ADDEQ X4,X2,#1 ;添加有条件执行后缀EQ,当CPSR中的Z标志置位(之前某条CMP结果相等)时,该指令才执行。
注意:如果条件后缀和"S"标识同时出现,则S在条件后缀的后面,例如:
ADDEQS X4,X2,#1 ;即为有条件执行X4=X2+1,结果更新条件标志位