[1]arm64架构学习

介绍

ARM架构也曾经叫做进阶精简机器指令集,早期是一个32位的处理器架构,目前为止占据了32位嵌入式处理器的75%,成为全世界最多数的32位架构。arm架构主要用于低性能或一般性能的嵌入式操作系统,对于一些高级的比如计算机或者服务器,取决于CPU的架构,如AMD的AMD64或Intel的x64架构等等,他们是相似但不相同的。不过由于市面上绝大部分架构都是从arm演变过来的,所以学ARM架构就足够了。

ARM的第一代产品在1985年推出,使用了RISC架构,史称ARM架构。arm不同版本有不同的功能:

版本 特点
v1 26位地址
v2 乘法和加法指令,支持协处理器
v3 地址扩展到32位
v4 增加Thumb指令集
v5 增加Jazelle和VFPv2指令
v6 增加SIMD、Thumb-v2扩展
v7 增强Neon和VFPvs/v4扩展
v8 同时支持32位和64位指令集处理器
v9 支持矢量扩展计算

基本寄存器

通用寄存器:X0~X30,用于保存基本数据,每个寄存器有64位,W可以用于表示X的低32位的数据

PSTATE寄存器:

  • N:负数标志位
  • Z:0标志位
  • C:进位标志位
  • V:溢出标志位
  • SS:软件单步
  • IL:异常标志位
  • nRW:架构模式,可以判断寄存器数(0有32个通用寄存器,1有16个通用寄存器)
  • EL:异常等级标志位
  • SP:选择异常处理寄存器
  • D:调试位
  • A:屏蔽错误
  • I:屏蔽中断(IRQ)
  • F:屏蔽快速中断(FIQ)
  • PAN:特权模式禁止访问位(异常等级跨界访问置为1)
  • UAO:访问覆盖标志位(无特权访问地址指令置为1)

除了上述之外还有很多寄存器,这里就不一一列举了。

PC寄存器是程序运行的指针,但是无法访问。SP是异常等级指针。

SP寄存器中有:SP_EL0,也就是EL0的寄存器,同理还有EL1,EL2,EL3寄存器。这些寄存器都是被穷举出来的,也就是说不可变。

简单指令集

