adr,ldr指令的区别

首先来看一段指令和它的汇编结果:

AREA test,CODE,READONLY
        ENTRY
        ldr r0,_start
        adr r0,_start
        ldr r0,=_start
        nop

_start
        nop
        END

汇编结果如下图:

首先,当执行第一条指令时,pc的指向是0x8008

_start的值是0x8010,相差为8,所以,ldr指令是将通过这个差值来寻找,加载的是数据,不是地址值。

执行第二条指令时,pc指向0x800c,跟_start指令相差为4,所以这个adr指令也是通过差值来寻找。

r0=当前pc值加上(算出来的当前pc值和_start地址之间的差值)

当前pc值和_start地址之间的差值是已经确定的,当程序运行在不同的地址上,这个r0会随之发生变化,但是

最终都会正确找到这个_start的地址值。

进行第三条指令分析之前,先来看一下伪指令,ldr r0,=start

这条指令在编译的时候,会占两条指令的空间,因为通常ldr加载的不是一个立即数,超出立即数的范围,所以需要另外的32bit空间来存储这个数据。

我们来看一下这条指令的编译结果:

ldr r0 ,[pc,#0x0004]

(即使pc发生变化,只要使得pc+04这个地址中的数据,也就是r0实际的值,也就是_start的值确定下来,那么r0就不会发生变化)

是这个地址中存储的数据就是_start的地址,

首先,看一下当前的pc值是0x8010,加上4之后为0x8014,而在0x8014位置中存储的是0x8010这个数据

而0x8010这个数据就是_start的数值,所以,可以说0x8014这个地址中存储的就是_start这个数据,也就是ldr伪指令

这也就是说无论pc运行在哪个地址空间,ldr中的r0的值永远不会发生变化。

文字池所占得第二个空间地址。

第一点注意:pc值比当前执行指令值大8

第二点注意:实际上,ldr r0,=_start可以转化为:

LDR      rn, [pc, #offset to literal pool] 的形式,直接去文字池所在的地址去找到这个地址。

且对于ldr伪指令,这个偏移范围在ARM状态下<4KB,且这个偏移的方向可以向前可以向后

在THUMB状态下,这个偏移范围<1KB,且只能向前偏移。

第三点注意:adr指令通常转化为一个add或者sub指令

可以加载的偏移地址的范围

在字对齐的情况下,是不超过+-255bytes,在非字对齐的情况下是不超过+-1020bytes

这里要求的都是字对齐,ARM中字是32位。

而对于adrl指令,它的跳转范围更大一点:

当字不对其的情况下是+-64kB,当字对齐的情况下是+-256KB,且没有在thumb情况下使用

猜你喜欢

转载自blog.csdn.net/chengchaonan/article/details/84996221
LDR