写在前面:逆向工程小白,仅供参考,如有错误,欢迎指正。
armv8主要有三套指令集,依据执行状态(execution state)的不同可以划分为:
AArch64 AArch64 状态只支持一套指令集,叫做A64.
A64为定长32位的指令集,即每个指令的大小为32bit.
指令集手册:https://developer.arm.com/documentation/dui0801/k/A64-Data-Transfer-Instructions/LDR--register-
AArch32 AArch32 状态支持两套指令集:
A32 也是32位定长指令集
T32 可变长指令集,其中支持两种不同长度的指令一种长度16位一种长度32位,其中16位的指令也称为thumb code
指令集手册:https://developer.arm.com/documentation/dui0473/m/arm-and-thumb-instructions/ldr--register-offset-
通过对比我们可以发现同一个指令的格式有较大差异,比如LDR的指令。aarch32是这样的:
LDR{type}{cond} Rt, [Rn, ±Rm {, shift}] ;
似乎支持正负号。
而aarch64的指令是这样的:
LDR , [, (|){, {}}] ; 32-bit
似乎简洁一些。
armv8的cpu将需要运行的一部分程序指令打包为一个PE(Processing Element 简称PE翻译成处理单元好像也可以)并将PE分别划分为四层,EL0~EL3,数字越大,安全等级越高,而切换执行状态(aarch32还是aarch64)只能通过切换EL或者重置来实现,而切换EL后切换的执行状态则由更高级的EL来决定。
要进入的EL | 决定这个EL执行状态的寄存器 |
---|---|
Non-secure EL1 | HCR_EL2.RW |
Secure EL1 | SCR_EL3 (当Secure EL2 启用的时候是 HCR_EL2 ) |
EL2 | SCR_EL3.RW |
EL3 | 重置 EL3时候设置的值 |
比如:
处于aarch32状态的EL0的程序时候如果监测到接下来要执行的指令(比如一些系统调用什么的)是aarch64的,就会主动触发异常,根据这个异常会切换到高权限的EL1(系统运行的层面),并将接下来需要执行的aarch64指令通过AArch64状态执行,而这个El1层执行状态是AArch64是由谁决定的呢?是由E2层的SCR_EL3.RW寄存器决定的。
并还遵循以下原则:
- 当异常发生时,有两种选择,停留在当前的EL,或者跳转到更高的EL,EL不能降级。同样,异常处理返回时,也有两种选择,停留在当前EL,或者调到更低的EL
- 从低权限EL切换到高权限EL(抛出异常),执行状态可以保持不变或者切换到 AArch64。
- 从高权限EL切换到低权限EL(从异常中返回使用汇编指令ERET ),执行状态可以保持不变或者切换到 AArch32。
因此在armv8虚拟化的时候,64-bit 层可以运行 32-bit层的应用, 但反过来不行。
举个例子一个 64-bit 系统内核既可以用来运行 64-bit 程序也可以运行 32-bit 程序,而一个 32-bit OS 内核 只能运行 32-bit 程序。