一,前言
既然是第二轮学习,当然要比第一轮增加深度,获取更多技能和通用方法论。之前我想通过代码关闭relocate功能,结果一尝试就复位了,看来没我想的简单,还是先了解下relocate的代码。
二,源码分析
调用前r0有传参为gd->relocaddr,也就是一个指针地址保存在r0。
arch/arm/lib/crt0.S
ldr r0, [r9, #GD_RELOCADDR] /* r0 = gd->relocaddr */
b relocate_code
relocate_code的整个过程分析,关于我的理解都添加了注释。
arch/arm/lib/relocate.S
ENTRY(relocate_code)
relocate_base:
adr r3, relocate_base /* 读取运行地址到r3 */
ldr r1, _image_copy_start_ofs /* _image_copy_start和relocate_base的地址差保存到r1 */
add r1, r3 /* 运行时的_image_copy_start地址保存到r1 */
/* 这里说下,要获取运行时候地址正常来说是可以直接读,但是_image_copy_start是编译时候的一个变量值,这个变量值代表了一个地址,所以才要用这样的方法 */
subs r4, r0, r1 /* gd->relocaddr的指针地址,再减去运行时候_image_copy_start_ofs的值,获取差值,是relocate要修改的offset值 */
beq relocate_done /* 若差值为0就不做重定向了 */
ldr r1, _image_copy_start_ofs
add r1, r3 /* r1 <- Run &__image_copy_start */
ldr r2, _image_copy_end_ofs
add r2, r3 /* r2 <- Run &__image_copy_end */
/* 如上4行就是获取运行时候的_image_copy_start和_image_copy_end地址保存到r1和r2中
copy_loop:
ldmia r1!, {r10-r11} /* copy from source address [r1] */
stmia r0!, {r10-r11} /* copy to target address [r0] */
cmp r1, r2 /* until source end address [r2] */
blo copy_loop
/* 如上4行时copy命令,就是r1地址中的值先缓存到r10-r11,然后r1地址加2,然后将缓存中的内容保存到r0地址(gd->relocaddr)中,由于r1是源,然后源地址加加后直到与r2地址值一样,就停止copy了。而r1是运行时的image_copy_start,r2是image_copy_end,这样就完成了uboot的重定向copy */
/* 根据u-boot.lds__image_copy_end后紧接着就是__rel_dyn_start,而这段是重定向的关键,但是内容不做迁移,仅对内容值做修改 */
/*
* fix .rel.dyn relocations
*/
ldr r1, _rel_dyn_start_ofs
add r2, r1, r3 /* r2 <- Run &__rel_dyn_start */
ldr r1, _rel_dyn_end_ofs
add r3, r1, r3 /* r3 <- Run &__rel_dyn_end */
/* 如上4行一样的是是获取运行时的开始和结束地址 */
fixloop:
ldmia r2!, {r0-r1} /* (r0,r1) <- (SRC location,fixup) */
and r1, r1, #0xff
cmp r1, #R_ARM_RELATIVE
bne fixnext
/* 如上4行意思是从首地址拿出2个值到缓存r0和r1,而2个值的含义是一个代表函数或变量位置,第二个代表是否支持重定向,所以第二个值比较关键,也就是r1的值最后uint8是R_ARM_RELATIVE(23)代表是重定向类型则会继续,否则调用fixnext,继续遍历到dyn_end */
/* relative fix: increase location by offset */
add r0, r0, r4 /* r0增加offset值后保存到r0 */
/*一开始不明白的是这里r0也就是r2开始的_rel_dyn_start中的内容,为什么要先加offset,后来知道由于.rel.dyn段不copy,其实用了直接设置值的方法,也就是在image copy完后,继续在image内容后面,写入新的值,之前image直接写入gd->relocaddr地址的,而这个地址和编译生成的地址的offset保存在r4,所以r0需要加r4,接下来就是写值了,看起来是提取2个值,但是写入的新值仅1个,猜测已经够了,应该是23不需要再写入了。*/
ldr r1, [r0] /* 把r0地址中的值保存到r1 */
add r1, r1, r4 /* r1增加offset值后保存到r1 */
str r1, [r0] /* 把r1的值保存到r0地址的内容中 */
/* 如上3行的时候,r1其实之前mask过0xff已经不需要保存了,可以用来做其它事情,所以3行主要是处理r0地址中的内容,为其加offset值后在存入r0 */
fixnext: /* 全部遍历完r2(start)值已经增长到了r3(end),那么就停止循环了。*/
cmp r2, r3
blo fixloop
relocate_done:
这里add r0, r0, r4
我有一个猜测,就是23类型不用再写入了。但是地址信息需要写入,这是什么原理,我要继续找原因。
三,源码仿真调试
之前qemu可以仿真vexpress,所以我单步调试了下。对我理解的代码进行了闭环验证,至少我理解是正确的。关于寄存器值的变换我都添加在每行后面了。
ENTRY(relocate_code)
relocate_base:
adr r3, relocate_base // r3 = 0x60801608 , r0 = 0x7ff55000
ldr r1, _image_copy_start_ofs // r1 = 0xffffe9f8
add r1, r3 /* r1 <- Run &__image_copy_start */ // r1 =0x60800000
subs r4, r0, r1 /* r4 <- Run to copy offset */ // r4 = 0x1f755000
beq relocate_done /* skip relocation */
ldr r1, _image_copy_start_ofs // r1 = 0xffffe9f8
add r1, r3 /* r1 <- Run &__image_copy_start */ // r1 =0x60800000
ldr r2, _image_copy_end_ofs // r2=0x903ec
add r2, r3 /* r2 <- Run &__image_copy_end */ // r2=0x608919f4
copy_loop:
ldmia r1!, {r10-r11} /* copy from source address [r1] */
stmia r0!, {r10-r11} /* copy to target address [r0] */
cmp r1, r2 /* until source end address [r2] */
blo copy_loop
/*
* fix .rel.dyn relocations
*/ // in mapfile .rel_dyn_start 0x608919f4
ldr r1, _rel_dyn_start_ofs //r1 = 0x903ec
add r2, r1, r3 /* r2 <- Run &__rel_dyn_start */ //r2 = 0x608919f4
ldr r1, _rel_dyn_end_ofs //r1 = 0x9d69c
add r3, r1, r3 /* r3 <- Run &__rel_dyn_end */ //r3 = 0x6089eca4
fixloop:
ldmia r2!, {r0-r1} /* (r0,r1) <- (SRC location,fixup) */ // r0=0x60800020
and r1, r1, #0xff
cmp r1, #R_ARM_RELATIVE
bne fixnext
/* relative fix: increase location by offset */
add r0, r0, r4 // r0=0x7ff55020
ldr r1, [r0] // r1=0x60800060
add r1, r1, r4 // r1=0x7ff55060
str r1, [r0] // r0地址内容从原来的0x60800060变成了7FF55060
fixnext:
cmp r2, r3
blo fixloop
relocate_done:
rel_dyn_start_ofs地址中的值确实能看到23,见下图
四,疑问项
之前我猜测rel_dyn_start_ofs中仅需要前4个字节改变地址,后面23不需要了,这是为什么呢,之后再研究。估计就是要symbol表得到地址,所以23可以不需要,但是地址是需要的。
五,小结
深度思考就是多问几个why。为什么要这样设计,为什么其他人会知道答案,我不知道,原因就是我一定缺少或者误解了某些信息。看来我要再看一遍<程序员的自我修养>,我记得里面对代码编译链接都写的很详细,以前细节估计看漏了,或者理解不清晰导致我会有这样的疑问项。
标签:r0,Apple,--,start,地址,relocate,uboot,copy,r1 From: https://blog.51cto.com/AppleCai/8134281