链接就是将多个.o文件连接成为一个可执行文件的过程。链接中最终要的部分就是空间地址分配和符号的解析和重定位。
首先说空间地址分配:扫描所有的输入目标文件,获得他们各个段的长度、属性和位置,并进一步将输入目标文件的符号表中所有的符号定义和符号引用收集起来,统一放到一个全局符号表里。在这一步中,连接器能获得所有输入段的长度,并将它们合并,计算出合并后的段长度和位置,并建立映射关系。
符号解析和重定位:使用上面第一步中收集到的所有信息,读取输入文件中的短的数据、重定位信息,并且进行符号的解析和重定位、调整代码中的地址等。事实上第二步是链接过程的核心。特别是重定位过程。
l inux中使用ld a.o b.o -e main -o ab来进行链接。
在.o文件中不存在虚拟地址,链接后产生虚拟地址,符号地址确定。对于可执行文件中的bss段,在elf文件中是没有占据空间的,但是在链接的过程中却要为bss数据段分配虚拟内存空间。
下面描述一下在静态链接中的几个重要概念:
重定位表:对于.o文件中任何未定义的符号都在重定位表里有一项与之对应,该项使用Elf32-rel数据结构表示,其中包括了重定位入口偏移(重定位的符号相对于段的偏移)和与之对应的符号在符号表中的下标。
COMMON块:我们都知道符号类型对链接器是透明的,也就是链接器不知道符号的类型。当有多个同名符号类型不一致的时候就会出现问题。为此设置了一种COMMON块机制,及当多个同名的弱符号类型不一致的时候以占据空间大的那个为准。存在强符号和弱符号类型不一致时以强符号为准,不过当弱符号大小大于强符号时,会发出警告。
重复代码删除问题:以C++模板函数为例,在某个编译单元可能产生了add<int> 和add<double>类型的代码,在另一个编译单元中也存在add<int>的代码,这个时候链接器就会发现,并删除重复代码。
函数级别的链接,对于有些库而言其异常的庞大,在用到其中一个函数的时候就将整个库包含进来,为解决这个问题可将函数生成独立的段,在链接过程中只包含有用的部分,减少可执行文件的大小。