如果想了解重定位是怎么一回事,可以参考这位老哥的博客:https://blog.csdn.net/skyflying2012/article/details/37660265
第一阶段结束,便开始u-boot的重定位(这里有点小疑惑,在前面将u-boot从SD卡拷贝到DDR中时,我就是拷贝到链接地址的,也就是说那个时候u-boot的运行地址就和链接地址一致了,可以正常运行完整个u-boot,这里又再次重定位,只不过是将u-boot搬到内存的高地址去运行,网上搜了一下,有说是因为防止内核解压的时候覆盖了u-boot本身,那也就是说只要解压内核不会覆盖u-boot这部分内存,理论上就不需要再次重定位了,等之后有时间我测试一下,去掉这部分重定位代码,看u-boot是否还能正常运行,以验证自己的想法),关于这部分新版u-boot和以前的不太一样,个人感觉新版的设计更加简单便于理解。
现在回到arch/arm/lib/crt0.S文件:
重定位前的准备工作
/*arch/arm/lib/crt0.S*/
/*GD_START_ADDR_SP定义在include/generated/generic-asm-offsets.h文件(这个文件是u-boot自动生成的,而且过程也是有点高端,
感兴趣的可以网上了解一下,贴一个我看过的链接:http://blog.chinaunix.net/uid-25000873-id-4134037.html),
在这里表示在gd这个数据结构中start_addr_sp的偏移值,前面我们也知道r9这个寄存器绑定的就是gd全局变量这个指针,
所以相当于将gd->start_addr_sp里面的值加载到r0中*/
ldr r0, [r9, #GD_START_ADDR_SP] /* sp = gd->start_addr_sp */
bic r0, r0, #7 /* 8-byte alignment for ABI compliance */
/*设置栈指针*/
mov sp, r0
/*获取gd->bd的位置*/
ldr r9, [r9, #GD_BD] /* r9 = gd->bd */
/*在内存图上可以看出,新的gd结构在bd结构的下面紧挨着,所以减去gd的大小就是新的gd起始地址*/
sub r9, r9, #GD_SIZE /* new GD is below bd */
/*这里将here标号的地址值读取到lr中,注意这里是adr,关于adr与ldr的区别可自行网上搜索*/
adr lr, here
/*将重定位偏移值加载到r0寄存器中*/
ldr r0, [r9, #GD_RELOC_OFF] /* r0 = gd->reloc_off */
/*链接寄存器加上偏移值后,lr的地址就变成重定位后的地址了*/
add lr, lr, r0
/*未定义*/
#if defined(CONFIG_CPU_V7M)
orr lr, #1 /* As required by Thumb-only */
#endif
/*将重定位地址加载到r0中,作为参数传给relocate_code*/
ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */
/*执行重定位*/
b relocate_code
/*从relocate_code回来后,就直接运行在重定位后的u-boot中了,here标号已经是重定位后那个here标号了*/
here:
/*
省略
*/
上面的步骤结束后,sp栈指针就设置成gd->start_addr_sp指向的地方了,而r9之前绑定的那个gd也变成重定位后的新地址的gd结构了,内存图如下:
执行重定位
/*arch/arm/lib/relocate.S*/
ENTRY(relocate_code)
/*从链接脚本可知,主要是将代码段和数据段拷贝到新的地址*/
ldr r1, =__image_copy_start /* r1 <- SRC &__image_copy_start */
/*r0是重定位地址,r1是目前u-boot运行地址,这里也有个疑问,要是u-boot运行地址现在和链接地址不一样,
它要拷贝的地方可能根本就是空的或者是错的,那这里不就有问题吗?这个我之后得验证一下*/
subs r4, r0, r1 /* r4 <- relocation offset */
/*上一句subs指令会影响CPSR的状态,这里如果r4的值为0,表示r0和r1是一样的,不需要再重定位*/
beq relocate_done /* skip relocation */
/*将拷贝结束地址放到r2中*/
ldr r2, =__image_copy_end /* r2 <- SRC &__image_copy_end */
copy_loop:
/*以r1为起始地址(也就是目前u-boot的起始地址),加载两个字到r10,r11中*/
ldmia r1!, {r10-r11} /* copy from source address [r1] */
/*以r0为起始地址(也就是重定位的新地址),加载r10,r11的值到r0,r0+4中*/
stmia r0!, {r10-r11} /* copy to target address [r0] */
/*比较是否读取到结束地址*/
cmp r1, r2 /* until source end address [r2] */
/*一直循环,直到拷贝结束*/
blo copy_loop
/*
* fix .rel.dyn relocations
*/
/*下面这些才是重定位最重要的,上面虽然将u-boot拷贝到新地址了,但是我们要使用的变量,
和一些数据这些的地址(这里说地址可能有些不太准确)还是没变的,重定位后的u-boot应该访问的是新地址的变量和数据等,
下面这段代码就是干这个事的,我们给新地址的u-boot每一个要访问的变量地址等加上一个偏移,这样就能正确访问了*/
ldr r2, =__rel_dyn_start /* r2 <- SRC &__rel_dyn_start */
ldr r3, =__rel_dyn_end /* r3 <- SRC &__rel_dyn_end */
fixloop:
/*以r2为起始地址(也就是动态符号表的起始地址),加载两个字到r0,r1中*/
ldmia r2!, {r0-r1} /* (r0,r1) <- (SRC location,fixup) */
/*取出r1中数据的低8位*/
and r1, r1, #0xff
/*R_ARM_RELATIVE用来检查这个符号是不是需要被重定位*/
cmp r1, #R_ARM_RELATIVE
/*不需要的话就跳过*/
bne fixnext
/* relative fix: increase location by offset */
/*r4是重定位偏移,这里就是要加上这个偏移*/
add r0, r0, r4
/*取出加上偏移后的以r0为地址内的数据存到r1中,这里取出的也就是新的u-boot里面存放变量的那个地址*/
ldr r1, [r0]
/*给新的u-boot存放变量的那个地址加上偏移*/
add r1, r1, r4
/*将加了偏移后的值(变量的地址)写回,这样新的u-boot就能正确访问了*/
str r1, [r0]
fixnext:
cmp r2, r3
blo fixloop
relocate_done:
/*未定义*/
#ifdef __XSCALE__
/*
* On xscale, icache must be invalidated and write buffers drained,
* even with cache disabled - 4.2.7 of xscale core developer's manual
*/
mcr p15, 0, r0, c7, c7, 0 /* invalidate icache */
mcr p15, 0, r0, c7, c10, 4 /* drain write buffer */
#endif
/* ARMv4- don't know bx lr but the assembler fails to see that */
#ifdef __ARM_ARCH_4__
mov pc, lr
#else
/*重定位结束*/
bx lr
#endif
ENDPROC(relocate_code)
接下来重定位向量表,这个很简单,就是操作协处理器
/*arch/arm/lib/relocate.S*/
.section .text.relocate_vectors,"ax",%progbits
.weak relocate_vectors
ENTRY(relocate_vectors)
/*未定义*/
#ifdef CONFIG_CPU_V7M
/*
省略
*/
#else
/*定义了,前面也讲过,这个就是向量表基地址寄存器*/
#ifdef CONFIG_HAS_VBAR
/*
* If the ARM processor has the security extensions,
* use VBAR to relocate the exception vectors.
*/
/*这个还是操作协处理器,和start.S里面设置重设向量表是一样的*/
ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */
mcr p15, 0, r0, c12, c0, 0 /* Set VBAR */
#else
/*
省略
*/
#endif
#endif
bx lr
ENDPROC(relocate_vectors)
/*arch/arm/cpu/armv7/start.S*/
ENTRY(c_runtime_cpu_setup)
/*
* If I-cache is enabled invalidate it
*/
/*因为u-boot又已经重定位了,现在流水线和缓存里面的内容都是旧的,需要清掉重新填充*/
#if !CONFIG_IS_ENABLED(SYS_ICACHE_OFF)
mcr p15, 0, r0, c7, c5, 0 @ invalidate icache
mcr p15, 0, r0, c7, c10, 4 @ DSB
mcr p15, 0, r0, c7, c5, 4 @ ISB
#endif
bx lr
ENDPROC(c_runtime_cpu_setup)
然后是清bss段,这是C语言运行环境需要的,然后执行board_init_r
/*arch/arm/lib/crt0.S*/
/* Set up final (full) environment */
bl c_runtime_cpu_setup /* we still call old routine here */
#endif
/*CONFIG_SPL_BUILD未定义,要执行*/
#if !defined(CONFIG_SPL_BUILD) || CONFIG_IS_ENABLED(FRAMEWORK)
/*CONFIG_SPL_EARLY_BSS未定义,要执行*/
#if !defined(CONFIG_SPL_EARLY_BSS)
/*清BSS段,这是一个宏定义,执行了一个memset,就不追进去了*/
SPL_CLEAR_BSS
#endif
/*未定义*/
# ifdef CONFIG_SPL_BUILD
/* Use a DRAM stack for the rest of SPL, if requested */
bl spl_relocate_stack_gd
cmp r0, #0
movne sp, r0
movne r9, r0
# endif
/*未定义,要执行,就是点亮一颗led灯,执不执行都无所谓*/
#if ! defined(CONFIG_SPL_BUILD)
bl coloured_LED_init
bl red_led_on
#endif
/*将gd_t这个数据结构放到r0中,将重定位地址放到r1中,作为参数传给board_init_r*/
/* call board_init_r(gd_t *id, ulong dest_addr) */
mov r0, r9 /* gd_t */
ldr r1, [r9, #GD_RELOCADDR] /* dest_addr */
/* call board_init_r */
#if CONFIG_IS_ENABLED(SYS_THUMB_BUILD)
ldr lr, =board_init_r /* this is auto-relocated! */
bx lr
#else
/*调用board_init_r,进入u-boot第二阶段*/
ldr pc, =board_init_r /* this is auto-relocated! */
#endif
/* we should not return here. */
#endif
到了这里,u-boot重定位就结束了,其实u-boot重定位的知识远不止代码中的这点儿,有很多的细节,建议读者有时间的话,多去网上查阅一下相关的资料,比如位置无关代码与位置有关代码,为什么需要重定位等问题,这样对重定位才有更加深入和深刻的理解。