链接、装载与库——静态链接

来源:互联网 发布:淘宝oppo手机 编辑:程序博客网 时间:2024/05/21 04:20

*赶紧结束这本书的总结。。太拖了。。→_→*

第四章 静态链接

1.链接器的空间与地址分配

链接器为目标文件分配地址和空间
可执行文件中的代码段和数据段由输入的目标文件中合并而来。

  • 按序叠加:直接将各个目标文件依次合并
    简单的分配策略
    缺点:若有多个目标文件,则可能造成输出文件中有很多零散的段。每个段都有空间对齐要求,可能造成内存空间的大量内部碎片

  • 相似段合并:将相同性质的段合并到一起
    实际的内存分配

    • “.bss”段在目标文件和可执行文件中并不占用文件的空间(此空间指实际的物理数据空间),但其在装载时占用地址空间(此空间指虚拟地址空间)。

    • 使用此策略的链接器一般都是两步链接(Two-pass Linking)
      第一步:空间地址分配。扫描所有的输入目标文件,获取各段长度、属性和位置,将它们合并,计算出输出文件中各段合并后的长度和位置,并建立映射关系(此时为合并之后的各段分配虚拟地址)。将符号表中的符号定义和符号引用收集,统一放到一个全局符号表
      第二步:符号解析与重定位。读取输入文件中段的数据、重定位信息,进行符号的解析与重定位、调整代码中的地址

2.链接器的符号解析与重定位

  • 重定位目标文件在链接的空间分配之前,代码段中的起始地址为0x00000000。编译器在编译阶段使用临时的假地址表示未知符号的地址,而将真正的地址计算工作留给链接器。链接器完成空间与地址分配后,根据符号的虚拟地址对每个需要重定位的指令进行修正

  • 重定位表(Relocation Table):保存与重定位相关的信息,用来描述如何修改相应的指令。每个要被重定位的ELF段都有一个对应的重定位表(或叫重定位段)。比如:“.rel.text”、“.rel.data”。

  • //重定位表结构typedef struct{    //被重定位的地方叫**重定位入口(Relocation Entry)**    //表示重定位入口的**偏移**    //对于可重定位文件,即**所要修正的位置的第一个字节相对段起始的偏移**    //对于可执行文件或共享对象文件,即**所要修正的位置的第一个字节的虚拟地址**    Elf32_Addr r_offset;    //**重定位入口的类型和符号**,低8位表示重定位入口的类型,高24位表示重定位入口的符号在符号表中的下标     Elf32_Word r_info;}Elf32_Rel;
  • 符号解析:重定位的过程中,每个重定位的入口都是一个符号的引用,当链接器对某个符号的引用进行重定位时,链接器会去查找由所有输入目标问价的符号表组成的全局符号表,找到相应的符号后进行重定位。

3.C++与静态链接的问题

  • C++编译器可能在不同的编译单元中生成相同的代码(模板(Templates)、外部内联函数(Extern Function)、虚函数表(Virtual Function Table))。
    若不消除重复代码,可能造成空间浪费、地址出错(两个函数指针不相等)、指令运行效率低(CPU对指令和数据进行缓存,副本过多造成指令Cache命中率低)

  • 解决方法:将每个模板的实例代码都单独存放在一个段里,每个段只包含一个模板实例。这样链接器在最终链接时可以区分相同的模板实例段,将它们合并入代码段。

  • GCC中叫做“Link Once”,将此类段命名为“.gun.linkonce.name”,“name”是该模板函数实例的修饰后名称

  • 当用到某个目标文件中的一个函数或变量时,需要将其整个链接,随着文件数量地增多,造成输出文件越来越大。
    Visual C++中使用函数级别链接(Function-Level Linking)选项,其作用就是让所有的函数都单独保存到一个中,当链接器用到它时,就将其合并到输出文件中,抛弃没有用到的函数
    这种方减少了输出文件地大小,减少了空间浪费。但会减慢编译和链接过程,因为链接器要计算个函数之间的依赖关系,且目标函数段数量增大,重定位过程也会变得复杂。

4.静态库链接

  • 静态库是一组目标文件的集合,即很多目标文件经过压缩打包后形成的一个文件
  • 静态库链接
  • 链接器自动寻找所有需要的符号及其所在的目标文件从静态库中“解压”出来
  • 静态库中的一个目标文件只包含一个函数,每个函数独立的存放在一个目标文件中可以尽量减少空间浪费
原创粉丝点击