第七章 链接


链接(linking) 是将各种代码和数据片段收集并组合成为一个单一文件的过程,这个文件可以被加载(复制)到内存并执行。

  • 静态链接:以一组可重定位目标文件和命令行参数作为输入,生成一个完全链接的、可以加载和运行的可执行目标文件作为输出。使用静态库(Linux系统中通常由后缀.a标识)时,一般将它们放在命令行的结尾,如果静态库之间不是相互独立的,必须对它们进行排序。
  • 动态链接:目标模块(共享库)在运行或加载时加载到任意的内存地址,并和一个在内存中的程序链接起来

链接器有两个主要任务

  1. 符号解析:目标文件定义和引用符号,每个符号对应于一个函数、一个全局变量或一个静态变量,符号解析将每个符号引用和符号定义关联起来
  2. 重定位:编译器和汇编器生成从地址0开始的代码和数据节,链接器把每个符号定义与一个内存位置关联起来,并修改这些符号的引用,使得它们指向这个内存位置

编译器驱动程序将源文件翻译为目标文件


  1. 程序代码为ASCII码源文件,如main.c
  2. 驱动程序运行C预处理器(cpp),将源程序翻译为ASCII码中间文件,如main.i
  3. 驱动程序运行C编译器(ccl),将中间文件翻译为ASCII汇编文件,如main.s
  4. 驱动程序运行汇编器(as),将汇编文件翻译成可重定位目标文件,如main.o
  5. 运行链接器程序(ld),将main.o和一些必要的目标文件组合起来,创建一个可执行目标文件,如prog
  6. shell调用操作系统中叫做加载器的函数,将可执行文件prog中的代码和数据复制到内存,然后将控制转移到这个程序的开头

目标文件


目标文件纯粹是字节块的集合。 这些块中,有些包含程序代码,有些包含程序数据,其他的则包含引导链接器和加载器的数据结构。链接器将这些块连接起来,确定被连接块的运行时位置,并且修改代码和数据块中的各种位置。

目标文件有三种形式

  1. 可重定位目标文件:编译时与其他可重定位目标文件合并起来,创建一个可执行目标文件
  2. 可执行目标文件:可以被直接复制到内存并执行
  3. 共享目标文件:特殊的可重定位目标文件,可以在加载或者运行时被动态的加载进内存并链接

GNU binutils包 可以帮助理解和处理目标文件,且可以运行在每个Linux平台。

符号和符号表


每个可重定位模块都有一个符号表,包含该模块定义和引用的符号的信息。

符号有三种类型

  1. 模块定义且能被其他模块引用的全局符号,对应于非静态的C函数和全局变量
  2. 由其他模块定义并被本模块引用的全局符号,又称为外部符号,对应于其他模块中定义的非静态的C函数和全局变量
  3. 只被模块定义和使用的局部符号,对应于带static属性的C函数和全局变量(不包括本地非静态程序变量,这些符号在运行时在栈中被管理)

函数和已初始化的全局变量称为强变量,未初始化的变量称为弱变量。Linux链接器使用下面规则处理多重定义的符号名:

  1. 不允许有多个同名的强符号
  2. 如果有一个强符号和多个弱符号同名,那么选择强符号
  3. 如果有多个弱符号同名,那么从这些弱符号中任意选择一个

重定位


符号解析之后进行重定位,这一步将合并输入模块,并为每个符号分配运行时地址。重定位分为两步:

  • 重定位节和符号定义:链接器将所有相同类型的节合并为同一类型的新聚合节,程序中的每条指令和全局变量都有唯一的运行时内存地址
  • 重定位节中的符号引用:链接器修改代码节和数据节中对每个符号的引用,使得它们指向正确的运行时地址

动态链接共享库


静态库需要定期维护和更新,而且相关函数的代码会被复制到每个运行进程的文本段中。

共享库是一个目标模块,在运行或加载时,可以加载到任意的内存地址,并和一个在内存中的程序链接起来。这个过程叫做动态链接,共享库也称为共享目标(shared object)。Linux系统中通常用.so后缀标识共享库,微软通常使用DLL(动态链接库)。
一个库只有一个.so文件,所有引用该库的可执行目标文件共享这个.so文件中的代码和数据,而不是像静态库的内容那样被复制和嵌入到引用它们的可执行文件中。在内存中一个共享库的.text节的一个副本可以被不同的正在运行的进程共享。

构造共享库的一个例子,使用如下指令:

linux> gcc -shared -fpic -o libvector.so addvec.c multvec.c  
// 使用共享库进行链接  
linux> gcc -o prog21 main2.c ./libvector.so  

可以加载而无需重定位的代码称为位置无关代码(Position-Independent Code,PIC),用户对GCC使用-fpic选项指示GNU编译系统生成PIC代码。共享库的编译必须总是使用该选项。

评论

还没有登陆?评论请先登陆注册

还没有评论,抢个沙发吧!

 联系方式 contact me

Github
Email
QQ
Weibo