MDK环境的ARM汇编中内存对齐与对ADR Rd,{PC}+n形式的理解

来源:互联网 发布:编程什么意思 编辑:程序博客网 时间:2024/05/24 03:23

在使用ARM汇编的过程中,经常因内存对齐的问题,引发各种奇怪的错误,

以及在使用MDK环境编写Cortex-M3的汇编语言时

ADR Rd,label
汇编成一条与PC有关的指令,并表现为如下形式:

ADR Rd,{PC}+n ;其中n的取值不是2,就是4

这经常让人产生疑惑,因为不知道{PC}到底是一种什么样的表达。

现在,通过一段测试代码,来看看内存对齐是如何引发错误的,以及通过它的反汇编,说明{PC}的性质。

代码如下:

上述代码将会产生两个错误:

User\base.s(22): error: A1867E: Immediate 0x00000006 out of range for this operation.  Permitted values are multiples of 4 from 0x00000000 to 0x000003FF

User\base.s(23): error: A1875E: Register Rn must be from R0 to R7 in this instruction

这两个错误其实就是一个错误,都是非对齐访问引起的。

首先,对于ADR和LDR与标号有关的内存访问,在不加.W后缀的情况下,都只能汇编成16位的Thumb指令。

因此,22行与23行的两条指令都是16位的。由于他们的标号都会汇编成PC与相对于PC的偏移量构成。

由于16位编码的原因,偏移量由8位表示,且根据规定,只能是0~1020之间4的倍数。

显然,22行的指令地址为0x08000014,而标号SUM的地址为0x0800001E,相差为10

(考虑到指令流水线,PC的值相差10-4=6,ADR  R2,SUM将用指令ADD R2,PC,#6代替,显然6不能被4整除

因此无法生成相对于PC的ADD指令,编译器报告错误:立即数0x00000006超出该操作的范围。允许的值是0x00000000到0x000003FF间4的倍数

23行的指令地址为0x08000016,与标号SUM的地址0x0800001E相差为8,这样看来是能被4整除的,但是怎么也报错?

其实,这就是内存对齐的问题了。执行时,取出PC,先将它字对齐,这样0x08000016就变成了0x08000014,实际上还是与SUM像差10(考虑到流水线机制,PC值相差6)

这样,LDR  R3,SUM将使用LDR R3,[PC,#6]代替,该指令编码如下:

      LDR  Rt ,[PC,#<imm8>]

编译器从低位到高位编码指令,8位立即数<imm8>要求是0~1020间4的倍数,显然,指令中的立即数6不合法!

由于LDR指令有两种编码格式,编译器尝试第二种格式。

这种编码格式如下:

      LDR  Rt,[Rn,#imm5]

编译器从低位到高位编码指令,我们看到,Rt与Rn只有3位的编码,这意味着,它们只能在R0~R7之间选择。

首先,该指令的Rt选择了R3,而Rn确是PC(R15),超过了允许范围,汇编至此停止,编译器报告错误:该指令的寄存器Rn必须在R0到R7之间

解决的办法很简答,在这两条指令后加一条NOP指令(半字),使指令与标号SUM的PC值相差增加2,由6变为8,能被4整除!

正确代码如下:


现在,来关注反汇编窗口

结合ADR指令的16位编码格式:
   ADR  Rd ,<label>  即 ADD  Rd ,PC,#<imm8>

分析红色框中的机器码:
A201:1010001000000001       编码中imm8=1,指令中<imm8>=4*imm8=4
A200:1010001000000000       编码中imm8=0,指令中<imm8>=4*imm8=0
A202:1010001000000010       编码中imm8=2,指令中<imm8>=4*imm8=8
我们得知:这三条指令分别解释为:
ADD  R2,PC,#4ADD  R2,PC,#0ADD  R2,PC,#8
考虑到指令流水线机制,将上述三条加法指令计算的结果都加上4,非字对齐结果按字对齐,就得到存入R2中数据的值,也就是反汇编窗口中@符号后面的值
仔细分析反汇编窗口中的三条指令:
ADR  r2,{pc}+4ADR  r2,{pc}+2ADR  r2,{pc}+4
可以发现,其实{PC}就是表示PC的当前值(没有采取任何对齐操作)
而后面加的数字,是有规律的
当指令在字对齐边界上时,如在地址0x08000010和地址0x08000014处,该数字是4
在半字对齐边界上时,如在地址0x08000012处,该数字是2

这个数字的目的在于将PC补成字对齐的,而且已经考虑到了指令流水线对PC的影响,之后再与8位的立即数相加!直接得到结果。

我们来看彩色框中的四个部分,分别是{PC}的值,立即数<imm8>的编码值,{PC}之后的数字,和r2中的结果


分析如下:

r2 = {pc} + 4*imm8 + 4 = 0x08000010 + 4*1 + 4 =0x08000018

r2 = {pc} + 4*imm8 + 2 = 0x08000012 + 4*0 + 2 = 0x08000014

r2 = {pc} + 4*imm8 + 4 = 0x08000014 + 4*2 + 4 = 0x08000020

最后:代码窗口中第21行的指令

LDR  R2,{PC}+2
不同于反汇编窗口中的表达,它是一条伪指令,表示将PC的当前值+2载入到R2中。

该伪指令后面的数字不受限制,即可以是

LDR  R2,{PC}+n   ;n是寻址范围内的任意值
对于反汇编窗口或汇编源文件中出现的,形如:

LDR R2,{PC}-n 
的指令,参照上述是+的两种情况对应分析。





0 0