从零开始的UBOOT的学习4--回顾重定位的过程
参考朱有鹏ARM裸机课程
1、什么是重定位?
(1)很多时候我们的启动介质没有那么大的空间进行启动程序,所以为了节约成本,我们会在启动介质使用SRAM或者NORFLASH,然后使用一个长跳转指令跳入到DDR内存当中运行,此时就有足够的地方运行大程序了。
2、链接地址和运行地址
(1)对于位置有关代码来说:最终执行时的运行地址和编译链接时给定的链接地址必须相同,否则一定出错。
(2)我们之前的裸机程序中,Makefile中用 -Ttext 0x0 来指定链接地址是0x0。这意味着我们认为这个程序将来会放在0x0这个内存地址去运行。
(3)但是实际上我们运行时的地址是0xd0020010(我们用dnw下载时指定的下载地址)。这两个地址看似不同,但是实际相同。这是因为S5PV210内部做了映射,把SRAM映射到了0x0地址去。
概念分析:
链接地址:链接时指定的地址,最好的方法就是makefile中用-Ttext或者使用链接脚本-Tlink.lds在里面进行指定。
运行地址:程序实际上运行的地址,实际运行的地址是下载到内存中的哪个位置说了算。
3、三星和UBOOT使用的启动过程
(1)三星推荐的启动方式:BootLoader的大小必须小于96KB而且大于16KB,假定BootLoader为80KB。
启动过程简单的分析:
先开机上电后BL0运行,BL0会加载外部启动设备中的前16KB(BL1)到SRAM里面运行,此时BL1的代码会加载BL2(80KB的)到SRAM中去运行。
此时BL2运行的时候,初始化DDR并且将OS搬运到DDR去执行OS,启动完成。
(2)UBOOT的实际使用过程:UBOOT的大小没有限制
启动过程:
先开机上电后BL0运行,BL0会加载外部启动设备中的UBOOT的前16KB的BL1到SRAM中去运行,BL1运行时就会初始化DDR,然后将整个UBOOT搬运到DDR中,然后用一句长跳转(从SRAM跳转DDR)。最后指令从SRAM直接跳转到DDR中继续执行UBOOT直到UBOOT完全启动。
UBOOT启动后在UBOOT命令行中启动OS。
为什么UBOOT不使用三星的方法,因为UBOOT的代码的大小比80KB大。
而且使用三星的方法,更加的麻烦,多了一个启动步奏。
4、链接脚本的实例分析
(1)链接脚本是用来干嘛的?
用来指导程序编译的时候,放置在哪个地址上面的。
.点号代表的是当前的地址
.=定义当前的地址为0xd0024000
(1)定义代码段的地址,因为我们需要在链接的时候,把start.s的内容链接到最前面,故在前面定义start.o
(2)定义数据段的地址
(3)定义BSS段的地址
SECTIONS
{
. = 0xd0024000;
.text : {
start.o
* (.text)
}
.data : {
* (.data)
}
bss_start = .;
.bss : {
* (.bss)
}
bss_end = .;
}
5、分析重定位代码
(1)判断运行地址和链接地址是否相等
(2)如果运行地址和链接地址是相等的话,
那么就没有必要进行重定位。直接跳过重定位的部分,直接跳转到clean_bss
(3)如果运行地址和链接地址不相等的话,那么就需要进行重定位了。
重定位的实际上就是拷贝数据段和代码段的内容从运行地址到链接地址处
那个while循环是用来拷贝数据的
(4)ldr pc, =led_blink 此命令就是从运行地址跳转到链接地址中去执行的。
由上面的4步可以看出重定位的过程已经结束。
//-----------------1---------------------//
// adr指令用于加载_start当前运行地址
adr r0, _start // adr加载时就叫短加载
// ldr指令用于加载_start的链接地址:0xd0024000
ldr r1, =_start // ldr加载时如果目标寄存器是pc就叫长跳转,如果目标寄存器是r1等就叫长加载
// bss段的起始地址
ldr r2, =bss_start // 就是我们重定位代码的结束地址,重定位只需重定位代码段和数据段即可
//-----------------2---------------------//
cmp r0, r1 // 比较_start的运行时地址和链接地址是否相等
beq clean_bss // 如果相等说明不需要重定位,所以跳过copy_loop,直接到clean_bss
// 如果不相等说明需要重定位,那么直接执行下面的copy_loop进行重定位
// 重定位完成后继续执行clean_bss。
// 用汇编来实现的一个while循环
copy_loop:
ldr r3, [r0], #4 // 源
str r3, [r1], #4 // 目的 这两句代码就完成了4个字节内容的拷贝
cmp r1, r2 // r1和r2都是用ldr加载的,都是链接地址,所以r1不断+4总能等于r2
bne copy_loop
// 清bss段,其实就是在链接地址处把bss段全部清零
clean_bss:
ldr r0, =bss_start
ldr r1, =bss_end
cmp r0, r1 // 如果r0等于r1,说明bss段为空,直接下去
beq run_on_dram // 清除bss完之后的地址
mov r2, #0
clear_loop:
str r2, [r0], #4 // 先将r2中的值放入r0所指向的内存地址(r0中的值作为内存地址),
cmp r0, r1 // 然后r0 = r0 + 4
bne clear_loop
run_on_dram:
// 长跳转到led_blink开始第二阶段
ldr pc, =led_blink // ldr指令实现长跳转
// 从这里之后就可以开始调用C程序了
//bl led_blink // bl指令实现短跳转
// 汇编最后的这个死循环不能丢
b .