C语言之链接知识

函数库、链接、载入

编译器创建一个包含可重定位对象的输出文件,可重定位的对象就是与源程序对应的数据和机器指令

绝大多数编译器都是由多达六七个稍小的程序组成的,这些程序由“编译器驱动器”的控制程序来调用,这些稍小的可以很方便的从编译器中分离出来的单独程序包括:预处理器、语法和语义检查器、代码生成器、汇编程序、优化器、链接器,当然还包括一个调用所有这些程序并向各个程序传递正确选项的驱动器程序,优化器几乎可以加在上述所有阶段的后面
C语言之链接知识_第1张图片
可以通过给编译器驱动器一个特殊的“-W”选项向各个阶段传递选项信息,“W”后紧跟一个阶段字符提示哪个阶段,一个“,”,然后就是具体的选项,所以如果要从编译器驱动器向链接器传递任何选项,必须在具体的选项前面加上“-W1”前缀

cc -W1, -m main.c > main.linker.map

将“-m”选项传递给链接-载入器,要求它产生链接器映像

目标文件并不能直接执行,首先需要载入到链接器中,链接器确认main函数为初始进入点,把符号引用绑定到内存地址,把所有的目标文件集中在一起,在加上库文件,从而产生可执行文件

静态链接:函数库的一份拷贝是可执行文件的物理组成部分
动态链接:可执行文件只是包含了文件名,载入器在运行时寻找程序所需的函数库

收集模块准备执行的三个阶段的规范名称:链接-编辑、载入、运行时链接。
静态链接的模块被链接编辑并载入以便运行
动态链接的模块被链接编辑后载入,并在运行时进行链接以便运行

程序执行时,在main()函数被调用前,运行时载入器把共享的数据对象载入到进程的地址空间,外部函数被真正的调用之前,运行时载入器并不解析它们,所以即使链接了函数库,如果并没有实际调用,也不会带来额外开销
C语言之链接知识_第2张图片
在静态链接中,也不是将整个libc.a全部装入可执行文件中,装入的只是需要的函数

动态链接的优点

动态链接的目的之一是ABI:动态链接的主要目的就是把程序从它们所使用的的特定的函数库版本中分离出来,取而代之的是,约定由系统向程序提供一个稳定、不随时间和操作系统版本发生变化的接口,程序可以调用接口所承诺的服务,而不必担心这些功能是怎样实现的或者它们的底层是否改变,由于它是介于应用程序和函数库二进制可执行文件所提供的服务之间的接口,所以称它为应用程序二进制接口(Application binary interface)

尽管单个可执行文件的启动速度稍受影响,但动态链接可以从两个方面提升性能

  1. 动态链接可执行文件比功能相同的静态链接可执行文件的体积小,可以节省磁盘空间和虚拟内存,因为函数库只有在需要的时候才被映射到进程中
  2. 所有动态链接到某个特定函数库的可执行文件在运行时共享该函数库的一个单独拷贝,操作系统内核保证映射到内存中的函数库可以被所有使用它们的进程共享,如果可执行文件是静态链接的,每个文件都将拥有一份函数库的拷贝,显然极为浪费

动态链接使得函数库的版本升级更加容易,新的函数库可以随时发布,只要安装到系统中,旧的程序就能够自动获得新版本函数库的优点而无需重新链接

动态链接是一种“Just in time(JIT)”链接,程序在运行时必须能够找到它们所需的函数库,链接器通过把库文件名或路径名植入可执行文件中来做到这一点,这意味着函数库的路径不能随意移动

使用静态链接的最大危险在于将来版本的操作系统可能与可执行文件所绑定的系统函数库不兼容,如果应用程序静态链接于N版本的操作系统中,当把程序运行于N+1版本操作系统时,它可能立即崩溃,也可能出现一个不明显的错误

静态库被称作archive,它们通过ar(用于archive的实用工具)来创建和更新,约定以“.a”作为扩展名
动态链接库由链接编辑器ld创建,约定以“.so”(shared object)作为扩展名

函数库链接的5个特殊秘密

  1. 动态库文件的扩展名是“.so”,而静态库文件的扩展名是“.a”
  2. 例如,你通过“-lthread”选项,告诉编译链接到“libthread.so”
  3. 编译器期望在确定的目录中找到库
  4. 观察头文件,确认所使用的函数库
  5. 与提取动态库中的符号相比,静态库中的符号提取方法限制更严
    在动态链接中,所有的库符号进入输出文件的虚拟地址空间中,所有的符号对于链接在一起的所有文件都是可见的,相反对于静态链接,在处理archive时,它只是在archive中查找载入器当前所知道的未定义符号

警惕Interpositioning

Interpositioning(或interposing)就是通过编写与库函数同名的函数来取代该库函数的行为

使用Interpositioning需要格外小心,很容易发生自己代码中某个符号的定义取代函数库中的相同符号,不仅自己所进行的所有对该函数库的调用被自己版本的函数所取代,而且所有调用该函数库的系统调用也将用自己的函数取而代之

你可能感兴趣的:(C/C++)