深度剖析ARM内核寄存器及基本汇编语言1

电子说

1.2w人已加入

描述

对于嵌入式开发者来说,了解汇编语言和内核寄存器是对内核深入理解的基础
 ..增加 2.2 汇编伪指令 章节					2021/12/12
 ..完善 2.3 ARM汇编指令集						2021/12/12
 ..增加 3.1 不同编译器的反汇编					2021/12/14 
 ..增加 3.2 C和汇编 比较分析				        2021/12/15 
 从开始写起也没想到内容有这么多,其中有很多干货的东西,希望自己能够说明到了,
 其中有很多推荐的博文和网站,在此要特别感谢韦东山老师的视频,绝对干货满满
  • 一、ARM内核寄存器
    • 1.1 M3/M4内核寄存器
    • 1.2 A7内核寄存器
    • 1.3 ARM中的PC指针的值
  • 二、ARM汇编语言
    • 2.1 ARM汇编基础
    • 2.2 汇编伪指令
    • 2.3 ARM汇编指令集
  • 三、代码反汇编简析
    • 3.1 不同编译器的反汇编
    • 3.2 C 和 汇编 比较分析

开头直接来看几个简单的汇编指令:

MOV R0,R1``MOV PC,R14

上面的指令中使用了汇编 MOV指令,但是其中的 R0,R1,R14,PC分别是什么?哪来的?怎么用?

要讲 ARM 汇编语言,必须得先了解ARM的内核寄存器,内核处理所有的指令计算,都需要用到内核寄存器,所以ARM汇编里面指令大都是基于寄存器的操作。

文章前推荐韦东山老师的单片机核心视频,视频可以在韦东山老师官网里面找到:百问网

ARM版本简单介绍:

内核(架构)版本 处理器版本
ARMv1 ARM1
ARMv2 ARM2、ARM3
ARMv3 ARM6、
ARMv4 ARM7、StrongARM
ARMv5 ARM9、ARM10E
ARMv6 ARM11
ARMv7 ARM Cortex-A、ARM Cortex-M、ARM Cortex-R
ARMv8 ARM Cortex-A30、ARM Cortex-A50、ARM Cortex-A70

一、ARM内核寄存器

内核寄存器与外设寄存器:

内核寄存器与外设寄存器是完全不同的概念。内核寄存器是指 CPU 内部的寄存器,CPU处理所有指令数据需要用到这些寄存器保存处理数据;外设寄存器是指的 串口,SPI,GPIO口这些设备有关的寄存器。

在我的另一篇博文:FreeRTOS记录(三、FreeRTOS任务调度原理解析_Systick、PendSV、SVC)内核中断管理 章节讲到过Cortex-M的寄存器的相关内容,这里我们再简单说明一下:

1.1 M3/M4内核寄存器

对于M3/M4而言:汇编语言R13,栈指针(Stack Pointer)

  • R13寄存器中存放的是栈顶指针,M3/M4 的栈是向下生长的,入栈的时候地址是往下减少的。
  • 裸机程序不会用到PSP,只用到MSP,需要运行RTOS的时候才会用到PSP。
  • 堆栈主要是通过POP,PUSH指令来进行操作。在执行 PUSH 和 POP 操作时, SP 的地址寄存器,会自动调整。

R14 ,连接寄存器(Link Register)

  • LR 用于在调用子程序时存储返回地址。例如,在使用 BL(分支并连接, Branch and Link)指令时,就自动填充 LR 的值(执行函数调用的下一指令),进而在函数退出时,正确返回并执行下一指令。如果函数中又调用了其他函数,那么LR将会被覆盖,所以需要先将LR寄存器入栈。
  • 保存子程序返回地址。使用BL或BLX时,跳转指令自动把返回地址放入r14中;子程序通过把r14复制到PC来实现返回
  • 当异常发生时,异常模式的r14用来保存异常返回地址,将r14如栈可以处理嵌套中断

R15,程序计数器(Program Count)

  • 在Cortex-M3中指令是3级流水线,出于对Thumb代码的兼容的考虑,读取pc时,会返回当前指令地址+4的值。
  • 读 PC 时返回的值是当前指令的地址+4,关于M3、M4 和 A7的 PC值的问题需要单独来解释一下

