Palabras clave en este artículo: formato de instrucción, campo de condición, campo de tipo, campo de operación, instrucción de datos, instrucción de acceso a memoria, instrucción de salto, SVC (interrupción de software)
Los artículos relacionados con el ensamblaje del kernel son:
- v74.01 Análisis del código fuente del kernel de Hongmeng (método de codificación) | cómo se codifican las instrucciones de la máquina
- v75.03 Análisis del código fuente del núcleo de Hongmeng (Conceptos básicos de ensamblaje) |
- v76.04 Análisis del código fuente del núcleo de Hongmeng (parámetros de paso de ensamblaje) | Cómo pasar parámetros complejos
- v77.01 Análisis del código fuente del kernel de Hongmeng (parámetros variables) | En producción...
- v78.01 Análisis del código fuente del kernel de Hongmeng (arranque) | En producción...
- v79.01 Análisis del código fuente del kernel de Hongmeng (cambio de proceso) | en producción...
- v80.03 Análisis del código fuente del kernel de Hongmeng (cambio de tareas) | Vea cómo el ensamblado cambia de tareas
- v81.05 Análisis del código fuente del núcleo Hongmeng (cambio de interrupción) | El sistema está lleno de vitalidad debido a las interrupciones
- Análisis del código fuente del kernel v82.06 Hongmeng (adquisición anormal) | la sociedad es muy simple y compleja es la gente
- v83.01 Análisis del código fuente del núcleo de Hongmeng (interrupción de falta de página) | En curso...
Este artículo aclara cómo se codifican las ARM
instrucciones , de qué partes se componen las instrucciones de máquina, qué tipos de instrucciones existen y cuál es la sintaxis de cada tipo.
Caso de código | C -> Ensamblaje -> Instrucciones de máquina
Mire la última instrucción de la máquina (armv7) compilada por lenguaje C (clang)
int main(){
int a = 0;
if( a != 1)
a = 2*a + 1;
return a;
}
生成汇编代码如下:
main:
60c: sub sp, sp, #8
610: mov r0, #0
614: str r0, [sp, #4]
618: str r0, [sp]
61c: ldr r0, [sp]
620: cmp r0, #1
624: beq 640 <main+0x34>
628: b 62c <main+0x20>
62c: ldr r1, [sp]
630: mov r0, #1
634: orr r0, r0, r1, lsl #1
638: str r0, [sp]
63c: b 640 <main+0x34>
640: ldr r0, [sp]
644: add sp, sp, #8
648: bx lr
Las instrucciones máquina correspondientes al código de montaje se muestran en la siguiente figura:
Para facilitar el análisis posterior, el código anterior se organiza en la siguiente tabla
codigo ensamblador | Instrucciones de máquina (notación hexadecimal) | Instrucciones de máquina (representación binaria) |
---|---|---|
sub sp, sp, #8 | e24dd008 | 1110 0010 0100 1101 1101 0000 0000 1000 |
movimiento r0, #0 | e3a00000 | 1110 0011 1010 0000 0000 0000 0000 0000 |
str r0, [sp, #4] | e58d0004 | 1110 0101 1000 1101 0000 0000 0000 0100 |
str r0, [sp] | e58d0000 | 1110 0101 1000 1101 0000 0000 0000 0000 |
ldr r0, [sp] | e59d0000 | 1110 0101 1001 1101 0000 0000 0000 0000 |
cmpr0, #1 | e3500001 | 1110 0011 0101 0000 0000 0000 0000 0001 |
beq 640 <principal+0x34> | 0a000005 | 0000 1010 0000 0000 0000 0000 0000 0101 |
b 62c <principal+0x20> | eaffffff | 1110 1010 1111 1111 1111 1111 1111 1111 |
ldr r1, [sp] | e59d1000 | 1110 0101 1001 1101 0001 0000 0000 0010 |
movimiento r0, #1 | e3a00002 | 1110 0011 1010 0000 0000 0000 0000 0001 |
orr r0, r0, r1, lsl #1 | e1800081 | 1110 0001 1000 0000 0000 0000 1000 0001 |
str r0, [sp] | e58d0000 | 1110 0101 1000 1101 0000 0000 0000 0000 |
b 640 <principal+0x34> | eaffffff | 1110 1010 1111 1111 1111 1111 1111 1111 |
ldr r0, [sp] | e59d1000 | 1110 0101 1001 1101 0001 0000 0000 0000 |
agregar sp, sp, #8 | e28dd008 | 1110 0010 1000 1101 1101 0000 0000 1000 |
bx lr | e12fff1e | 1110 0001 0010 1111 1111 1111 0001 1110 |
registro CPSR
Antes de comprender este artículo, debe comprender el significado CPSR
de los bits de orden 4
superior . [31,28]
Para una introducción detallada a los registros, consulte la serie (registros)
N、Z、C、V
Ambos son indicadores de código de condición. Su contenido puede ser cambiado por el resultado de una operación aritmética o lógica, ¡y puede determinar si una instrucción se ejecuta o no!
CPSR
El primer31
bit esN
el bit indicador de signo. Registra si el resultado de la instrucción correspondiente es negativo o no. Si es negativoN = 1
, si no es negativoN = 0
.CPSR
El primer30
bit esZ
el0
bit de bandera. Registra si se ejecuta o no el resultado de la instrucción correspondiente0
. Si el resultado es0
. buenoZ = 1
_ Si el resultado no es0
, entoncesZ = 0
.CPSR
La primera29位
esC
, la bandera de acarreo(Carry)
. En general, se realizan operaciones sobre números sin signo. Operación de suma: cuando el resultado de la operación produce un acarreo (desbordamiento sin signo),C=1
, en caso contrarioC=0
. Resta (inclusiveCMP
): Cuando se produce un préstamo durante la operación (desbordamiento sin signo),C=0
, de lo contrarioC=1
.CPSR
El primer28
bit esV
el indicador de desbordamiento (Overflow
). Al realizar operaciones con números con signo, si supera el rango que la máquina puede identificar, se denomina desbordamiento.
Formato de instrucción
ARM
El flujo de instrucciones es una secuencia de flujos de instrucciones de cuatro bytes alineados con palabras. Cada instrucción ARM es una palabra de un solo 32
bit ( 4
byte), como se muestra en la Figura (3) :
Interpretación La imagen muestra ARM
el formato de codificación de primer nivel de la instrucción.Todas las instrucciones deben ajustarse al formato de primer nivel, que se divide en tres partes:
- Campo de condición: indica las banderas de código
cond[31:28]
de condición a las que afectará el campo de condición .CPSR
N、Z、C、V
- Tipo de campo:
op1[27:25]
,op[4]
,arm
divide la instrucción en seis tipos. - Campo de operación: El resto
[24:5]
,[4:0]
los bits en blanco/bits reservados en la figura, se dejan en el nivel inferior para jugar libremente.Diferentes tipos tienen diferentes definiciones para estos bits reservados. Puede entenderse como un formato secundario que cambia debido a cambios de tipo. - ¿Habrá un formato de tercer nivel con el formato de segundo nivel?La respuesta es sí, el formato de segundo nivel solo definirá algunos bits para los bits reservados, y dejará una parte para que el formato de instrucción específico se reproduzca libremente.
- Es necesario comprender esta estructura jerárquica para comprender
ARM
la idea general del diseño de conjuntos de instrucciones, porque la longitud de instrucción de RISC (conjunto de instrucciones reducido) se fija en16/32/64
bits.Tomando los32
bits como ejemplo, todos los diseños de instrucciones deben32
expresarse en bits . Es difícil que la estructura de capas cumpla con los requisitos de diseño de muchas instrucciones. Para ser flexible e inclusivo, se le debe dar el espacio adecuado para jugar.
Campo de condición
cond
Para el campo de condición, cada instrucción condicional ejecutable condicionalmente tiene 4
un campo de bits de bits condicional, que 2^4
puede representar varias 16
condiciones.
cond | mnemotécnico | Significado (entero) | Significado (flotador) | indicador de condición |
---|---|---|---|---|
0000 | ecualizador | igual | igual | Z == 1 |
0001 | nordeste | distancia | desigual o fuera de orden | Z == 0 |
0010 | CS | llevar | Mayor o igual que o desordenado | C == 1 |
0011 | CC | llevar claro | menos que | C == 0 |
0100 | MI | menos, negativo | menos que | norte == 1 |
0101 | ES | más, positivo o 0 | Mayor o igual que o desordenado | norte == 0 |
0110 | contra | Desbordamiento | trastorno | V == 1 |
0111 | CV | no desbordar | ordenado | V == 0 |
1000 | HOLA | sin firmar mayor que | mayor que o desordenado | C == 1 y Z == 0 |
1001 | LS | sin signo menor o igual que | Menos que o igual a | C == 0 o Z == 1 |
1010 | GE | Firmado mayor o igual que | Mayor qué o igual a | norte == V |
1011 | LT | firmó menos de | menos que o desordenado | N != V |
1100 | GT | Firmado mayor que | más que el | Z == 0 y N ==V |
1101 | LE | Firmado mayor o igual que | menor o igual o desordenado | Z == 1 o N != V |
1110 | ninguna | incondicional | incondicional | ninguna |
- La mayoría de las instrucciones son instrucciones de
1110 = e
ejecución incondicional, siempre que vea que las instrucciones de la máquina ale
principio pertenecen a esta categoría. -
beq 640 <main+0x34> // 机器码 0a000005 <=> 0000 1010 0000 0000 0000 0000 0000 0101 0000 EQ Equal(相等) Z == 1
tipo de campo
图(3) 的 op1
域位于 bits[27:25]
,占三位;op
域位于 bit[4]
,占一位。它们的取值组合在一起,决定指令所属的分类(Instruction Class),其值对应的关系如下
op1 op 指令类型
00x - 数据处理以及杂项指令
010 - load/store word类型 或者 unsigned byte
011 0 同上
011 1 媒体接口指令
10x - 跳转指令和块数据操作指令,块数据操作指令指 STMDA 这类,连续内存操作。
11x - 协处理器指令和 svc 指令,包括高级的 SIMD 和浮点指令。
操作域
操作域是因类型变化而变化的二级格式 ,作用于保留位。包含
00x | 数据处理类指令
- 上图为涉及数据处理指令的对应编码,由
op[占5位]
和op2[占2位]
两项来确定指令的唯一性 - 一般情况下只需
op
指定唯一性,图中SUB
指令对应为0010x
,而代码案例中的第一句
对应sub sp, sp, #8 // 机器码 e24dd008 <=> 1110 001`0 0100` 1101 1101 0000 0000 1000
[24:20]
位就是0 0100
,从而CPU
在译码阶段将其解析为SUB
指令执行 - 需要用到
op2
的是MOV
系列指令,包括逻辑/算术左移右移,例如:
中的mov r0, #0 //e3a00000 <=> 1110 0011 1010 0000 0000 0000 0000 0000
op = 1 1010
,op2 = 00
对应 MOV(register,ARM) on page A8-48900x
中的x
表示数据处理分两种情况000
无立即数参与(寄存器之间) ,图A5.2.1 表示了这种情况[27:25]= 000
001
有立即参与的运算,例如mov r0, #0
中的[27:25]= 001
,此处未展示图,可前往 ARM体系结构参考手册.pdf 翻看
010 | 加载存储指令
-
Load/store
是一组内存访问指令,用来在ARM
寄存器和内存之间进行数据传送,ARM
指令中有3
种基本的数据传送指令- 单寄存器
Load/Store
内存访问指令(single register
):这些指令为ARM寄存器和存储器提供了更灵活的单数据项传送方式。数据可以使字节,16位半字或32位字 - 多寄存器
Load/Store
内存访问指令:可以实现大量数据的同时传送,主要用于进程的进入和退出、保存和恢复工作寄存器以及复制寄存器中的一片(一块)数据 - 寄存器交换指令(
single register swap
): 实现寄存器数据和内存数据进行交换,而且是在一条指令中完成,执行过程中不会受到中断干扰
- 单寄存器
-
出现在代码案例中的
str r0, [sp, #4] // 机器码 e58d0004 <=> 1110 0101 1000 1101 0000 0000 0000 0100 str r0, [sp] // 机器码 e58d0000 <=> 1110 0101 1000 1101 0000 0000 0000 0000 将r0中的字数据写入以SP为地址的存储器中 ldr r0, [sp] // 机器码 e59d0000 <=> 1110 0101 1001 1101 0000 0000 0000 0000 存储器地址地址为SP的数据读入r0 寄存器
[27:25] = 010
说明都属于这类指令,完成对内存的读写,包括LDR
、LDRB
、LDRH
、STR
、STRB
、STRH
六条指令。ldr
为加载指令,但是加载到内存还是寄存器,这该怎么记 ? 因为主角是CPU
,加载有进来的意思,将内容加载至寄存器中。STR
有出去的意思,将内容保存到内存里。[sp]
相当于C
语言的*sp
,sp
指向程序运行栈当前位置
010 | 多媒体指令
多媒体指令使用较少,但是它涉及指令却很多
10x | 跳转/分支/块数据处理 指令
- 出现在代码案例中的
beq 640 <main+0x34> // 机器码 0a000005 <=> 0000 1010 0000 0000 0000 0000 0000 0101 b 62c <main+0x20> // 机器码 eaffffff <=> 1110 1010 1111 1111 1111 1111 1111 1111
[27:25] = 101
说明都属于这类指令 - 听得很多的
pop
,push
也属于这类,成块的数据操作,例如push
常用于将函数的所有参数一次性入栈。 - 内存 <> 寄存器 批量数据搬运指令
STMDA (STMED)
LDMDA/LDMF
。
11x | 软中断/协处理器 指令
- 其中最有名的就是
svc 0
,在系列篇中曾多次提及它,此处详细说下svc
,svc
全称是Supervisor Call
,Supervisor
是CPU
的管理模式,svc
导致处理器进入管理模式,很多人问的系统调用底层是怎么实现的?svc
就是答案。 - 例如
printf
是个标准库函数,在标准库的底层代码中会调用svc 0
,导致用户态的ARM
程序通常将系统调用号传入R7
寄存器(也被鸿蒙内核使用),然后用SVC
指令调用0
号中断来直接执行系统调用, - 在以前的ARM架构版本中,
SVC
指令被称为SWI
,软件中断。 - 描述
svc
功能的详细伪代码如下,请尝试读懂它The TakeSVCException() pseudocode procedure describes how the processor takes the exception: // TakeSVCException() // ================== TakeSVCException() // Determine return information. SPSR is to be the current CPSR, after changing the IT[] // bits to give them the correct values for the following instruction, and LR is to be // the current PC minus 2 for Thumb or 4 for ARM, to change the PC offsets of 4 or 8 // respectively from the address of the current instruction into the required address of // the next instruction, the SVC instruction having size 2bytes for Thumb or 4 bytes for ARM. ITAdvance(); new_lr_value = if CPSR.T == '1' then PC-2 else PC-4; new_spsr_value = CPSR; vect_offset = 8; // Check whether to take exception to Hyp mode // if in Hyp mode then stay in Hyp mode take_to_hyp = (HaveVirtExt() && HaveSecurityExt() && SCR.NS == '1' && CPSR.M == '11010'); // if HCR.TGE is set to 1, take to Hyp mode through Hyp Trap vector route_to_hyp = (HaveVirtExt() && HaveSecurityExt() && !IsSecure() && HCR.TGE == '1' && CPSR.M == '10000'); // User mode // if HCR.TGE == '1' and in a Non-secure PL1 mode, the effect is UNPREDICTABLE preferred_exceptn_return = new_lr_value; if take_to_hyp then EnterHypMode(new_spsr_value, preferred_exceptn_return, vect_offset); elsif route_to_hyp then EnterHypMode(new_spsr_value, preferred_exceptn_return, 20); else // Enter Supervisor ('10011') mode, and ensure Secure state if initially in Monitor // ('10110') mode. This affects the Banked versions of various registers accessed later // in the code. if CPSR.M == '10110' then SCR.NS = '0'; CPSR.M = '10011'; // Write return information to registers, and make further CPSR changes: IRQs disabled, // IT state reset, instruction set and endianness set to SCTLR-configured values. SPSR[] = new_spsr_value; R[14] = new_lr_value; CPSR.I = '1'; CPSR.IT = '00000000'; CPSR.J = '0'; CPSR.T = SCTLR.TE; // TE=0: ARM, TE=1: Thumb CPSR.E = SCTLR.EE; // EE=0: little-endian, EE=1: big-endian // Branch to SVC vector. BranchTo(ExcVectorBase() + vect_offset);
- 这部分内容在系列篇 (寄存器篇) ,(系统调用篇) ,(标准库篇) 中都有提及。
具体指令
细看几条代码案例出现的常用指令
sub sp, sp, #8
sub sp, sp, #8 // 机器码 e24dd008 < = > 1110 0010 0100 1101 1101 0000 0000 1000
是减法操作指令,减法编码格式为
图中除了给出格式语法还有一段伪代码用于描述指令的使用条件
-
sp
为13
号寄存器,lr
为14
号寄存器 ,pc
为15
号寄存器。 -
如果是
PC
寄存器(Rn = 15)
且S
等于0
查看ADR
指令。。 -
如果是
SP
寄存器(Rn = 13)
看SUB
(申请栈空间)。 -
如果是
PC
寄存器(Rd = 15)
且S
等于1
。查看subs
pc
lr
相关指令 -
套用格式结合源码
cond op1 操作码 S Rn Rd imm12(立即数) 1110 001 0010 0 1101 1101 0000 0000 1000 无条件执行 表示数据处理 SUB sp sp 8
mov r0, #0
mov r0, #0 //e3a00000 1110 0011 1010 0000 0000 0000 0000 0000
bx lr
bx lr e12fff1e 1110 0001 0010 1111 1111 1111 0001 1110
Rm = 1110
对应lr
寄存器 ,其相当于高级语言的return
,函数执行完了需切回到调用它的函数位置继续执行,lr
保存的就是那个位置,从哪里来就回到哪里去。
百文说内核 | 抓住主脉络
- 百文相当于摸出内核的肌肉和器官系统,让人开始丰满有立体感,因是直接从注释源码起步,在加注释过程中,每每有心得处就整理,慢慢形成了以下文章。内容立足源码,常以生活场景打比方尽可能多的将内核知识点置入某种场景,具有画面感,容易理解记忆。说别人能听得懂的话很重要! 百篇博客绝不是百度教条式的在说一堆诘屈聱牙的概念,那没什么意思。更希望让内核变得栩栩如生,倍感亲切。
- 与代码需不断
debug
一样,文章内容会存在不少错漏之处,请多包涵,但会反复修正,持续更新,v**.xx
代表文章序号和修改的次数,精雕细琢,言简意赅,力求打造精品内容。 - 百文在 < 鸿蒙研究站 | 开源中国 | 博客园 | 51cto | csdn | 知乎 | 掘金 > 站点发布,鸿蒙研究站 | weharmonyos 中回复 百文 可方便阅读。
按功能模块:
- 基础知识 >> 双向链表 | 内核概念 | 源码结构 | 地址空间 | 计时单位 | 宏的使用 | 钩子框架 | 位图管理 | POSIX | main函数 |
- 进程管理 >> 调度故事 | 进程控制块 | 进程空间 | 线性区 | 红黑树 | 进程管理 | Fork进程 | 进程回收 | Shell编辑 | Shell解析 |
- 任务管理 >> 任务控制块 | 并发并行 | 就绪队列 | 调度机制 | 任务管理 | 用栈方式 | 软件定时器 | 控制台 | 远程登录 | 协议栈 |
- 内存管理 >> 内存规则 | 物理内存 | 虚拟内存 | 虚实映射 | 页表管理 | 静态分配 | TLFS算法 | 内存池管理 | 原子操作 | 圆整对齐 |
- 通讯机制 >> 通讯总览 | 自旋锁 | 互斥锁 | 快锁使用 | 快锁实现 | 读写锁 | 信号量 | 事件机制 | 信号生产 | 信号消费 | 消息队列 | 消息封装 | 消息映射 | 共享内存 |
- 文件系统 >> 文件概念 | 文件故事 | 索引节点 | VFS | 文件句柄 | 根文件系统 | 挂载机制 | 管道文件 | 文件映射 | 写时拷贝 |
- 硬件架构 >> 芯片模式 | ARM架构 | 指令集 | 协处理器 | 工作模式 | 寄存器 | 多核管理 | 中断概念 | 中断管理 |
- 内核汇编 >> 编码方式 | 汇编基础 | 汇编传参 | 可变参数 | 开机启动 | 进程切换 | 任务切换 | 中断切换 | 异常接管 | 缺页中断 |
- 编译运行 >> 编译过程 | 编译构建 | GN语法 | 忍者无敌 | ELF格式 | ELF解析 | 静态链接 | 重定位 | 动态链接 | 进程映像 | 应用启动 | 系统调用 | VDSO |
- 调测工具 >> 模块监控 | 日志跟踪 | 系统安全 | 测试用例 |
- 前因后果 >> 总目录 | 源码注释 | 静态站点 | 参考手册 |
百万注源码 | 处处扣细节
-
百万汉字注解内核目的是要看清楚其毛细血管,细胞结构,等于在拿放大镜看内核。内核并不神秘,带着问题去源码中找答案是很容易上瘾的,你会发现很多文章对一些问题的解读是错误的,或者说不深刻难以自圆其说,你会慢慢形成自己新的解读,而新的解读又会碰到新的问题,如此层层递进,滚滚向前,拿着放大镜根本不愿意放手。
-
< gitee | github | codificación | gitcode > Empuje del almacén de cuatro códigos | Sincronice el código fuente oficial, estación de investigación de Hongmeng | Responda un millón weharmonyos para facilitar la lectura.
Se dice que las personas a las que les gusta gustar y compartir se han convertido en dioses más tarde. :)