首先来看一段指令和它的汇编结果:
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情况下使用