代码的重定位有很多种,之前认识到的重定位是把链接地址设在程序员认为程序应该运行在的地址上,比如对于s3c2440来说,SDRAM的起始位置是0x30000000,就可以把链接地址设为0x31000000,当编译器编译时,会根据程序员设定的0x31000000这个地址,计算各个全局变量的存储地址(在data段或rodata段)。

如果一个函数要读写一个全局变量,要怎么寻址呢?

  可以用arm-linux-objdump -D uboot > uboot.dis命令生成反汇编文件查看board_init_f的汇编实现,会发现函数末尾存储着一段内容,这些是所谓“LABEL”;

 



000020d0 <board_init_f>:
20d0: e59f8104 ldr r8, [pc, #260] ;
20d4: e3a01000 mov r1, #0 ; 0x0
20d8: e3a02078 mov r2, #120 ; 0x78
...
...
...
21a4: e2455084 sub r5, r5, #132 ; 0x84
21a8: e3c55007 bic r5, r5, #7 ; 0x7
...
...
21d0: e1a01007 mov r1, r7
21d4: e1a02004 mov r2, r4
21d8: ebfff7bb bl cc <relocate_code>
21dc: 30000f80 .word 0x30000f80
21e0: 00000048 .word 0x00000048
21e4: 0005a6c2 .word 0x0005a6c2
21e8: 00068e94 .word 0x00068e94
21ec: 0005a6d1 .word 0x0005a6d1
21f0: 0006104f .word 0x0006104f
21f4: 00000040 .word 0x00000040


  当board_init_f函数想调用某个全局变量时,会从“PC+某个偏移量”的地址上定位到LABEL中的某一个存储单元,里面存着的就是存储全局变量的地址。如果链接文件中设的链接地址是0x31000000,那么LABEL中存储的全局变量地址都应该大于这个链接地址,而对于smdk2410这款开发板的uboot来说,它的链接地址设为了0,可以看出LABEL里面存的地址值都比较小,是基于0地址得到的。那么当进行代码重定位以后,这里存储的变量地址也应该进行修改,因为如果不修改的话,如果函数要读写一个全局变量,它读写的地址仍然是重定位之前的地址。

所以重定位之后,需要把所有函数之后的LABEL区域里面的内容,增加一个重定位的偏移量。为此编译器提供了一个编译选项“-pie”,在arch/arm/config.mk文件中添加这个编译选项:

uboot移植之重定位_全局变量

  添加了这个编译选项之后,编译器会把所有函数的LABEL集中起来放在.rel.dyn段,需要注意的是,.rel.dyn段存储的内容是“存储着全局变量地址的LABEL的地址”,简单来说就是.rel.dyn段存储着LABEL的地址,而不是全局变量的地址,所以重定位代码并不直接修改.rel.dyn段里面的内容,而是通过对.rel.dyn段里存储的地址进行寻址,来找到各个函数LABEL所处的位置,对于LABEL里的内容进行修改。

注:上面代码中的     21dc: 30000f80 .word 0x30000f80    存储的不是全局变量的地址,而是之前代码中设置的栈指针指向的位置,因此不需要重定位,.rel.dyn段里面也找不到21dc这个地址

 

重定位的代码量不多,如下:



.globl    relocate_code
relocate_code:
mov r4, r0 /* save addr_sp */  
mov r5, r1 /* save addr of gd */ 
mov r6, r2 /* save addr of destination */  //r6->重定位目标地址

/* Set up the stack */
stack_setup:
mov sp, r4  //设置栈,与重定位无关

adr r0, _start  //根据 当前pc的值 和 _start与当前PC的偏移量 得到_start的绝对地址,存入r0.由于uboot的链接地址是0,这里的_start为0.r0->重定位源位置的首地址
cmp r0, r6    //比较 当前代码段的起始地址 和 重定位目标地址是否一样,如果一样就只清除bss段(这是重定位后每次上电都要进行的),不一样就进行重定位
beq clear_bss /* skip relocation */
mov r1, r6 /* r1 <- scratch for copy_loop */  //r1->重定位目标地址
ldr r3, _bss_start_ofs                      //r3->需要拷贝的代码段、数据段、rel.dyn段的总大小,bss段不需要拷贝,只要把对应的地址段清0即可
add r2, r0, r3 /* r2 <- source end address */  //由于r0==0,这里r2==r3,表示重定位源位置的结束地址(如果链接脚本里定义的链接地址不为0,r2就会加上一个r0的偏移量)。
//r2->重定位源位置的结束地址

copy_loop:
ldmia r0!, {r9-r10} /* copy from source address [r0] */  //从重定位源地址读两个32位的数据,保存在r9、r10,之后r0自增8字节
stmia r1!, {r9-r10} /* copy to target address [r1] */  //把r9、r10中的数据存到重定位目标地址,之后r1自增8字节
cmp r0, r2 /* until source end address [r2] */     //比较重定位源地址是否等于结束地址
blo copy_loop
/***************************到此各段拷贝完成,接下来需要修改全局变量的调用地址*****************************/
#ifndef CONFIG_SPL_BUILD
/*
* fix .rel.dyn relocations
*/
ldr r0, _TEXT_BASE /* r0 <- Text base */
sub r9, r6, r0 /* r9 <- relocation offset */
ldr r10, _dynsym_start_ofs /* r10 <- sym table ofs */  //目前不知道dynsym段时干啥的,先不关心
add r10, r10, r0 /* r10 <- sym table in FLASH */
ldr r2, _rel_dyn_start_ofs /* r2 <- rel dyn start ofs */
add r2, r2, r0 /* r2 <- rel dyn start in FLASH */  //得到重定位前的.rel.dyn段的起始地址r2
ldr r3, _rel_dyn_end_ofs /* r3 <- rel dyn end ofs */
add r3, r3, r0 /* r3 <- rel dyn end in FLASH */   //得到重定位前的.rel.dyn段的结束地址r3
fixloop:  //修改全局变量地址的循环
ldr r0, [r2] /* r0 <- location to fix up, IN FLASH! */  //得到r2地址的内容,也就是重定位前某个LABEL的地址
add r0, r0, r9 /* r0 <- location to fix up in RAM */ //加上一个重定位偏移量,得到重定位之后的某个LABEL地址,也是实际使用的LABEL地址
ldr r1, [r2, #4]
and r7, r1, #0xff                            //获取这个LABEL的重定位类型,如果是23(0x17),就是上文讲的那种重定位方式,跳转到fixrel
cmp r7, #23 /* relative fixup? */
beq fixrel
cmp r7, #2 /* absolute fixup? */            //如果重定位类型是0x02,跳转到fixabs,先不关心
beq fixabs
/* ignore unknown type of fixup */
b fixnext
fixabs:
/* absolute fix: set location to (offset) symbol value */
mov r1, r1, LSR #4 /* r1 <- symbol index in .dynsym */
add r1, r10, r1 /* r1 <- address of symbol in table */
ldr r1, [r1, #4] /* r1 <- symbol value */
add r1, r1, r9 /* r1 <- relocated sym addr */
b fixnext
fixrel:
/* relative fix: increase location by offset */
ldr r1, [r0]              //读取这个LABEL中的内容,加上一个重定位的偏移量
add r1, r1, r9
fixnext:
str r1, [r0]              //再存回LABEL里面
add r2, r2, #8 /* each rel.dyn entry is 8 bytes */  在.rel.dyn段里找到下一个LABEL地址,继续修改。
cmp r2, r3
blo fixloop
#endif