一. uboot启动流程
_main 函数中会调用 relocate_code 函数。
relocate_code 函数分两个部分:
1. 拷贝 uboot 代码部分
2. 有关 " 重定位后有关函数调用或全局变量地址的问题"的解决方法。
本文继上一篇文章的学习,地址如下:
uboot代码重定位:解决调用与地址无关问题的测试-CSDN博客
这里学习 uboot 重定位的第二部分内容:重定位后有关函数调用或全局变量地址的问题。
二. uboot 重定位
可以看出, uboot 对于重定位后链接地址和运行地址不一致的解决方法就是采用位置无关码,在使用 ld 进行链接的时候使用选项“ -pie ”生成位置无关的可执行文件。在文件 arch/arm/config.mk 下有如下代码:
82 # needed for relocation
83 LDFLAGS_u-boot += -pie
第 83 行就是设置 uboot 链接选项,加入了“ -pie ” 选项,通过 "make V=1"命令(此命令可显示编译详情)编译uboot时,编译链接 uboot 的时候就会使用到 “ -pie ”,如下所示:
arm-linux-gnueabihf-ld.bfd -pie --gc-sections -Bstatic -Ttext 0x87800000 -o u-boot -T u-boot.lds arch/arm/cpu/armv7/start.o --start-group arch/arm/cpu/built-in.o arch/arm/cpu/armv7/built-in.o arch/arm/imx-common/built-in.o arch/arm/lib/built-in.o board/freescale/common/built-in.o board/freescale/mx6ull_alientek_nand/built-in.o cmd/built-in.o common/built-in.o disk/built-in.o drivers/built-in.o drivers/dma/built-in.o drivers/gpio/built-in.o drivers/i2c/built-in.o drivers/mmc/built-in.o drivers/mtd/built-in.o drivers/mtd/nand/built-in.o drivers/mtd/onenand/built-in.o
使用 “ -pie ” 选项以后会生成一个 .rel.dyn 段, uboot 就是靠这个 .rel.dyn 来解决重定位问题的,在 u-boot.dis 的 .rel.dyn 段中有如下所示内容:
1 Disassembly of section .rel.dyn:
2
3 8786b174 <__rel_dyn_end-0x95a0>:
4 8786b174: 87800020 strhi r0, [r0, r0, lsr #32]
5 8786b178: 00000017 andeq r0, r0, r7, lsl r0
6 ......
7 8786b6c4: 8780409c ; instruction: 0x8780409c
8 8786b6c8: 00000017 andeq r0, r0, r7, lsl r0
先来看一下 .rel.dyn 段的格式,类似第 7 行和第 8 行这样的是一组,也就是两个 4 字节数据 为一组。高 4 字节是 Label 地址标识 0X17 ,低 4 字节就是 Label 的地址,首先判断 Label 地址 标识是否正确,也就是判断高 4 字节是否为 0X17 ,如果是的话,低 4 字节就是 Label 地址值。
第 7 行值为8780409C ,第 8 行为 0X00000017 ,说明第 7 行的 8780409C 是个 Label ,
这个正是 存放变量 rel_a 地址的那个 Label 。
根据前面的分析,只要将地址 0X87804198+offset 处的值改为重定位后的变量 rel_a 地址即可。
我们猜测的是否正确,看一下 uboot 对 .rel.dyn 段的重定位即可 ,uboot 对 .rel.dyn 段的重定位关于函数调用地址处理:
94 ldr r2, =__rel_dyn_start /* r2 <- SRC &__rel_dyn_start */
95 ldr r3, =__rel_dyn_end /* r3 <- SRC &__rel_dyn_end */
96 fixloop:
97 ldmia r2!, {r0-r1} /* (r0,r1) <- (SRC location,fixup) */
98 and r1, r1, #0xff
99 cmp r1, #23 /* relative fixup? */
100 bne fixnext
101
102 /* relative fix: increase location by offset */
103 add r0, r0, r4
104 ldr r1, [r0]
105 add r1, r1, r4
106 str r1, [r0]
107 fixnext:
108 cmp r2, r3
109 blo fixloop
第 94 行, r2=__rel_dyn_start ,也就是 .rel.dyn 段的起始地址。
第 95 行, r3=__rel_dyn_end ,也就是 .rel.dyn 段的终止地址。
第 97 行,从 .rel.dyn 段起始地址开始,每次读取两个 4 字节的数据存放到 r0 和 r1 寄存器中,
r0 存放低 4 字节的数据,也就是 Label 地址; r1 存放高 4 字节的数据,也就是 Label 标志。
第 98 行, r1 中给的值与 0xff 进行与运算,其实就是取 r1 的低 8 位。
第 99 行,判断 r1 中的值是否等于 23(0X17) 。
第 100 行,如果 r1 不等于 23 的话就说明不是描述 Label 的,执行函数 fixnext ,否则的话继续执行下面的代码。
第 103 行, r0 保存着 Label 值, r4 保存着重定位后的地址偏移, r0+r4 就得到了重定位后的 Label 值。此时 r0 保存着重定位后的 Label 值,相当于 0X8780409C+0X18747000=0X9FF4B09C。
第 104 ,读取重定位后 Label 所保存的变量地址,此时这个变量地址还是重定位前的 ( 相当 于 rel_a 重定位前的地址 0X8785DA50) ,将得到的值放到 r1 寄存器中。
第 105 行 , r1+r4 即 可 得 到 重 定 位 后 的 变 量 地 址 , 相 当 于 rel_a 重 定 位 后 的
0X8786B180 +0X18747000=0X9FFB2180。
第 106 行,重定位后的变量地址写入到重定位后的 Label 中,相等于设置地址 0X8780409C 处的值为 0X8786B180 。
第 108 行,比较 r2 和 r3 ,查看 .rel.dyn 段重定位是否完成。
第 109 行,如果 r2 和 r3 不相等,说明 .rel.dyn 重定位还未完成,因此跳到 fixloop 继续重定 位 .rel.dyn 段。
可以看出, uboot 中对 .rel.dyn 段的重定位方法和我们猜想的一致。 .rel.dyn 段的重定位比较复杂一点,有点绕,因为涉及到链接地址和运行地址的问题。