1.4.4. adr

#ifndef CONFIG_SKIP_RELOCATE_UBOOT
relocate:				/* relocate U-Boot to RAM	    */
	adr1	r0, _start		/* r0 <- current position of code   */
        

1

adr指令的语法和含义:

http://blog.mcuol.com/User/cdkfGao/article/8057_1.htm

1、ADR伪指令--- 小范围的地址读取

ADR伪指令将基于PC相对偏移的地址值或基于寄存器相对偏移的地址值读取到寄存器中。

在汇编编译器编译源程序时,ADR伪指令被编译器替换成一条合适的指令。通常,编译器用一条ADD指令或SUB指令来实现该ADR伪指令的功能,若不能用一条指令实现,则产生错误,编译失败。

ADR伪指令格式 :ADR{cond} register, expr

地址表达式expr的取值范围:

当地址值是字节对齐时,其取指范围为: +255 ~ 255B;

当地址值是字对齐时,其取指范围为: -1020 ~ 1020B;

所以,上述:

adr r0, _start

的意思其实很简单,就是将_start的地址赋值给r0.但是具体实现的方式就有点复杂了,对于用adr指令实现的话,说明_start这个地址,相对当前PC的偏移,应该是很小的,意思就是向前一段后者向后一段去找,肯定能找到_start这个标号地址的,此处,自己通过看代码也可以看到_start,就是在当前指令的前面,距离很近,编译后,对应汇编代码,也可以猜得出,应该是上面所说的,用sub来实现,即当前PC减去某个值,得到_start的值,

参照前面介绍的内容,去:

arm-inux-objdump –d u-boot > dump_u-boot.txt

然后打开dump_u-boot.txt,可以找到对应的汇编代码,如下:

33d00000 <_start>:
33d00000:	ea000014 	b	33d00058 <reset>
。。。
33d000a4 <relocate>:
33d000a4:	e24f00ac 	sub	r0, pc, #172	; 0xac
                

可以看到,这个相对当前PC的距离是0xac=172,细心的读者可以看到,那条指令的地址减去0xac,却并不等于_start的值,即

33d000a4 - 33d00000 = 0xa4 != 0xac

而0xac – 0xa4 = 8,

那是因为,由于ARM920T的五级流水线的缘故导致指令执行那一时刻的PC的值等于该条指令PC的值加上8,即

sub r0, pc, #172中的PC的值是

sub r0, pc, #172

指令地址:33d000a4,再加上8,即33d000a4+8 = 33d000ac,

所以,33d000ac – 0xac,才等于我们看到的33d00000,才是_start的地址。

这个由于流水线导致的PC的值和当前指令地址不同的现象,就是我们常说的,ARM中,PC=PC+8。

对于为何是PC=PC+8,请参见后面的内容:第 3.4 节 “为何ARM7中PC=PC+8”

对于此处为何不直接用mov指令,却使用adr指令,请参见后面内容:第 3.7 节 “关于为何不直接用mov指令,而非要用adr伪指令”

对于mov指令的操作数的取值范围,请参见后面内容:第 3.8 节 “mov指令的操作数的取值范围到底是多少”

adr	r0, _start

的伪代码,被翻译成实际汇编代码为:

33d000a4:	e24f00ac 	sub	r0, pc, #172	; 0xac

其含义就是,通过计算PC+8-172 ⇒ _start的地址,

而_start的地址,即相对代码段的0地址,是这个地址在运行时刻的值,而当ARM920T加电启动后,,此处是从Nor Flash启动,对应的代码,也是在Nor Flash中,对应的物理地址是0x0,所以,此时_start的值就是0,而不是0x33d00000。

所以,此时:

r0 = 0x0