上篇博客主要分析了main函数的主要框架,还要relocate_code、board_init_f、board_init_r 三个主要函数没有分析。这篇博客主要分析relocate_code重定位函数。
重定位代码的作用:
我们知道当从NAND FLASH中启动代码的时候,SOC系统会自动复制4K的内容到片内内存中,然后从片内内存中开始执行。而我们的U-BOOT程序大小远远大于4K,那多余的代码怎么加载到内存运行,这个时候就需要对代码进行重定位。
重定位的功能是将我们的程序拷贝到内存的某块地址中,在片内4K的程序执行结束之前跳到重定向的位置处进行执行
在上篇博客中,我们分析了:
ldr x0, [x18, #GD_START_ADDR_SP] //x0存入gd->start_addr_sp的地址
bic sp, x0, #0xf //sp指向x0并且16字节对齐
//重新设置栈指针的地址为gd->start_addr_sp
ldr x18, [x18, #GD_NEW_GD] //x18存入gd->new_gd的地址
//将x18设置为新的gd地址gd->new_gd
adr lr, relocation_return //设置lr为relocation_return的地址
ldr x9, [x18, #GD_RELOC_OFF] //x9存入gd->reloc_off 的地址
add lr, lr, x9 //设置新的返回地址为lr+x9
//设置程序新的返回地址为重定向处的地址lr + gd->reloc_off
ldr x0, [x18, #GD_RELOCADDR] //将重定向的地址gd->relocaddr赋值给x0传入重定向函数
b relocate_code
//关于gd重定位变量的初始化主要在board_init_f中,后续分析board_init_f函数中在分析
relocate_code代码分析:
ENTRY(relocate_code)
stp x29, x30, [sp, #-32]! //保存x29和x30寄存器的值
mov x29, sp //保存栈的地址到x29中
str x0, [sp, #16] //将重定向地址x0存入到sp+16处
adr x1, __image_copy_start //__image_copy_start在连接文件中定义,程序的起始地址
subs x9, x0, x1
b.eq relocate_done
//如果重定向地址等于程序的起始地址,则跳过重定向过程
ldr x1, _TEXT_BASE
subs x9, x0, x1
//计算出重定向地址与程序代码段地址的偏移量offset并存入x9寄存器中
adr x1, __image_copy_start
adr x2, __image_copy_end
copy_loop:
ldp x10, x11, [x1], #16 /* copy from source address [x1] */
stp x10, x11, [x0], #16 /* copy to target address [x0] */
cmp x1, x2 /* until source end address [x2] */
b.lo copy_loop
str x0, [sp, #24]
//将__image_copy_start 到__image_copy_end 中的代码拷贝到重定向地址处,并保存新的代码段地址到sp+24中
adr x2, __rel_dyn_start /* x2 <- Run &__rel_dyn_start */
adr x3, __rel_dyn_end /* x3 <- Run &__rel_dyn_end */
fixloop:
ldp x0, x1, [x2], #16 /* (x0,x1) <- (SRC location, fixup) */
ldr x4, [x2], #8 /* x4 <- addend */
and x1, x1, #0xffffffff
cmp x1, #R_AARCH64_RELATIVE
bne fixnext
fixnext:
cmp x2, x3
b.lo fixloop
/*由于重定向之后,程序的运行地址与链接地址不一致,需要使用需要使用“位置无关代码”技术。对于一些绝对地址符号,使用位置无关码技术会将其以label的形式放在每个函数的代码实现的末端,统一保存在rel_dyn段中。该处程序的功能即将这些绝对地址转化为重定位之后的新的地址。
从反汇编代码中截取一段rel_dyn段的信息如下:
80074698: 80021758 .word 0x80021758
8007469c: 00000000 .word 0x00000000
800746a0: 00000403 .word 0x00000403
800746a4: 00000000 .word 0x00000000
800746a8: 8007eb98 .word 0x8007eb98
800746ac: 00000000 .word 0x00000000
800746b0: 80021760 .word 0x80021760
800746b4: 00000000 .word 0x00000000
800746b8: 00000403 .word 0x00000403
800746bc: 00000000 .word 0x00000000
800746c0: 8009ebf8 .word 0x8009ebf8
800746c4: 00000000 .word 0x00000000
每个标签有24个字节,其中中间8个字节是标签地址标识00000403即为R_AARCH64_RELATIVE(1027),该程序的含义为,取出标签中的24个字节分别放入x0/x1/x4中,当x2等于R_AARCH64_RELATIVE时说明为标签地址,故需要重定向x0、x4的地址,即加上偏移值x9,然后将新的全局变量地址x4写入到重定位后的地址x0处。
relocate_done:
switch_el x1, 3f, 2f, 1f
bl hang
3: mrs x0, sctlr_el3
b 0f
2: mrs x0, sctlr_el2
b 0f
1: mrs x0, sctlr_el1
//根据不同中断等级读取相应的sctlr_el寄存器值存入到x0中
0: tbz w0, #2, 5f //如果w0的第2位为0则跳到5,即跳过刷新缓存
tbz w0, #12, 4f //如果w0的第12位为0则跳到4,即跳过对i-cache的影响
ic iallu //设置对整个i-cache有效
isb sy //指令同步
4: ldp x0, x1, [sp, #16] //取出sp+16处的值分别赋给x0、x1,从上面分析可知,x0为重定向地址,x1为重定向代码末端地址
bl __asm_flush_dcache_range //刷新数据缓存区
5: ldp x29, x30, [sp],#32 //取出保存的 x29, x30寄存器的值
ret
ENDPROC(relocate_code)
关于代码的重定向就分析到这里,由于水平有限,欢迎大家批评指正