其中程序状态寄存器 XPSR:汇编语言

程序状态寄存器,该寄存器由三个程序状态寄存器组成 应用PSR(APSR) :包含前一条指令执行后的条件标志,比较结果:大于等于,小于,进位等等; 中断PSR(IPSR ) :包含当前ISR的异常编号 执行PSR(EPSR) :包含Thumb状态位

1.2 A7内核寄存器

对于 A7 而言:汇编语言(上图取自原子教材,此图在官方文档《ARM Cortex-A(armV7)编程手册V4.0》中第3章.ARM Processor Modes and Registers 部分有英文原版,这里用中文版本更容易理解)

A7的 R13、R14、R15 的作用和 M3/4类似。

需要注意的一点就是,对于A7而言 R15,程序计数器(Program Count)

  • 读 PC 时返回的值是当前指令的地址+8, PC 指向当前指令的下两条指令地址。
  • 由于ARM指令总是以字对齐的,故PC寄存器 bit[1:0] 总是00。

A7内核的程序状态寄存器 CPSR:汇编语言

1.3 ARM中的PC指针的值

因为ARM指令采用三级流水线机制,所以PC指针的值并不是当前执行的指令的地址值:

  1. 当前执行地址A的指令,
  2. 同时已经在对下一条指令进行译码,
  3. 同时已经在读取下下一条指令:PC = A +4 (Thumb/Thumb2指令集)、PC = A + 8 (ARM指令集)

在文档《ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition》中对于 PC 的值有明确的说明:汇编语言

M3/M4/M0:

PC的值 = 当前地址 + 4;

