linkers_and_loaders.pdf

符号决议

编译器在遇到外部定义的全局变量或者函数时只要能在当前文件找到其声明,编译器就认为编译正确。而寻找使用变量定义的这项任务就被留给了链接器。链接器的其中一项任务就是要确定所使用的变量要有其唯一的定义。虽然编译器给链接器留了一项任务,但为了让链接器工作的轻松一点编译器还是多做了一点工作的,这部分工作就是符号表(Symbol table)。
符号表中保存的信息有两个部分:

  • 该目标文件中引用的全局变量以及函数;
  • 该目标文件中定义的全局变量以及函数。

编译器在编译过程中每次遇到一个全局变量或者函数名都会在符号表中添加一项,最终编译器会统计一张符号表。

动态库

静态库在编译链接期间就被打包copy到了可执行文件,也就是说静态库其实是在编译期间(Compile time)链接使用的。
动态链接可以在两种情况下被链接使用,分别是加载时动态链接(load-time dynamic linking)运行时动态链接 (run-time dynamic linking)

  • 加载时动态链接:在这里我们只需要简单的把加载理解为程序从磁盘复制到内存的过程,加载时动态链接就出现在这个过程。操作系统会查找可执行文件依赖的动态库信息(主要是动态库的名字以及存放路径),找到该动态库后就将该动态库从磁盘搬到内存,并进行符号决议,如果这个过程没有问题,那么一切准备工作就绪,程序就可以开始执行了,如果找不到相应的动态库或者符号决议失败,那么会有相应的错误信息报告为用户,程序运行失败。
  • 运行时动态链接:run-time dynamic linking 运行时动态链接则不需要在编译链接时提供动态库信息,也就是说,在可执行文件被启动运行之前,可执行文件对所依赖的动态库信息一无所知,只有当程序运行到需要调用动态库所提供的代码时才会启动动态链接过程。

可以使用特定的API来运行时加载动态库,在Windows下通过LoadLibrary或者LoadLibraryEx,在Linux下通过使用dlopen、dlsym、dlclose这样一组函数在运行时链接动态库。当这些API被调用后,同样是首先去找这些动态库,将其从磁盘copy到内存,然后查找程序依赖的函数是否在动态库中定义。这些过程完成后动态库中的代码就可以被正常使用了。
在动态链接下,可执行文件当中会新增两段,即dynamic段以及GOT(Global offset table)段,这两段内容就是是我们之前所说的必要信息。
dynamic 段中保存了可执行文件依赖哪些动态库,动态链接符号表的位置以及重定位表的位置等信息。
当加载可执行文件时,操作系统根据dynamic段中的信息即可找到使用的动态库,从而完成动态链接。、