动态库文件的装载

动态库状态过程的细节,待整理

动态符号表(.dynsym)是一个符号集,保存动态链接相关的符号,这些符号对于运行时的动态对象是可见的。在动态链接过程中,如果发现未定义的动态符号,链接器会把动态符号加入到动态符号表。但是我们的插件是显示地运行时链接的(为了减少与主程序的耦合),不可能在编译过程中就动态链接到对应的插件库,所以只能主动导出插件中使用的未定义的符号(注册与反注册符号)到动态符号表中,所以在编译主程序时,使用-Wl,-E(-Wl,–export-dynamic)链接器参数将主程序中所有的符号导出到动态符号表中。这样在使用dlopen打开插件动态库时,插件动态库中相关的注册和反注册接口符号在主程序的动态符号表中就有了定义,于是就可以正常运行了。
使用readelf –dyn-syms + 可执行文件或动态库可以查看可执行文件或者动态库中的动态符号表。本例中动态库中动态符号表部分内容如下:
Symbol table ‘.dynsym’ contains 22 entries:
Num: Value Size Type Bind Vis Ndx Name
1: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _Z19unregister_ioengineP1
2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND printf@GLIBC_2.2.5 (2)
11: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND _Z17register_ioengineP11I
13: 0000000000000000 0 FUNC GLOBAL DEFAULT UND pthread_self@GLIBC_2.2.5 (3)
14: 0000000000000000 0 FUNC GLOBAL DEFAULT UND pread64@GLIBC_2.2.5 (3)
15: 0000000000000000 0 FUNC GLOBAL DEFAULT UND pwrite64@GLIBC_2.2.5 (3)
16: 0000000000202080 80 OBJECT GLOBAL DEFAULT 24 ioengine
从中可以看出注册和反注册接口、以及调用glic库中的符号的是未定义(UND)状态,需要在加载时确定这些符号的位置,glic库相关的符号在加载glic库时完成确定,注册与反注册则需要在主程序中确定位置,所以主程序的动态符号表必须有这两个符号的定义。
主程序在未加-Wl,-E参数编译时,动态符号表内容如下:
Symbol table ‘.dynsym’ contains 87 entries:
Num: Value Size Type Bind Vis Ndx Name
0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND
1: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZNSsaSEPKc@GLIBCXX_3.4 (2)
2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZNSsC1Ev@GLIBCXX_3.4 (2)
3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND printf@GLIBC_2.2.5 (3)
4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND _ZSt21__throw_runtime_err@GLIBCXX_3.4 (2)
5: 0000000000000000 0 FUNC GLOBAL DEFAULT UND snprintf@GLIBC_2.2.5 (3)
其中只包含一些调用其它动态库的动态符号内容,像glibc库这些第三方库中的符号。

主程序在添加-Wl,-E参数编译时,动态符号表内容过多,添加grep过滤(readelf –dyn-syms main_prj | grep register)可得:
240: 0000000000410dd5 218 FUNC GLOBAL DEFAULT 14 _Z17register_ioengineP11I
596: 0000000000410eaf 211 FUNC GLOBAL DEFAULT 14 _Z19unregister_ioengineP1
可以看出在使用了-Wl,-E参数后,动态符号表中多出了一些主程序中内部函数的符号,包含插件注册与反注册接口的符号(符号修饰名与插件中的修饰名一致)。

参考:
.dynsym:动态符号表:保存动态链接相关的导入导出的符号,不包括模块内部符号
.dynsym默认会导出使用其它动态链接库的符号
主程序在编译时默认只会导出未定义的符号(将来从动态库中加载)和动态库中定义了并且被使用的符号(定义了函数定义,并有实现和调用)。不会导出有定义但是没有使用的符号。
所以当插件需要向主程序注册时,主程序中需要有注册函数符号,才能被插件调用。
gcc编译使用-Wl,-E参数可以导出所有符号。
主程序加载动态库时,动态符号表会进行合并
合并到GOT表
显式运行时链接
查看动态符号表:readelf –dyn-syms plugin_test
待补充的内容:动态表是怎么链接到一起的,动态库加载的时候做了什么?
动态链接过程中如果发现未定义的动态符号,链接器会把动态符号加入动态符号表(所以第一个例子中,程序输出正常),但是fun符号因为是在dlopen中(dlopen发生在运行过程中)调用的,不会加入到动态符号表,所以导致找不到符号。
-E’–export-dynamic’
当创建一个动态连接的可执行程序时, 把所有的符号加到动态符号表中.动态符号表是一个符号集,这些符号对于运行时的动态对象是可见的.
如果你不使用这个选项,动态符号表中就会只含有那些连接进来的动态对象中用到的符号

如果你使用’dlopen’来载入动态对象,它需要引用程序中的符号,那你可能需要在连接程序时用到这个选项.你也可以使用版本脚本来控制哪些符号应当被加到动态符号表中.

文章目录