Uboot重定位是uboot启动后的一个重要功能,重定位的目的是为了让uboot运行在速度性能更好的RAM上,一般是从外部RAM搬移到内部RAM。重定位这块说简单也简单,说复杂也复杂,主要涉及到编译和链接等相关原理才能很好的理解整个过程。另外,理解了ARM的重定位,对于Kernel启动过程的重定位也就理解了。
编译器惹的祸
uboot重定位不是简单的copy,归根结底在于gcc的编译和链接过程。编译和链接的过程决定了重定位不是简单的copy,而是一件包含有技术含量的事情。
举个简单的例子来说明为什么不是简单的copy。ARM访问一个全局变量,一般的情况是先将地址写到寄存器里面,然后通过寄存器寻址来访问。假设地址是0xC0000000,那么ARM指令是无法直接将地址写入的,因为ARM最长指令也才32bit,不可能包含一个32bit的地址。那是不是可以分两步写?这个是可以,但ARM的编译器通常不这么做。
ARM会在编译的时候产生一个section,这个section是紧邻着代码段,这个section就会保留一个word存储了0xC0000000这个地址。当ARM在这段代码中要访问这个全局变量的时候,ARM会寻到这个section的起始地址,然后编译器计算这个偏移,根据起始地址和偏移计算得到保存0xC0000000的地址,最终得到这个需要地址再进行寻址。注意,从section的首地址到计算偏移这个过程是在编译链接的时候就完成了,因此代码中看到的汇编就是对地址的直接访问。
举例来说:
0x0000 test()...
/* read 全局变量,地址为0xC0000000 */
0x0010 ldr r1, [r0] /* 读取保存0xC0000000的地址 */
0x0014 ldr r2, [r1] /* 读取0xc0000000的内容 */
...
0x0100 .section
0x0100 0xC0000000
简单来说,在每个代码段都会包含一个section,这个section保留了代码里面由于指令长度限制不能直接访问的变量或者函数的地址。
那么,重定位的时候,所有代码和数据都不在原来位置上了,那么这个section里面的所有数据的内容也就要改变。因此,简单的copy是不行的,还有些section里面的数据要重新写入。<