Ubuntu Linux---GNU libc库( 三 )


3.5 动态链接器(运行时链接器)
Linux动态链接器(/lib/ld.so.1或/lib64/ld64.so.1)查找和装载应用程序所需的共享库,准备应用程序的运行,然后运行应用程序 。除非编译时为ld指明-static选项,否则Linux二进制程序都是动态链接的 。
【Ubuntu Linux---GNU libc库】在所有现代UNIX操作系统上,都有一些环境变量可以影响动态链接器的运行 。例如AIX上的环境变量LIBPATH可以改变动态链接器的搜索路径 。以下环境变量可以影响到Linux上动态链接器的运行:
- LD_LIBRARY_PATH,以冒号分开的目录列表,运行时会在这些目录中查找需要的库 。
- LD_PRELOAD,以空格分开的库列表,这些库会在其他所有库之前装载 。这常常用来有选择的覆盖某些共享库中的函数 。
- LD_BIND_NOW,如果该环境变量设置成非空字符串,动态链接器会在程序启动时解析所有符号,而不是首次引用时才解析符号(也就是常说的“延迟绑定) 。这在使用调试器时非常有用 。
- LD_TRACE_LOADED_OBJECTS,如果该环境变量设置成非空字符串,程序会列出它所依赖的共享库,就像运行ldd命令一样,而不是正常的执行 。
Linux动态链接器采用广度优先(breadth first)的方式解决库的依赖关系 。也就是说,首先是可执行程序所依赖的库按照动态节(dynamic section)列出的顺序被装载进来,然后是“第一个被依赖的库所依赖的库按照同样的方法装载进来,以此类推,直到所有的依赖关系都被解决 。
在命令行运行下面的命令,会得到更多关于Linux动态链接器的信息:
(代码)(P69第最后一行)
$ info ld.so
上一页;;[1];[2];[3][4];[5];下一页
3.5.1 编程接口
Linux提供了一套API来动态装载库 。下面列出了这些API:
- dlopen,打开一个库,并为使用该库做些准备 。
- dlsym,在打开的库中查找符号的值 。
- dlclose,关闭库 。
- dlerror,返回一个描述最后一次调用dlopen、dlsym,或dlclose的错误信息的字符串 。
C语言用户需要包含头文件dlfcn.h才能使用上述API 。glibc还增加了两个POSIX标准中没有的API:
- dladdr,从函数指针解析符号名称和所在的文件 。
- dlvsym,与dlsym类似,只是多了一个版本字符串参数 。
在Linux上,使用动态链接的应用程序需要和库libdl.so一起链接,也就是使用选项-ldl 。但是,编译时不需要和动态装载的库一起链接 。程序3-1是一个在Linux上使用dl*例程的简单示例 。
(代码)(P70-73)
编译该程序:
(代码)(P73第5行)
$ make
运行程序:
(代码)(P73第15行)
$ ./main
用ldd命令检查可执行程序:
$ ldd ./main
(代码)(P73第19行)
可以看到,可执行程序main没有引用动态装载的库 。
3.5.2 延迟重定位(Lazy Relocation)
延迟重定位/装载是一个允许符号只在需要时才重定位的特性 。这常在各UNIX系统上解析函数调用时用到 。当一个和共享库一起链接的应用程序几乎不会用到该共享库中的函数时,该特性被证明是非常有用的 。这种情况下,只有库中的函数被应用程序调用时,共享库才会被装载,否则不会装载,因此会节约一些系统资源 。但是如果把环境变量LD_BIND_NOW设置成一个非空值,所有的重定位操作都会在程序启动时进行 。也可以在链接器命令行通过使用-z now链接器选项使延迟绑定对某个特定的共享库失效 。需要注意的是,除非重新链接该共享库,否则对该共享库的这种设置会一直有效 。
3.5.3 初始化(initializing)和终止化(finalizing)函数
有时候,以前的代码可能用到了两个特殊的函数:_init和_fini 。_init和_fini函数用在装载和卸载某个模块(注释14)时分别控制该模块的构造器和析构器(或构造函数和析构函数) 。他们的C语言原型如下:

推荐阅读