加载指令:LDR

  • LDR Xt, [Xn, #偏移量] 加载Xn+偏移量处(不写就不偏)的值存储进Xt

存储指令:STR

  • STR Xt, [Xn, #偏移量] 保存Xt的值进入Xn+偏移量处(不写就不偏)

前变基:先偏移后取值

  • LDR Xt, [Xn, #8] 先移动8位然后加载值进入Xt

后变基:先取值后偏移

  • LDR Xt, [Xn], #8 先加载Xn的值进Xt,然后Xn向后移动8位

标签:

1
2
3
my_label:
.data 0xff
ldr x0, my_label

其中的my_labe就是标签,ldr加载my_label的值data为0xff,给了x0寄存器。为了美观一般也写作

1
ldr x0, =my_label

其他指令:

指令 功能
LDR 加载数据指令
LDRSW 加载有符号字大小的数据
LDRB 加载字节大小的数据
LDRSB 加载有符号字节的数据
LDRH 加载半个字节大小的数据
LDRSH 加载半个有符号字节大小的数据
STRB 存储一个字节的数据
STRH 存储半个字节的数据

移动指令:MOV

  • MOV Xt, #偏移量 将xt的数据移动偏移量位

加法指令:ADD

  • ADD Xd, Xn, #偏移量 意为Xn的值加上偏移量的值存入Xd内
  • ADD Xd, Xn, Xt 数学含义为:Xd = Xn + Xt
  • ADD Xd, Xn, Xt LSL 2 数学含义为: Xd = Xn + Xt << 2

减法指令:SUB

  • SUB Xd, Xn, #偏移量 意为Xn的值减去偏移量的值存入Xd内
  • SUB Xd, Xn, Xt 数学含义为:Xd = Xn - Xt
  • SUB Xd, Xn, Xt LSL 2 数学含义为: Xd = Xn - Xt << 2
  • SUBS Xd, Xn, Xt 数学含义为:Xd = Xn - Xt,如果出现了NZCV标志可以直接获取

比较指令:CMP

  • CMP Xn, Xt 等效的算式为:Xn + NOT(Xt) + 1

通过读取LS寄存器的值就可以知道它是小于或等于,读取CS则是大于或等于

比较指令2:CMN

  • CMN Xn, R 伪代码意为:Xn == -R?eq=1:eq=0

CMN是相反数比较,Xn与-R是否一样,一样则置EQ寄存器为1,反之为0.

移位指令:

  • LSL向左移位
  • LSR向右移位
  • ASR算式右移
  • ROR循环右移

与运算符:AND

  • AND Xd, Xn, Xm 数学含义为:Xd = Xn & Xm

相对加载指令:ADR

  • ADR Xd, label 加载label处的程序进入寄存器Xd内,对齐为4Kb

编译过程

一段C语言代码的编译经过如下过程:

  • 预处理:将所有使用到的头文件内的方法导入进来,并组成中间文件
1
gcc -E main.c -o main.i
  • 编译:优化中间文件的内容,优化代码并生成汇编代码
1
gcc -S main.i -o main.s
  • 汇编:将汇编代码转化为二进制文件,并进行重定位
1
as main.s -o main.o
  • 链接:将二进制文件转化为可执行文件并导入所有使用到的库
1
ld -o main main.o -lc

学习过C语言一定知道指针的概念,大家就会发现指针可以指向任何内容,包括函数,所谓函数也是一段在内存中保存的指令区域,一样可以获取到所在的内存地址。

链接器

任何一个可执行程序都不是独立存在并且不依赖其他文件的,所以我们需要进行添加所有依赖的文件,汇编内使用到的指令块为:

1
2
3
4
5
6
7
8
SECTIONS
{
.=链接起点地址
.text: { *{.text}} # 输出文件由输入文件决定
.=重新指向链接的地址
.data: { *{.data}} # 程序起点
.bss: { *{.bss}} # 数据段构成
}

程序的第一条指令叫做入口点

ENTRY(symbol)

对于C语言,symbol就是main函数的内存地址。对于SECTION而言它可以写的格式如下:

1
2
3
4
5
SECTION 虚拟地址 对齐要求
输出段属性
{
描述输入如何映射到输出段
}特定的内存空间 特定的程序段

之所以结合C语言是因为C语言可以内嵌汇编指令也就是:

1
2
3
4
5
6
7
8
9
10
11
12
int add(int a, int b){
int res = 0;
asm volatile(
// ADD指令,%w为获取对应地址段
"add %w[result], %w[num1], %w[num2]"
// 填充其中的变量,"=r"是为了获取函数自定义的变量res
: [result] = "=r" (res)
// "r" 为函数的参数
: [num1] "r" (a), [num2] "r" (b)
);
return res;
}

我们甚至可以用汇编高效的完成交换算法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void swap(unsigned char* src, unsigned char* dst, unsigned int size){
unsigned int len = 0;
unsigned int tmp;
asm volatile(
"1: ldeh w5, [%[src]]], #2 \n"
"lsl w6, w5, #8 \n"
"orr %w[tmp, [%[dst]]], #w \n"
"strh %w[tmp], [%[dst]]], #2 \n"
"add %[len], %[len], #2 \n"
"cmp %[len], %[size] \n"
"bne 1b \n"
: [dst] "+r"(dst), [len] "+r"(len), [tmp] "+r"(tmp), [src] "+r"(src)
: [size] "r" (size)
: "memory", "w5", "w6"
);
}

其中的W为高位寄存器,可以跳到基本寄存器部分查看。

异常与中断

中断有两种,普通中断IRQ和快速中断FIQ,其中FIQ的优先级高于IRQ。异常有4个等级分别为:EL0、EL1、EL2、EL3.

  • EL0:非特权模式,用于运行程序
  • EL1:特权模式,用于运行系统内核
  • EL2:用于运行虚拟化任务
  • EL3:用于运行安全世界的管理程序

安全世界TrustZone:https://blog.csdn.net/pslyunhai3255/article/details/129229732

寄存器保存的地址就是异常返回的地址,ELR_ELx存放的是异常返回地址,也就是程序段地址。异常存放的位置叫异常向量表:

  • NS:值为1表示低于EL3的异常级别处于非安全状态
  • IRQ:值为1表示来自低于EL3的异常级别的IRQ会路由到EL3
  • FIQ:值为1表示来自低于EL3的异常级别的FIQ会路由到EL3
  • EA:值为1表示来自低于EL3的异常级别的外部中止和SError中断会路由到EL3
  • RW:值为1表示低于EL3的异常级别都在AArch64执行状态下
  • TGE:值为1表示如果系统实现了EL2
  • AMO:值为1表示来自低于EL2的异常级别的SError中断会路由到EL2
  • IMO:值为1表示来自低于EL2的异常级别的IRQ中断会路由到EL2
  • FMO:值为1表示来自低于EL2的异常级别的FIQ会路由到EL2

总结

本期针对ARM的基础部分进行了描述,下一节将会讲解关于CPU的更多细节。


[1]arm64架构学习
https://blog.minloha.cn/posts/152822a003debd2023122811.html
作者
Minloha
发布于
2023年12月28日
更新于
2023年12月29日
许可协议