zoukankan      html  css  js  c++  java
  • 《深入理解计算机系统》读书笔记五

    第七章 链接

    7.1 编译器驱动程序

    大多数编译系统提供编译驱动程序,它代表用户在需要时调用语言预处理器、编译器、汇编器和链接器。

    7.2 静态链接

       像Unix ld程序这样的静态链接器以一组可重定位目标文件和命令行参数作为输入,生成一个完全链接的可以加载和运行的可执行目标文件作为输出。输入的可重定位目标文件由各种不同的代码和数据节组成。指令在一个节中,初始化的全局变量在另一个节中,而未初始化的变量又在另外一个节中。
    
       为了构造可执行文件,链接器必须完成两个主要任务:
    
    • 符号解析 目标文件定义和引用符号。符号解析的目的是将每个符号引用刚好和一个符号定义联系起来。
    • 重定位 编译器和汇编器生成从地址0开始的饿代码和数据节。链接器通过把每个符号定义与一个存储器位置联系起来,然后修改所有对这些符号的引用,使得它们指向这个存储器位置,从而重定位这些节。
      链接器的一些基本事实:目标文件纯粹是字节块的集合。这些块中,有些包含程序代码,有些则包含程序数据,而其他的则包含指导链接器和加载器的数据结构。链接器将这些块连接起来,确定被连接块的运行时位置,并且修改代码和数据块中的各种位置。链接器和汇编器已经完成了大部分工作。

    7.3 目标文件

      编译器和汇编器生成可重定位目标文件(包括共享目标文件)。链接器生成可执行目标文件。从技术上来说,一个目标模块就是一个字节序列,而一个目标文件就是一个存放在磁盘文件中的目标模块。
    
      编译器和汇编器生成可重定义目标文件(包括共享目标文件)。链接器生成可执行目标文件。
    
      各个系统之间,目标文件格式都不相同。
    

    7.4 可重定位目标文件

      一个典型的ELF可重定位目标文件的格式P451。ELF头(ELF header)以一个16字节的序列开始,这个序列描述了生成该文件的系统的字的大小和字节顺序。ELF头剩下的部分包含帮助链接器语法分析和解释目标文件的信息。其中包括ELF头的大小、目标文件的类型(如可重定位、可执行或是共享的)、机器类型(如IA32)、节头部表的文件偏移,以及节头部表中的条目大小和数量。不同的节的位置和大小是由节头部表描述的,其中目标文件中每个节都有一个固定大小的条目。
    
      夹在ELF头和节头部表之间的都是借。一个典型的ELF可重定位目标文件包含下面几个节:
    
    • .text 已编译程序的机器代码
    • .rodata 只读数据
    • .data 已初始化的全局C变量。局部C变量在运行时保存在栈中,既不出现在.data节中 ,也不出现在.bss节中。
    • .bass 未初始化的全局C变量。在目标文件中这个节不占据实际的空间,它仅仅是一个占位符。目标文件格式区分初始化和未初始化变量是为了空间效率:在目标文件中,未初始化变量不需要占据任何实际的磁盘空间。
    • .symtab 一个符号表,它存放在程序中定义和引用的函数和全局变量的信息。每个可重定位目标文件在.symtab中都有一张符号表 。
    • .rel.text 一个.text节中位置的列表,当链接器吧这个目标文件和其他文件结合时,需要修改这些位置。一般而言,任何调用外部函数或引用全局变量的指令都需要修改。另一方面,调用本地函数的指令则不需要修改。注意,可执行目标文件中并不需要重定位信息,因此通常省略,除非用户显示第指示链接器包含这些信息。
    • .rel.data 被模块引用或定义的任何全局变量的重定位信息。一般而言,任何已初始化的全局变量,如果它的初始值是一个全局变量地址或者外部定义函数的地址,都需要被修改。
    • .debug 一个调试符号表,其条目是程序总定义的局部变量和类型定义,程序中定义和引用的 全局变量,以及原始的C源文件。
    • .line 原始C源文件中的行号和.text节中机器指令之间的映射。
    • .strtab 一个字符串表,其内容包括.symtab和.debug节中的符号表,以及节头部中的节名字。
    • 7.5 符号和符号表
      在链接器的上下文中,有三种不同的符号:

    1.由m定义并能被其他模块引用的全局符号
    2.由其他模块定义并被模块m引用的全局符号
    3.只被模块m引用的本地符号

    7.6 符号解析

    7.6.1 链接器如何解析多重定义的全局符号

       在编译是,编译器向汇编器输出每个全局符号,或者是强或者是弱,而汇编器把这个信息隐含地编码在可重定位目标文件的符号表里。函数和已初始化的全局变量时强符号,未初始化的全局变量是弱符号。
    
       根据强弱符号的定义,Unix链接器使用下面的规则来处理多重定义的符号:
    
    1. 规则1:不允许有多个强符号。
    2. 规则2:如果有一个强符号和多个弱符号,那么选择强符号。
    3. 规则3:如果有多个弱符号,那么从这些弱符号中任意选择一个。

    7.6.2 与静态库链接

        在Unix系统中,静态库以一种称为存档的特殊文件格式村凡在磁盘中。存档文件是一组连接起来的可重定位目标文件的集合,有一个头部用来描述每个成员目标文件的大小和位置。存档文件名由后缀.a标识。
    

    7.6.3 链接器如何使用静态库来解析引用

        在符号解析的阶段,链接器从左到右按照它们在编译器驱动程序命令行上出现的相同顺序来扫描可重定位目标文件和存档文件。在这次扫描中,链接器维持一个可重定位目标文件的集合E(这个集合中的文件会被合并起来形成可执行文件),一个未解析的符号(即引用了但是尚未定义的符号)集合U,以及一个在前面输入文件中已定义的符号集合D。初始时,E、U和D都是空的。
    

    1.对于命令行上的每个输入文件f,链接器会判断f是一个目标文件还是一个存档文件。如果f是一个目标文件,那么链接器吧f添加到E, 修改U和D来反映f中的符号定义和引用,并继续下一个输入文件。

    2.如果f是一个存档文件,那么链接器就尝试匹配U中未解析的符号和由存档文件成员定义的符号。如果某个存档文件成员m,定义了一个符号来解析U中的一个引用,那么就将m加到E中,并且链接器修改U和D来反映m中的符号定义和引用。对存档文件中所有的成员目标文件都反复进行这个过程,直到U和D都不再发生变化。在此时,任何不包含在E中的目标文件都简单地被丢弃,而链接器将继续处理下一个输入文件。

    3.如果当链接器完成对命令行上输入文件的扫描后,U是非空的,那么链接器就好输出一个错误并终止。否则,它会合并和重定位E中的目标文件,从而构建输出的可执行文件。

       这种算法会导致一些令人困扰的链接时错误,因为命令行上的库和目标文件的顺序非常重要。在命令行中,如果定义一个符号的库出现在引用这个符号的目标文件之前,那么引用就不能被解析,链接会失败。关于库的一般准则是将它们放在命令行的 结尾。
    
      另一方面,如果库不是相互独立的,那么它们必须排序,使得对于每个被存档文件的成员外部引用的符号s,在命令行中至少有一个s的定义实在对s的引用之后的。
    
      如果需要满足依赖需求,可以在命令行上重复库。
    

    7.7 重定位

      一旦链接器完成了符号解析这一步,它就是把代码中的每个符号引用和确定的一个符号定义(即它的一个输入目标模块中的一个符号表条目)联系起来。在此时,链接器就知道它的输入目标模块中的代码节和数据节的确切大小。现在就可以开始重定位了,在这个步骤中,将合并输入模块,并为每个符号分配运行时地址。
  • 相关阅读:
    BZOJ_2588_Spoj 10628. Count on a tree_树剖+主席树
    BZOJ_1901_Zju2112 Dynamic Rankings_树状数组+主席树
    单例模式
    JDBC连接数据库查询信息的步骤(提取成配置文件方式)
    JDBC访问数据库查询信息的步骤(硬编码格式)
    大数据
    accp
    递归
    struts2中Action到底是什么,怎么理解
    转发和重定向的区别(简单解释)
  • 原文地址:https://www.cnblogs.com/yswysw/p/5360180.html
Copyright © 2011-2022 走看看