乒乓球世界杯_世界杯结束时间 - 0123838.com
首页世界杯意大利正文

鸿蒙内核源码分析 (编码方式篇)

2025-09-21 11:48:04

本篇说清楚 ARM指令是如何被编码的,机器指令由哪些部分构成,指令有哪些类型,每种类型的语法又是怎样的 ?

代码案例 | C -> 汇编 -> 机器指令

看一段C语言编译(clang)成的最后的机器指令(armv7)

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

628: b 62c

62c: ldr r1, [sp]

630: mov r0, #1

634: orr r0, r0, r1, lsl #1

638: str r0, [sp]

63c: b 640

640: ldr r0, [sp]

644: add sp, sp, #8

648: bx lr

汇编代码对应的机器指令如下图所示:

便于后续分析,将以上代码整理成如下表格

汇编代码

机器指令(十六进制表示)

机器指令(二进制表示)

sub sp, sp, #8

e24dd008

1110 0010 0100 1101 1101 0000 0000 1000

mov 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

cmp r0, #1

e3500001

1110 0011 0101 0000 0000 0000 0000 0001

beq 640

0a000005

0000 1010 0000 0000 0000 0000 0000 0101

b 62c

eaffffff

1110 1010 1111 1111 1111 1111 1111 1111

ldr r1, [sp]

e59d1000

1110 0101 1001 1101 0001 0000 0000 0010

mov 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

eaffffff

1110 1010 1111 1111 1111 1111 1111 1111

ldr r0, [sp]

e59d1000

1110 0101 1001 1101 0001 0000 0000 0000

add sp, sp, #8

e28dd008

1110 0010 1000 1101 1101 0000 0000 1000

bx lr

e12fff1e

1110 0001 0010 1111 1111 1111 0001 1110

CPSR寄存器

在理解本篇之前需了解下CPSR寄存器的高4位[31,28] 表达的含义。关于寄存器的详细介绍可翻看 系列篇的 (寄存器篇)

N、Z、C、V均为条件码标志位。它们的内容可被算术或逻辑运算的结果所改变,并且可以决定某条指令是否被执行!意义重大!

CPSR的第31位是 N,符号标志位。它记录相关指令执行后,其结果是否为负。

如果为负 N = 1,如果是非负数 N = 0。

CPSR的第30位是Z,0标志位。它记录相关指令执行后,其结果是否为0。

如果结果为0。那么Z = 1。如果结果不为0,那么Z = 0。

CPSR的第29位是C,进位标志位(Carry)。一般情况下,进行无符号数的运算。

加法运算:当运算结果产生了进位时(无符号数溢出),C=1,否则C=0。

减法运算(包括CMP):当运算时产生了借位时(无符号数溢出),C=0,否则C=1。

CPSR的第28位是V,溢出标志位(Overflow)。在进行有符号数运算的时候,

如果超过了机器所能标识的范围,称为溢出。

指令格式

ARM 指令流是一连串的字对齐的四字节指令流。每个 ARM 指令是一个单一的 32 位字(4字节),如图(3):

解读

图为ARM指令的编码一级格式,所有的指令都必须符合一级格式,分成三部分:

条件域: cond[31:28]表示,条件域会影响CPSR的条件码N、Z、C、V标志位。

类型域: op1[27:25], op[4],arm将指令分成了六大类型 。

操作域: 剩下的[24:5],[4:0] 即图中的空白位/保留位,这是留给下级自由发挥的,不同的类型对这些保留位有不同的定义。可以理解为因类型变化而变化的二级格式。

那有了二级格式会不会有三级格式 ? 答案是必须有, 二级格式只会对保留位定义部分位,会留一部分给具体的指令格式自由发挥。

一定要理解这种层次结构才能理解ARM指令集的设计总思路,因为RISC(精简指令集) 的指令长度是固定的16/32/64位,以32位为例,所有的指令设计必须全用32位来表示,如果只有一层结构是难以满足众多的指令设计需求的,要灵活有包容就得给适当的空间发挥。

条件域

cond 为条件域,每一条可条件执行的条件指令都有4位的条件位域,2^4能表示16种条件

cond

助记符

含义(整型)

含义(浮点型)

条件标志

0000

EQ

相等

相等

Z == 1

0001

NE

不等

不等或无序

Z == 0

0010

CS

进位

大于等于或无序

C == 1

0011

CC

进位清除

小于

C == 0

0100

MI

减、负数

小于

N == 1

0101

PL

加、正数或 0

大于等于或无序

N == 0

0110

VS

溢出

无序

V == 1

0111

VC

未溢出

有序

V == 0

1000

HI

无符号大于

大于或无序

C == 1 and Z == 0

1001

LS

无符号小于或等于

小于或等于

C == 0 or Z == 1

1010

GE

有符号大于或等于

大于或等于

N == V

1011

LT

有符号小于

小于或无序

N != V

1100

GT

有符号大于

大于

Z == 0 and N ==V

1101

LE

有符号大于或等于

小于等于或无序

Z == 1 or N != V

1110

无条件

无条件

任何

大部分的指令都是 1110 = e,无条件执行指令,只要看到 e开头的机器指令都属于这类

beq 640 // 机器码 0a000005 <=> 0000 1010 0000 0000 0000 0000 0000 0101

0000 EQ Equal(相等) Z == 1

类型域

图(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-489 00x中的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 指向程序运行栈当前位置

具体可看 >> ARM的六条访存指令集---LDR、LDRB、LDRH、STR、STRB、STRH

010 | 多媒体指令

多媒体指令使用较少,但是它涉及指令却很多

10x | 跳转/分支/块数据处理 指令

出现在代码案例中的

beq 640 // 机器码 0a000005 <=> 0000 1010 0000 0000 0000 0000 0000 0101

b 62c // 机器码 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保存的就是那个位置,从哪里来就回到哪里去。

写在最后

如果你觉得这篇内容对你还蛮有帮助,我想邀请你帮我三个小忙:

点赞,转发,有你们的 『点赞和评论』,才是我创造的动力。

关注小编,同时可以期待后续文章ing🚀,不定期分享原创知识。

想要获取更多完整鸿蒙最新学习知识点,请移步前往小编:https://gitee.com/MNxiaona/733GH/blob/master/jianshu

【心得】遺失的海洋卷軸 逆战游戏能联系客服吗
相关内容