下面是一个 STM32F103 反汇编程序,找了一段有[pc,#0]的代码,方便判断:汇编语言

A7:

PC的值 = 当前地址 + 8;

汇编语言

二、ARM汇编语言

ARM芯片属于精简指令集计算机(RISC:Reduced Instruction Set Computing),具体说明在下面这篇博文5.4小结有过说明:

STM32的内存管理相关(内存架构,内存管理,map文件分析)

2.1 ARM汇编基础

2.1.1 ARM指令集说明

最初,ARM公司发布了两类指令集:

  1. ARM指令集,32位的ARM指令,每条指令占据32位,高效,但是太占空间;
  2. Thumb指令集,16位的Thumb指令,每条指令占据16位,节省空间;

比如:MOV R0,R1 这条指令,可能是16位的,也可能是32位的

那么在汇编中是如何在 ARM 指令 和 Thumb 指令之间切换呢:

/*ARM指令 与 Thumb 指令 的切换*/

CODE16  ;(表示下面是 Thumb 指令)
...
...

;(调用下面的B函数)
bx  B_addr;(B的地址B_addr的bit0 = 0,表示跳转过去执行 ARM 指令)
;A 函数
...

CODE32  ;(表示下面是 ARM 指令)
...
...
;B 函数
;(回到上面的A函数)
bx  A_addr + 1 ;(A的地址A_addr的bit0 = 1,表示跳转过去执行 Thumb 指令)
...

/**********************/

对于A7、ARM7、ARM9 内核而言它们支持 16位的Thumb 指令集 和 32位的 ARM 指令集

对于M3、M4 内核而言它们支持的是 Thumb2 指令集,它支持16位、32位指令混合编程

对于内核来说使用的是 ARM指令集 还是 Thumb指令集,就是在 XPSR 和 CPSR

在M3/M4中, XPSR 寄存器的 T(bit24):1表示 Thumb指令集汇编语言根据上面所述,M3是使用的 Thumb2 指令集,所以会有 T 总是 1.

在A7中 CPSR中的:T(bit5) :控制指令执行状态,表明本指令是 ARM 指令还是 Thumb 指令,通常和 J(bit24)一起表明指令类型汇编语言

J(bit24) T(bit5) 指令集
0 0 ARM
0 1 Thumb
1 1 ThumbEE -- 提供从Thumb-2而来的一些扩充性,在所处的运行环境下,使得指令集能特别适用于运行阶段的编码产生(例如实时编译)。Thumb-2EE是专为一些语言如Limbo、Java、C#、Perl和Python,并能让实时编译器能够输出更小的编译码却不会影响到性能。
1 0 Jazelle

回到开始的指令 MOV R0,R1

code 16  ;(表示下面指令是16位的 Thumb 指令)
MOV R0,R1
code 32  ;(表示下面指令是32位的 ARM 指令)
MOV R0,R1
Thumb    ;(编译器会根据指令自动识别是32位还是16位的 Thumb2MOV R0,R1

2.1.2 ARM汇编格式

编码格式:

不同指令集的编码格式(以 LDR 为例),摘自《ARM ArchitectureReference Manual ARMv7-A and ARMv7-R edition》:汇编语言以“数据处理”(其他的还有内存访问,分支跳转等)指令为例,UAL汇编格式为:汇编语言Operation

表示各类汇编指令,比如 ADD、MOV;cond表示conditon,即该指令执行的条件,如 EQ,NE 等;S表示该指令执行后,是否会影响CPSR寄存器的值, 是否影响CPSR 寄存器的值,书写时影响CPSR,否则不影响;Rd

为目的寄存器,用来存储运算的结果;Rn 第一个操作数的寄存器Operand2第二个操作数 ,其可以有3种操作源:1-- 立即数 2-- 寄存器 3-- 寄存器移位

其指令编码格式如下(32位):|bit 31-28 |27-25 |24-21 |20 |19-16 | 15-12 |11-0 | |--|--|--|--|--|--|--|--|--| |cond | 001 |Operation |S |Rn |Rd | Operand2 |

举个例子:

...
CMP R0,R2      ;比较R0和R2的值
MOV EQ R0,R1  ;加上EQ,如果上面R0的值和R2的值相等的话,才执行此语句
...

对于“数据处理”处理指令中的Operation ,指令集如下:汇编语言对于其中的条件cond ,如下:汇编语言

2.1.3 立即数

在一条ARM数据处理指令中,除了要包含处理的数据值外,还要标识ARM命令名称,控制位,寄存器等其他信息。这样在一条ARM数据处理指令中,能用于表示要处理的数据值的位数只能小于32位;

在上面的ARM汇编格式中我们介绍过,ARM在指令格式中设定,只能用指令机器码32位中的低12位来表示要操作的常数。汇编语言

那么对于指令MOV R0, #value(把value的值存入R0寄存器)而言,value 的值也不能是任意的值,其值只能是符合某些规定的数,在官方文档中 value 的值需要满足如下条件:汇编语言什么是立即数?

满足上图中条件的数我们称之为 立即数,立即数就是符合一定规矩的数。

立即数表示方式:每个立即数由一个8位的常数循环右移偶数位得到。其中循环右移的位数由一个4位二进制的两倍表示。

立即数 = 一个8位的常数 循环位移 偶数位

一个8bit常数循环右移(Y*2 = {0,2,4,6,8, ...,26, 28, 30})就得到一个立即数了;(为什么是0到30的偶数下面解释)

如果需要深入理解立即数,推荐一篇博文:深刻认识 -->> 立即数

ARM处理器是按32位来处理数据的,ARM处理器处理的数据是32位,为了扩展到32位,因此使用了构造的方法,在12位中用8位表示基本数据值,用4位表示位移值,通过用8位基本数据值往右循环移动4位位移值*2次,来表示要操作的常数。

这里要强调最终的循环次数是4位位移值乘以2得到的,所以得到的最终循环次数肯定是一个偶数,为什么要乘以2呢,实质还是因为范围不够,4位表示位移次数,最大才15次(移位0,等于没有循环),加上8位数据还是不够32位,这样只能通过ALU的内部结构设计将4位位移次数乘以2,这样就能用12位表示32位常数了。

所以 12bit 数据存放格式如下:|bit 11-8 |7-0 | |--|--|--|--|--|--|--|--|--| |移位 1111b (0~15) | 8bit常数 |

但是我们去判断一个数是否立即数,实在是太麻烦了,但是我们想把任意数值赋给 R0 寄存器,怎么办? 这就需要用到伪指令了,下面说一说什么是伪指令。

打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分