动态链接的步骤与实现

来源:互联网 发布:淘宝发空包会签收吗 编辑:程序博客网 时间:2024/05/10 22:27

动态链接器本身也是一个共享对象,但事实上其本身具有一些特殊性。动态链接器要能完成对普通共享对象的链接与装载,那对链接器本身,其重定位工作又该由谁来完成?

1.对动态链接器的要求:

  为了解决上述的问题,对动态链接器有以下两个要求:


1.动态链接器本身不可以依赖于其他共享对象

2.动态链接器本身所需要的全局与静态变量的重定位工作要由其自身完成

2.满足要求的办法:

对于动态链接器的第一个要求我们可以认为的控制,在编写动态链接器时不适用任何系统库,运行库;但对第二个要求,动态链接器必须在启动时有一段非常精巧的代码可以完成重定位工作同时有不能用到全局和静态变量。这种有限制的启动代码称为自举

3.Linux下动态链接器工作的全过程:

内核在装载完ELF可执行文件后返回到用户空间,并将控制权交给程序的入口,对静态链接的可执行文件而言,程序的入口就是ELF文件头里面指定的入口;对动态链接的可执行文件而言,内核会分析它的动态链接器地址(在.interp段中指定),将动态链接器映射至进程地址空间,然后将控制权交给动态链接器

动态链接器的入口地址即是自举代码的入口地址,自举代码首先会找到自己的GOT,而GOT的第一个入口保存的是".dynamic"段的偏移地址,由此找到该段,通过“.dynamic”段可以获得动态链接器本身的重定位表和符号表等,从而得到动态链接器本身的重定位入口,完成自身的重定位。从这一步开始,动态链接器才可以使用自己的全局变量和静态变量。

完成基本自举后,动态链接器将可执行文件和链接器本身的符号表都合并到一个全局符号表中,然后链接器在“.dynamic”段中找到该可执行文件依赖的共享对象,链接器将这些共享对象的名字加入到一个装载集合中,然后链接器从这个集合中取一个共享对象的名字,找到相应的文件后打开该文件,读取相应的ELF头与其“.dynamic”段,然后将相应的代码段与数据段映射到进程空间中,如果这个共享对象还依赖别的对象,则继续加入装载集合,循环直到所有相关共享对象均加入到进程空间,通常,采用广度优先加载共享对象。

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

当所有共享对象加载完毕之后,动态链接器将控制权交回给ELF头中指定的程序入口地址

4.符号之间的优先性

全局符号介入(Global Symbol Interpose):一个共享对象中的全局符号被另一个共享对象中的同名全局符号覆盖的现象

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

这种处理方式将会导致后加入的函数被忽略不得以执行,当两个函数功能不相同时,可能会在莫名其妙的地方报错。

5.动态链接器的性质

动态链接器是一个特殊的共享对象,它不仅是共享对象,还是可执行文件。Linux的内核在执行execve()时不关心目标ELF文件是否可执行,它的工作是按照程序头表里的描述对文件进行装载然后将控制权转给ELF的入口地址。而共享对象其实也是ELF文件,也有跟可执行文件一样的ELF文件头,因此,动态链接器本身就可以作为可执行文件运行。(如果没有“.interp”就是动态链接器,否则是动态链接可执行文件)

动态链接器本身是静态链接的,且不能依赖别的共享对象。

动态链接器本身可以是PIC的也可以不是,但若是PIC的会简单些。否则,动态链接器的代码段将无法共享,浪费内存。

动态链接器可被当做可执行文件,且其装载地址是0x00000000,这是一个无效装载地址,作为一个共享库,内核在装载它的时候会为其选择合适的装载地址。

6.显示运行时链接

显示运行时链接(Explicit Run-time Linking),也叫运行时加载:让程序自己在运行时控制 加载指定的模块,并可以在不需要该模块时将其卸载。

这种可以由程序控制是否装载的共享对象称为动态装载库(Dynamic Loading Library.DLL,与动态链接库不同)。这种动态库与普通的共享对象相同,区别在于程序开发者的使用方式不同:

1.普通共享对象:由动态链接器在程序启动之前负责装载与链接,这些步骤由动态链接器完成,对程序员而言是透明的。

2.动态库:这种共享对象的装载与卸载都是由程序员在程序中控制的

0 0