Elf32_Dyn 结构由一个类型值加上一个附加的数值或指针,对于不同类型,后面附加的数值或者指针有着不同含义。我们这里列举几个比较常见的类型值(这些值都是定义在“elf.h”里面的宏),如表7-2所示:
表7-2中只列出了一部分定义,还有一些不太常用的定义我们就暂且忽略,具体可以参考LSB手册和elf.h的定义。从上面给出的定义来看,“.dynamic”段里面保存的信息有点像elf文件头,只是我们看到的elf文件头中保存的是静态链接时的相关信息,比如静态链接时使用到的符号表、重定位表等,这里换成了动态链接下所使用的相应信息了。所以,“.dynamic”段可以看成动态链接下的ELF文件的“文件头”。使用readelf工具可以查看 ".dynamic" 段的内容
$ readelf -d lib.so另外Linux还提供了一个命令用来查看一个程序主模块或一个共享库依赖于哪些共享库:
动态符号表为了完成动态链接,最关键的还是所依赖的符号和相关文件的信息。我们知道在静态链接中,有一个专门的段叫做符号表“.symtab”( Symbol Table),里面保存了所有关于该目标文件的符号的定义和引用。动态链接的符号表示实际上它跟静态链接十分相似,比如前面例子中的 Program1程序依赖于 Lib.so,引用到了里面的 foobar()函数。那么对于 Program1来说,我们往往称 ProgramI导入( Import)了 foobar函数, foobar是 Program1的导入函数(import Function):而站在Lib.so的角度来看,它实际上定义了 foobar函数,并且提供给其他模块使用,我们往往称Lib.so导出( Export)了 foobar函数, foobar是Lbso的导出函数(Export Function)。把这种导入导出关系放到静态链接的情形下,我们可以把它们看作普通的函数定义和引用。
为了表示动态链接这些模块之间的符号导入导出关系,ELF专门有一个叫做动态符号表(Dynamic symbol table)的段用来保存这些信息,这个段的段名通常叫做“.dynsym”(Dynamic Symbol)。与 “.sysmtab”不同的是,".dynsym" 只保存了与动态链接相关的符号,对于那些模块内部的符号,比如模块私有变量则不保存。很多时候动态链接的模块同时拥有“.dynsym” 和 ".sysmtab"中往往保存了所有符号,包括 “.dynsym” 中的符号
与 “.sysmtab” 类似, 动态符号表也需要一些辅助的表,比如用于保存符号名的字符串表。静态链接的时候叫做符号字符串“.strtab” (Striing Table),在这里就是动态符号字符串表“.dynstr” (Dynamic String Table);由于动态链接下,我们需要在程序运行时查找符号,为了加快符号的查找过程,往往还有辅助的符号哈希表(“.hash”)。我们可以用readelf工具来查看elf文件的动态符号表及哈希表:
readelf -sD lib.so动态链接符号表的结构与静态链接的符号表几乎一样,我们可以简单的将导入韩式看作是对其他目标文件中函数的引用:把导出函数看作是在本目标文件定义的函数就可以了;
3. 动态链接重定位表共享对象需要重定位的主要原因是导入符号的存在。在动态链接下,无论是可执行文件或共享对象,一旦它依赖于其他共享对象,也就是说有导入的符号时,那么它的代码或数据中就会有对于导入符号的引用。在编译时这些导入符号的地址未知,在静态链接中,这些未知的地址引用在最终链接时被修正。但是在动态链接中,导入符号的地址在运行时才确定,所以需要在运行时将这些导入符号的引用修正,即需要重定位;
我们在前面地址无关章节中也提到过,动态链接的可执行文件使用的是PIC方法,但这不能改变它需要重定位的本质。对于动态链接来说,如果一个共享对象不是以PIC模式编译的,那么毫无疑问,它是需要在装载时被重定位的:如果一个共享对象是PIC模式编译的,那么它还需要再装载时进行重定位吗? 是的,PIC的共享对象也是需要重定位的;