连接器的功能,是将一个可执行程序所需的目标文件和库文件最终整合为一体。一个程序通常包含传统的三个段,.test, .data, .bss段。连接器的功能就是将各个目标文件个库文件中的三个段进行合并。
重定位的概念
链接而生成的可执行程序虽然是放在文件中的,但当程序运行时需要加载到内存中。各段应该放在内存空间的声明位置是由可执行文件内的头部信息指定的。
一个程序一旦被加载到内存中,就意味着不论函数还是变量,它们都会在内存中占据一定的内存空间,而这关系到内存地址。假设foo()函数在加载到内存中后其地址刚好位于ox10000处,从处理器的角度看,当我们在c程序写下一句调用foo()函数的语句时,意味着在调用foo()函数时需要跳转到ox10000的内存地址处,那如何知道调用foo()函数时应当跳转到ox10000地址处的呢?这就是连接器所需要完成的工作。
链接脚本的功能是告诉连接器,如何将各个不同的目标文件(包括库)中的段合在一起并最终生成一个可执行程序(文件)。从链接脚本的角度来看,一方面它需要描述输出,即最终输出到可执行程序文件中的段;另一方面又要描述输入,即来自各个目标文件中的段。
/*********以下摘自:http://www.cnblogs.com/amanlikethis/p/3344519.html *********/
1.连接顺序的问题
SECTIONS {
firtst 0x00000000 : {head.o init.o nand.o}
second x30000000 : AT(4096){ main.o}
}
这个规则中定义了两个大段,first和second。
first的链接顺序为head.o init.o nand.o. Second的链接顺序为main.o。
2.链接地址的问题
先说明一下链接地址的概念,链接地址是程序实际运行的地址。通常程序中有位置无关代码和位置有关代码。位置无关代码是对于链接地址无要求,可以在不是它链接地址的地方运行;但是位置有关代码,必须在链接地址运行。也就是说当运行位置有关代码时,程序必须事先在链接地址上,如果没有在,通常需要COPY到那个位置或者利用MMU映射一下。
下边以一个例子来说明一下链接脚本中链接地址的问题。
SECTIONS {
firtst 0x00000000 : {head.o}
second 0x00000200 : AT(300) {init.o}
third 0x00000400 : AT(500) {nand.o}
fourth 0x30000000 : AT(3096) { main.o}
}
四个部分:first、second、third、foutth,它们的链接地址分别是0x00000000、0x00000200、0x00000400、0x30000000。
3.加载地址
加载地址指的是程序编译后的存放地址,通常存放在ROM、FLASH中,所以就是指这段程序在ROM、FLASH中的存放位置。还是以上边的连接脚本为例。
SECTIONS {
firtst 0x00000000 : {head.o}
second 0x00000200 : AT(300) {init.o}
third 0x00000400 : AT(500) {nand.o}
fourth 0x30000000 : AT(3096) { main.o}
}
它们的存放地址分别是0、300、500、3096。
/**********摘抄完毕********/
之所以摘抄这一段园友的博客,是因为在2440上,就有这样的链接脚本:
因为之前看关于ld的介绍资料,全是data,bss,test段开头,其实段名是可以自己取得;
SECTIONS {
firtst 0x00000000 : { head.o init.o }
second 0x30000000 : AT(4096) { main.o }
}
以上,head.o放在0x00000000地址开始处,init.o放在head.o后面,他们的运行地址也是0x00000000,即连接和存储地址相同(没有AT指定);main.o放在4096(0x1000,是AT指定的,存储地址)开始处,但是它的运行地址在0x30000000,运行之前需要从0x1000(加载处)复制到0x30000000(运行处),此过程也就用到了读取Nand flash。
这就是存储地址和连接(运行)地址的不同,称为加载时域和运行时域,可以在.lds连接脚本文件中分别指定。
这里有很多选项,但常用的也就那么一点。关于链接在第六章还有其他一些介绍,但是目前只是为了学习arm板,如果以后在2440上用到了其他功能,再在这里补充。