为了和源码当中对重定位的描述保持一致,下面所有的重定位都叫relocate(虽然重定位翻译过来就是relocate,但很多文章一会用英文,一会用中文,在描述上又存在一定的差异,导致概念认知混乱)。随着uboot的发展,基于一定原因的考虑,uboot的地址重定位已经有很多个版本,本文仅对2010-12之前的拷贝方式(基于arm920t核)进行阐述,在2010-12之前包括命名更改之前的所有版本都使用同一种地址重定位方式,这是由程序的布局决定的。
拷贝部分的源码:
点击(此处)折叠或打开
- relocate: /* relocate U-Boot to RAM */
- adr r0, _start /* r0 <- current position of code */
- ldr r1, _TEXT_BASE /* test if we run from flash or RAM */
- cmp r0, r1 /* don't reloc during debug */
- beq stack_setup
- ldr r2, _armboot_start
- ldr r3, _bss_start
- sub r2, r3, r2 /* r2
- add r2, r0, r2 /* r2
-
- copy_loop:
- ldmia r0!, {r3-r10} /* copy from source address [r0] */
- stmia r1!, {r3-r10} /* copy to target address [r1] */
- cmp r0, r2 /* until source end addreee [r2] */
- ble copy_loop
这就是u-boot拷贝的代码,其思路是找到当前程序运行的首地址在哪里,程序的总大小是多少,目标地址在哪里,知道了这些信息,则只需要一边读,一边写直到写完即可。
1.当前程序运行的首地址在哪里?
adr r0, _start /* r0 <- current position of code */
这是一条adr伪指令,目的便是为了获取_start标号在初始运行时的地址是多少。这条指令在编译的时候就会被真实的指令所替代,如:
sub r0, pc, #164 /* _start标号的地址在pc前面164byte处 */
在编译时,编译器便确定pc指向这条语句时和_start标号的地址差多少,然后用实际的指令进行替代。这样在relocate之前,程序到底位于什么地方,即首地址便知道了。
2.程序的大小有多大?
ldr r2, _armboot_start
ldr r3, _bss_start
sub r2, r3, r2 /* r2
_armboot_start是一个地址,该地址存放的是_start地址,和当前程序运行的首地址不同,_start标号是链接时指定的首地址,即链接时指定的程序首地址,如果链接时指定首地址为0x3f000000,那么_start标号便是0x3f000000。_bss_start也是一个地址,存放的是链接时指定的bss段的首地址,也就说armboot_start和bss_start之间存放着程序所需要的正文段、数据段等,通过这两个地址相减,便得到了中间部分的大小。
3.拷贝的目标地址在哪里?
ldr r1, _TEXT_BASE
毫无疑问,relocate的目标地址便是链接时指定的首地址,uboot的编译系统会将TEXT_BASE的值最终反应到链接命令当中去,让其成为程序链接的首地址,所以目标地址就是TEXT_BASE。
有了这三个要素,拷贝便可完成。