3.7. 关于为何不直接用mov指令,而非要用adr伪指令

在分析uboot的start.S中,看到一些指令,比如:

adr r0, _start

觉得好像可以直接用mov指令实现即可,为啥还要这么麻烦地,去用ldr去实现?

关于此处的代码,为何要用adr指令:

adr r0, _start
[注意] adr r0, _start会被翻译为真正的汇编指令

其被编译器编译后,会被翻译成:

sub	r0, pc, #172

而不直接用mov指令直接将_start的值赋值给r0,类似于这样:

mov r0, _start

呢?

其原因主要是,

sub	r0, pc, #172

这样的代码,所处理的值,都是相对于PC的偏移量来说的,这样的代码中,没有绝对的物理地址值,都是相对的值,利用产生位置无关代码。因为如果用mov指令:

mov r0, _start

那么就会被编译成这样的代码:

mov r0, 0x33d00000

如果用了上面这样的代码:

mov r0, 0x33d00000

那么,如果整个代码,即要执行的程序的指令,被移动到其他位置,那么

mov r0, 0x33d00000

这行指令,执行的功能,就是跳转到绝对的物理地址,而不是跳转到相对的_start的位置了,就不能实现我们想要的功能了,这样包含了绝对物理地址的代码,也就不是位置无关的代码了。

与此相对,这行指令:

sub	r0, pc, #172

即使程序被移动到其他位置,那么该行指令还是可以跳转到相对PC往前172字节的地方,也还是我们想要的_start的位置,这样包含的都是相对的偏移位置的代码,就叫做位置无关代码。其优点就是不用担心你的代码被移动,即使程序的基地址变了,所有的代码的相对位置还是固定的,程序还是可以正常运行的。

关于,之所以不用上面的:

mov r0, 0x33d00000

类似的代码,除了上面说的,不是位置无关的代码之外,其还有个潜在的问题,那就是,关于mov指令的源操作数,此处即为0x33d00000,不一定是合法的mov 指令所允许的值,这也正是下面要详细解释的内容第 3.8 节 “mov指令的操作数的取值范围到底是多少”

【总结】

之所以用adr而不用mov,主要是为了生成地址无关代码,以及由于不方便判断一个数,是否是有效的mov的操作数。