uboot的重定向汇编详细分析--Apple的学习笔记
  2Nv1H5BMjysw 2023年11月05日 41 0

一,前言

既然是第二轮学习,当然要比第一轮增加深度,获取更多技能和通用方法论。之前我想通过代码关闭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,见下图

uboot的重定向汇编详细分析--Apple的学习笔记_汇编源码分析

四,疑问项

之前我猜测rel_dyn_start_ofs中仅需要前4个字节改变地址,后面23不需要了,这是为什么呢,之后再研究。估计就是要symbol表得到地址,所以23可以不需要,但是地址是需要的。

1102更新:我对疑问项进行了验证,原来是我r0的值理解错误,r0是rel.dyn地址范围中的值,不是rel.dyn地址,所以r0的值是image范围地址,重新修改的label地址就在image中,所以根本不存在23,具体参考uboot的重定向原理分析--Apple的学习笔记

五,小结

深度思考就是多问几个why。为什么要这样设计,为什么其他人会知道答案,我不知道,原因就是我一定缺少或者误解了某些信息。看来我要再看一遍<程序员的自我修养>,我记得里面对代码编译链接都写的很详细,以前细节估计看漏了,或者理解不清晰导致我会有这样的疑问项。

【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
2Nv1H5BMjysw