CSAPP-----链接

本篇目录: 

1、编译器驱动程序

2、静态链接

3、目标文件

4、可重定位目标文件

5、符号和符号表

6、符号解析

7、重定位

8、可执行目标文件

9、加载可执行目标文件

10、动态链接库

11、从应用程序加载和链接共享库

12、位置无关码

13、库打桩机制

14、处理目标文件的工具

15、小结

16、本篇相关实践


 

 

 

本系列文章的观点和图片均来自《深入理解计算机系统第 3 版》仅作为学习使用

         链接(linking)是将各种代码和数据片段收集并组合成为一个单一文件的过程。这个文件可以被加载到内存并执行,链接可以执行于编译时,也就是在源代码被翻译成机器代码时,也可以执行于加载时,也就是程序被加载器加载到内存中并执行,甚至可以执行于运行时,由应用程序执行。在早期,是手动执行,现在基本都是由链接器(Linker)程序自动执行。

        链接器使分离编译成为可能,一个大型程序可以分为更小更好管理的模块,可以独立的修改编译这些模块,掌握链接的知识的好处:(1)理解链接器将帮助你构造大型程序。(2)理解链接器可以帮助你避免一些危险的编程错误。(3)理解链接帮助你理解语言的作用域规则是如何实现的,比如全局变量与局部变量的区别等。(4)理解链接能帮助你理解其他更重要的系统概念。(5)理解链接使你能够利用共享库。

1、编译器驱动程序

       这一部分内容都会以下图的程序作为参考来理解。

        

        

        上图的程序,有两个源文件组成,main.c和sum.c。

        大多数编译系统提供提供编译器驱动程序(compiler driver),它代表用户在需要时调用语言预处理器、编译器、汇编器、链接器。比如在GNU编译系统中需要输入 gcc -Og -oprog main.c sum.c  ,下图概括了驱动程序将上图程序从ASCII源文件翻译成可执行文件时的行为, 首先运行C预处理器(cpp)将C的源程序main.c翻译成一个ASCII的中间文件main.i,接下来驱动程序运行c编译器(cc1)将main.i翻译成一个ASCII汇编语言文件main.s,然后驱动程序运行汇编器(as)将main.s翻译成一个可重定位目标文件main.o,驱动程序经过相同的步骤生成sum.o,最后运行链接器程序ld,将main.o和sum.o以及一些必要的系统目标文件组合起来创建一个可执行文件prog,运行它只需要在shell中输入名字即可。shell会调用操作系统中一个加载器函数将可执行文件prog中代码和数据复制到内存,然后控制转移到这个程序的开头。

2、静态链接

        像linux中ld程序这样的静态链接器,以一组可重定位目标文件和命令行参数作为输入,生成一个完全链接的、可以加载的、运行的可执行文件作为输出,输入的可重定位目标文件由不同的代码和数据节组成,每一节都是一个连续的字节序列。为了构造可执行文件链接器必须完成两个主要任务:

        *符号解析:目标文件定义和引用符号,每个符号都对应一个函数,一个全局变量或一个静态变量符号解析的目的就是将每个符号引用正好和一个符号定义关联起来。

        *重定位:编译器和汇编器生成从地址0开始的代码和数据节,链接器通过把每个符号定义与一个内存位置关联起来,从而重定义这些节,然后修改所有对这些符号的引用,使得它们指向内存这个位置。链接器使用汇编器产生的重定位条目的详细指令。

3、目标文件

        目标文件有三种形式:

        *可重定位目标文件。包含二进制代码和数据,其形式可以在编译时与其他可重定位的目标文件合并起来,创建一个可执行目标文件。

        *可执行目标文件。包含二进制代码和数据,其形式可以直接被复制到内存执行。

        *共享目标文件。一种特殊类型的可重定位目标文件,可以在加载和运行时被动态的加载进内存并链接。

        编译器和汇编器生成可重定位目标文件,链接器生成可执行目标文件。目标文件是按照特定的目标文件格式来组织的,各个操作系统的目标文件格式都不相同。

4、可重定位目标文件

        下图展示了一个典型的ELF可重定位目标文件的格式。ELF头以一个16字节的序列开始,这个序列描述了生成该文件的系统的字的大小和字节顺序,ELF头剩下的部分包含帮助链接器语法分析和解释目标文件的信息,其中包括ELF头的大小、目标文件的类型(可重定位的、可执行的、可共享的)机器类型(如x86-64),节头部表的文件偏移以及节头部表中条目的大小和数量。

        

        夹在ELF头和节头部表之间的都是节。一个典型的ELF可重定位目标文件包含下面几个节:

        .text:已编译的程序的机器代码。

        .rodata:只读数据,比如printf中格式串和开关语句的跳转表。

        .data:已初始化的全局和静态变量a,局部变量在运行时被保存在栈中,既不出现在.data中,也不出现在.bss中。

        .bss:未初始化的全局和静态变量,以及所有被初始化为0的全局变量和静态变量。在目标文件中这个节不占据实际的空间,仅仅是一个占位符,目标文件格式区分已初始化和未初始化变量是为了空间效率,在目标文件中未初始化的变量不需要占据任何实际的磁盘空间。运行时在内存中分配这些变量,初始值为0。

        .symtb:一个符号表,它存放程序中定义和引用的函数和全局变量的信息。

        .rel.text:一个.text节中位置的列表,当链接器把这个目标文件和其他文件组合时需要修改这些位置。

       .rel.data:被模块引用通告或定义的所有全局变量的重定位信息,一般而言,任何已初始化的全局变量,如果他的初始值是一个全局变量地址或者外部定义函数的地址,都需要被修改。

        .debug:一个调试符号表,其条目是程序中定义的局部变量和定义类型,程序中定义和引用的全局变量,以及原始的C源文件,只有以-g选项调用编译器驱动程序时才会用到这张表。

        .line:原始C源程序中的行号和.text节中机器指令之间的映射,以-g选项调用编译器驱动程序时才会用到这张表。

        .strtab:一个字符串表,其中包括.symtab和.debug中的符号i表,以及节头部中的节名字,字符串表就是以null结尾的字符串序列。

5、符号和符号表

        每个可重定位目标模块m都有一个符号表,它包含m定义和引用的符号的信息,在链接器的上下文中有三种不同的符号:

        *由模块m定义并能被其他模块引用的全局符号,全局链接器符号对于非静态的c函数和全局变量。

        *由其它模块定义并被模块m引用的全局符号,这些符号称为外部符号࿰

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值