Linux动态链接之七:共享库的创建和安装

创建共享库和一般的共享对象.so基本一致,主要还是GCC的两个参数-shared –fPIC(意义不用多说,分别指明共享和PIC地址无关)

在前面说过gcc指令其实对于与cl\ld等具体程序的调用封装,故而完全可以在gcc指令传输一系列参数(比如传输一些参数给汇编器cl,一些参数给ld),-Wl便是在gcc指令中声明传给链接器ld的参数:
$gcc -shared -Wl, -soname, my_soname -o library_name source_files library_files
其中:
-Wl, -soname, my_soname指定输出共享库的SO-NAME,如果不为共享库指定SO-NAME,那么Idcofig将无法为该共享库生成软连接。举例eg: $gcc -shared –fPIC –Wl, -soname, libfoo.so.1 –o libfoo.so.1.0.0 libfoo1.c libfoo2.c –lbar1 -lbar2
(注:-lbar1参数是-lib libbar1.so的缩写,链接名机制)

-Wl, -rpath, libpath则是指定当前链接产生的目标程序的共享库的优先搜索路径和LD_Library_path同样的功能。

注意事项:
1.不要把共享库中的符号和调试信息去掉,也不要使用GCC的-fomit-frame-pointer选项,这样做虽不会让共享库停止运行,但是会影响调试共享库,给后面的工作带来影响;

2.默认情况下,链接器在产生可执行文件时,只会将那些链接时被其他共享模块引用到的符号放到动态符号表,有利于减少.dynsym大小。但有一种情况,程序在运行时加载dlopen()动态加载某个共享模块,而该共享模块需要反向引用主模块的符号,但是有可能这些符号此前在动态链接时没有被其他共享模块引用,从而没有放到动态符号表中。这将导致这一运行时加载进来的共享库反向引用失败。ld链接器提供一个-export-dynamic参数,这个参数表示链接器在生成可执行文件时,将所有全局符号表的所有符号内容导出到.dynsym,以防止出现反向引用失败,我们可以在GCC中使用-Wl, -export-dynamic将该参数传给链接器。

清除符号信息
正常编译器出来的共享库或可执行文件中含所有大量的调试信息,这些信息在release版本中会导致程序过于臃肿,所以通过binutils套件的strip工具一次性去除这些无用的符号信息,如$strip libfoo.so。当然还可以直接在链接器链接指令中加入-s -S指令,-S消除调试符号信息, -s消除所有符号信息,可以在gcc中通过-Wl, -s-Wl, -S给ld传递这两个参数。

共享库的安装
.最简单的:将共享库直接拖到/lib或/usr/lib中,然后运行Idconfig。(需要有root权限)
2.手动为共享库建立SO-NAME软连接,需要声明共享库所在的目录
$Idconfig -n shared_library_directory
但是在编译程序时,也是需要制定共享库的位置,GCC提供两个参数“-L”和“-l”分别用于指定共享库的而搜索目录和共享库的链接名,-Wl, -rpath和LD_LIBRARY_PATH也是可以达到同样的效果。

共享库的路径问题
Linux遵守着FHS(File Hierarchy Standard)标准,该标准规定了共享库的存放路径,主要有:
1. /lib: 存放着系统最关键和基础的共享库,比如动态链接器、C语言运行库、数学库等;
2. /usr/lib:保存着非系统运行所需的关键共享库,主要是一些开发时用到的共享库;
3. /usr/local/lib:保存着一些跟操作系统本身并不十分相关的库,主要是一些第三方库,比如存放着python解释器。

如果动态链接器每次在查找共享库时都要去遍历上述目录,所以Linux系统中的Idconfig程序在每次更新共享库的SO-NAME时,都会顺带将这些共享库SO-NAME的路径和库名对应关系存放在/etc/ld.so.cache,以建立一个SO-NAME的缓存机制,该cache是经过专门设计的,查找速度非常棒(比如红黑树?)。

如果在/etc/ld.so.cache中没有找到所需的共享库,那么系统才会去遍历/lib和/usr/lib这两个目录。现在很多软件包的安装程序都会在安装完所需的共享库之后主动调用Idconfig,更新SO-NAME和/etc/ld.so.cache内部缓存。

LD_LIBRARY_PATH等环境变量
Linux提供了数种改变动态链接器查找共享库路径的方法,通过使用手动指定共享库路径,可以实现共享库的调试和测试、应用程序级别的虚拟控制。其中LD_LIBRARY_PATH是最为简单的一种方法,使用LD_LIBRARY_PATH可以临时改变某个应用程序的共享库查找路径,而不会影响系统中的其他程序。

LD_LIBRARY_PATH环境变量指定的路径将成为ld.so动态链接器的首选查找路径,其次是路径缓存文件/etc/ld.so.cache文件,然后才是/usr/lib,最后是/lib。LD_LIBRARY_PATH变量指定目录的效果和-L参数效果一致。如在ld链接指令中添加-L/usr/lib -L/usr/X11R6/lib -lgtk -lgdk -rdynamic
此外要注意的,不要讲-L-I混淆,前者是指定库文件.so的查找路径,后者是这点头文件.h的查找路径。

LD_PRELOAD:该环境变量用来指定预装载文件(共享库甚至目标文件),既然是预装载,那么显然是发生在动态链接器按照LD_LIBRARY_PATH装载当前主程序依赖的共享库之前。看起来这种预装载没什么用,其实由于全局符号介入机制,LD_PRELOAD指定的共享库的符号将会覆盖后续装载进来的符号,这使得我们可以很方便地改写标准C语言库中的某些函数,而不影响其他程序的正常调用C标准库。当然LD_PRELOAD和LD_LIBRARY_PATH一样,正常release版本程序不应该使用。

LD_DEBUG:可以打开ld.so的调试功能,当设置该变量时,动态链接器可以在运行时打印出各种有用的信息,对我们开发和调试共享库有很大的帮助。比如$LD_DEBUG = files ./HelloWordl.out将会在shell中显示出整个装载过程的步骤和顺序。除了files,还有其他几种赋值

属性值 含义
bindings 显示动态链接的符号绑定过程
libs 显示动态链接共享库的查找过程
versions 显示符号的版本依赖关系
reloc 显示重定位过程
symbols 显示符号表的查找补全过程
statistics 显示动态链接过程中的各种统计信息
all 显示以上所有信息
help 显示各种可选值得帮助信息

你可能感兴趣的:(Linux内核)