从零开始的理解代码重定位
--参考朱有鹏ARM裸机编程
1、什么是重定位?
重定位就是代码搬移到你想要的地址,本来程序是运行在运行地址处的,
你可以通过重定位搬移到链接地址处。
2、为什么需要重定位?
大部分的程序是不需要重定位的,但是有时候需要进行重定位,最常见的例子就是
我们的UBOOT,因为我们的UBOOT有200多KB,但是我们开始BL0的地方只有96KB。
所以我们需要在96KB之前进行重定位,使开发板能够进行重定位。
3、重定位的基础知识
(1)一个事实:大部分指令是位置有关编码。
位置无关码:(PIC position independent code):
汇编源文件被编码成二进制可执行程序后与位置无关
位置有关码:汇编编码成二进制可执行程序后和内存地址是有关的。
PS:我们在设计一个程序时,会给这个程序指定一个运行地址。
就是说我们在写程序时,其实我们是知道我们程序将来被运行的地址的。
必须给编译器和链接器指定这个地址才行,最后得到二进制程序。
理论上和你指定的运行地址是有关的,这就叫做位置有关代码。
但是有些特别的指令,他可以跟地址没有关系。
也就是说这些代码实际运行时,不管放在哪里都能正常运行。
链接地址:你认为将来运行的地址:
运行地址:真实在程序中运行的地址:
(2)对于位置有关码来说:最终执行时的运行地址和编译链接时给定的链接地址必须
相同,否则一定会出错。
我们之前的裸机程序中Makefile中用-Ttext 0x0来指定链接地址是0x0
这意味着我们认为这个程序将来会放在这个内存地址中运行。
但是实际上我们运行的地址是下载在开发板的地址0xd0020010
因为是位置无关码,所以运行程序来是没有什么问题的。
而且我们开发板对这些程序进行了映射,所以说这是一个偶然的情况。
(3)我们再来分析一下S5PV210的启动过程。
BL1就是我们外部的前16KB进来,剩下的80K读了进来。
三星推荐的启动方式:
假定你的BootLoader必须小于96KB,并大于16KB。
然后假定Bootloader为80KB:
启动过程模拟:
先开机启动,BL0上电,BL0会加载外部启动设备中的bootloader的前16KB到SRAM,运行BL1
BL1在运行时要加载BL2(bootloader中80 - 16KB = 64KB)
到SRAM的16KB的开始用,去运行BL2会初始化DDR。
并且将OS搬运到DDR去执行OS,启动完成。
UBOOT实际上的启动的方法:
UBOOT的大小随意,假定是200KB
启动过程是这样子的:
先开机上电,BL0运行,BL0会加载外部启动设备中的UBOOT
的前16KB(BL1)到SRAM中去运行,BL1运行会初始化DDR。
然后将整个UBOOT,搬运到我们的DDR中。
然后用一条长跳转指令从SRAM中直接跳转到DDR中继续执行我们的UBOOT。
直到UBOOT完全启动。
长跳转的意思就是从SRAM中跳转到DDR中。
UBOOT启动后在命令行中去执行OS。
4、从源码到可执行程序的步奏:预编译、编译,链接、strip
首先要经过一个预编译的过程。
预编译是在编译之前做的事情,
宏定义就是由预编译来处理的。
注释等也是由预编译处理的。
预编译:比如C中的宏定义就是由预编译器处理的,注释等也是由预编译器处理的。
编译:编译器来执行,把源码中的.c/.s文件转换为.o文件。
把这些汇编的程序变为.o文件。
编译阶段是各自编译各自的文件。
f1段还有很多f2,还有f3段,我们编程是以函数为单位的
每一个函数。
链接:链接器来执行,
把.o文件中的各种函数(段)按照一定的规则(链接脚本来指定)累积到一起,形成可执行程序。
PS:把上面的各种文件链接到一起,类似于炒菜的过程。
要按照一定的顺序来编译。
就是听链接脚本来工作的,形成最后的可执行文件。
按说最后就是生成了可执行程序。
后面还有一些可选步奏。
strip:是把可执行程序中的符号信息给拿掉,
以节省空间,一般可以节省3分之一的空间。
Linux系统启动的时候,会失败的时候会有一些调试的符号信息。
objcopy:由可执行程序生成可烧录的镜像bin文件。
5、链接脚本存在的意义:
1、代码段
(.text)文本段
代码段其实就是函数编译后生成的东西。
2、数据段
(.data):数据段就是C语言中有显示的初始化为非0的全局变量。
3、BSS段
(.bss) :又叫做ZI段,零初始化段,就是初始化为0的全局变量。
C语言中把他放在了BSS段里面
后天的:
4、自定义段
由我们程序员自己定义,段的属性和特征也由我们自己定义。
我们分析一些东西,让你试图明白事物的本质:
1、C语言中如果没有显示初始化,值为0: 因为C语言会把没有初始化的全局变量放到了BSS段里面去。
2、局部变量如果没有初始化的话,值是随机的。
3、C运行时环境如果显示初始化为非0的全局变量的值在main之前放在了.data 中,会在main执行之前就会处理。(初始化)
链接脚本究竟在干神马?
链接的本质是规则文件,他指明了一种行动的规则,他是我们程序员用来指挥链接器工作的。
链接器会参考链接脚本来处理我们.o文件哪些段,将其链接成一个可执行程序。