7.6. 动态链接的步骤和实现

动态链接器自举:
动态链接器本身也是一个共享对象,但是它不能依赖任何其他共享对象,本身所需要的全局和静态变量的重定位工作由它本身完成。自举(Bootstrap)。
自举代码首先找到自己的GOT,而GOT的第一个入口是.dynamic段偏移地址,通过.dynamic中的信息,自举代码可以获得动态链接器本身的重定位表和符号表等,从而得到动态链接器本身的重定位入口,先将他们全部重定位,然后,动态链接器代码才可以使用自己的全局变量和静态变量。

在自举代码中不可以使用全局变量和静态变量,甚至不能调用函数,即使动态链接器本身的函数也不能使用,因为PIC模式下的共享对象,对于模块内部的函数调用也是采用模块外部函数调用一样使用GOT/PLT方式,所以不能调用函数。

装载共享对象:
完成基本自举以后,动态链接器将可执行文件和链接器本身的符号表合并到一个符号表,全局符号表。然后链接器开始寻找可执行文件所依赖的共享对象,在.dynamic段中DT_NEEDED,指出所依赖的共享对象。列出所有可执行文件所需要的所有共享对象,并将这些共享对象的名字放入到一个装载集合中。

然后链接器开始从集合里取一个所需要的共享对象后打开该文件,读取相应的ELF文件头和.dynamic段,然后将它相应的代码段和数据段映射到进程空间中,如果这个共享对象还依赖其他共享对象,那么将所依赖的共享对象的名字放到装载集合中,装载过程就是一个图的遍历过程,一般算法使用广度优先。

当一个新的共享对象被装载,它的符号表会被合并到全局符号表,当所有的共享对象都被装载进去时,全局符号表里面将包含进程中所有的动态链接所需要的符号。


符号
-XLiner -rpath ./ 表示链接器在当前路径寻找共享对象,否则链接器会报无法找到库

一个共享对象里的全局符号被另外一个共享对象的同名全局符号覆盖的现象称为共享对象全局符号介入

当一个符号需要被加入全局符号表时,如果相同的符号名已经存在,则后加入的符号被忽略。

为了调高模块内部函数调用的效率,需要将函数变成私有的使用static定义函数。


重定位和初始化:
链接器开始重新遍历可执行文件和每个共享对象的重定位表,将他们的GOT/PLT的每个需要重定位的位置进行修正。
完成重定位后,如果某个共享对象有.init段,那么动态链接器会执行.init段的代码,比如C++的全局/静态对象的构造。

如果可执行程序也有.init段,动态链接器不会执行它,因为.init段和.finit段由程序初始化部分代码负责执行。

当完成了重定位和初始化之后,动态链接器将进程的控制权交给程序的入口。