静态重定位:静态重定位是在程序执行之前进行重定位,它根据装配模块将要装入的内存起始位置,直接修改装配模块中的有关使用地址的指令
我们下面要分析就是静态重定位的情况。
动态重定位:动态重定位是指,不是在程序执行之前而是在程序执行过程中进行地址重定位。更确切地说,是在每次访问内存单元前才进行地址变换。动态重定位可使装配模块不加任何修改而装入内存,但是它需要硬件——定位寄存器的支持。
图片的参考来源:http://c.biancheng.net/cpp/html/2608.html
上面重定位的定义的参考来源:http://bbs.pediy.com/showthread.php?t=76876
我们的程序中的代码可分为:位置无关码和位置有关码。顾名思义,位置无关码就是代码在哪个地址运行都行的。那么位置有关码呢?就必须在规定的地址处执行才可以,这个规定的地址就是链接地址,而我们代码执行时候的地址是运行地址,位置有关码就要求链接地址和运行地址必须一致,执行的时候才不会出错。
譬如:uboot实际使用的方式:uboot大小随意,假定为200KB。启动过程是这样子:先开机上电后BL0运行,BL0会加载外部启动设备中的uboot的前16KB(BL1)到SRAM中去运行,BL1运行时会初始化DDR,然后将整个uboot搬运到DDR中,然后用一句长跳转(从SRAM跳转到DDR)指令从SRAM中直接跳转到DDR中继续执行uboot直到uboot完全启动。uboot启动后在uboot命令行中去启动OS。
扩展:分散加载:把uboot分成2部分(BL1和整个uboot),两部分分别指定不同的链接地址。启动时将两部分加载到不同的地址(BL1加载到SRAM,整个uboot加载到DDR),这时候不用重定位也能启动。
注释:
在之前的210中的裸机程序中,运行地址由我们下载时确定,下载时下载到0xd0020010,所以就从这里开始运行。(这个下载地址也不是我们随意定的,是iROM中的BL0加载BL1时事先指定好的地址,这是由CPU的设计决定的)。所以理论上我们编译链接时应该将地址指定到0xd0020010,但是实际上我们在之前的裸机程序中都是使用位置无关码PIC,所以链接地址可以是0。
链接脚本的代码如下:看懂下面的代码我们还需要了解:bss段,数据段(.data),代码段(.text)等知识,可以参考我的这篇博客:
程序中的bss段,数据段(.data),代码段(.text):http://blog.csdn.net/czg13548930186/article/details/54882907
然后,我们通过Makefile,编译链接程序时可以指定按照链接脚本的顺序来链接代码:arm-linux-ld -Tlink.lds-o led.elf $^
link.lds为链接脚本的名字:
SECTIONS { . = 0xd0024000; /*指定链接地址为0xd0024000*/ .text : { /*代码段*/ start.o /*指定链接的顺序为:start.o->sdram_init.o->其他的一些文件*/ sdram_init.o * (.text) /*这里表示其他的一些.o文件*/ } .data : { /*数据段*/ * (.data) /*这里表示所有的数据段的文件*/ } bss_start = .; /*把当前的地址赋值给bss_start*/ .bss : { /*bss段*/ * (.bss) /*所有bss段的文件*/ } bss_end = .; /*把当前的地址赋值给bss_end*/ }
问题:
1.为什么最后我们要用ldr长跳转而不是b短跳转?
因为实际上此时在SRAM中有2个czg_led函数镜像,两个都能执行,如果短跳转b czg_led则执行的就是0xd0020010开头处的这一份,
如果长跳转ldr pc, =czg_led则执行的是0xd0024000开头处的这一份)。这就是短跳转和长跳转的区别。
2.为什么复制的长度是代码段 +数据段的长度而bss段不需要重定位?
bss段在程序中并不占用任何空间,只是一个占位符,不占用执行文件的空间,没必要复制过去增加难度。我们知道C语言要求的运行条件是要将栈设置好,并且bss段要初始化为0;
一般情况下我们的程序是不需要负责清零bss段的(C语言编译器和链接器会帮我们的程序自动添加一段头程序,这段程序会在我们的main函数之前运行,这段代码就负责清除bss).但是在我们代码重定位了之后,因为编译器帮我们附加的代码只是帮我们清除了运行地址那一份代码中的bss,而未清除重定位地址开头的那一份代码的bss,所以重定位之后需要自己去清除bss.
总结:由上面我们可以看到:重定位的时候,先使用一段位置无关码来对重定位的地址那里的内存进行一些操作:
(1)把整段代码搬运过重定位的内存那里(用copy_loop来实现)
(2)清bss段(clean_bss来实现)
(3)跳转到重定位的那段内存去执行(run_on_dram来实现)
注意:上面那些汇编指令所实现的功能我们可以通过反汇编文件(.dis文件)来验证:arm-linux-objdump -D led.elf > led_elf.dis
