Linux共享库概述( 五 )


如果你没有权限去做这件事情 , 例如你不能修改/usr/lib目录 , 那么你就只好通过修改你的环境变量来实现这些函数库的使用了 。 首先 , 你需要创建这些共享函数库;然后 , 设置一些必须得符号链接 , 特别是从soname到真正的函数库文件的符号链接 , 简单的方法就是运行ldconfig:
ldconfig -n directory_with_shared_libraries 然后你就可以设置你的LD_LIBRARY_PATH这个环境变量 , 它是一个以逗号分隔的路径的集合 , 这个可以用来指明共享函数库的搜索路径 。 例如 , 使用bash , 就可以这样来启动一个程序my_program:
LD_LIBRARY_PATH=$LD_LIBRARY_PATH my_program
如果你需要的是重载部分函数 , 则你就需要创建一个包含需要重载的函数的object文件 , 然后设置LD_PRELOAD环境变量 。
通常你可以很方便的升级你的函数库 , 如果某个API改变了 , 创建库的程序会改变soname 。 然而 , 如果一个函数升级了某个函数库而保持了原来的soname , 你可以强行将老版本的函数库拷贝到某个位置 , 然后重新命名这个文件(例如使用原来的名字 , 然后后面加.orig后缀) , 然后创建一个小的“wrapper”脚本来设置这个库函数和相关的东西 。 例如下面的例子:
#!/bin/sh export LD_LIBRARY_PATH=/usr/local/my_lib,$LD_LIBRARY_PATH
exec /usr/bin/my_program.orig $*
我们可以通过运行ldd来看某个程序使用的共享函数库 。 例如你可以看ls这个实用工具使用的函数库:
【Linux共享库概述】 ldd /bin/ls
libtermcap.so.2 => /lib/libtermcap.so.2 (0x4001c000)
libc.so.6 => /lib/libc.so.6 (0x40020000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000) 通常我么可以看到一个soname的列表 , 包括路径 。 在所有的情况下 , 你都至少可以看到两个库:
· /lib/ld-linux.so.N(N是1或者更大 , 一般至少2) 。 这是这个用于加载其他所有的共享库的库 。
· libc.so.N(N应该大于或者等于6) 。 这是C语言函数库 。
值得一提的是 , 不要在对你不信任的程序运行ldd命令 。 在ldd的manual里面写得很清楚 , ldd是通过设置某些特殊的环境变量(例如 , 对于ELF对象 , 设置LD_TRACE_LOADED_OBJECTS) , 然后运行这个程序 。 这样就有可能使得某地程序可能使得ldd来执行某些意想不到的代码 , 而产生不安全的隐患 。
3.6. 不兼容的函数库
如果一个新版的函数库要和老版本的二进制的库不兼容 , 则soname需要改变 。 对于C语言 , 一共有4个基本的理由使得它们在二进制代码上很难兼容:
一个函数的行文改变了 , 这样它就可能与最开始的定义不相符合 。
· 输出的数据项改变了 。
· 某些输出的函数删除了 。
· 某些输出函数的接口改变了 。如果你能避免这些地方 , 你就可以保持你的函数库在二进制代码上的兼容 , 或者说 , 你可以使得你的程序的应用二进制接口(ABI:Application Binary Interface)上兼容 。
4. 动态加载的函数库Dynamically Loaded (DL) Libraries
动态加载的函数库Dynamically loaded (DL) libraries是一类函数库 , 它可以在程序运行过程中的任何时间加载 。 它们特别适合在函数中加载一些模块和plugin扩展模块的场合 , 因为它可以在当程序需要某个plugin模块时才动态的加载 。 例如 , Pluggable Authentication Modules(PAM)系统就是用动态加载函数库来使得管理员可以配置和重新配置身份验证信息 。
Linux系统下 , DL函数库与其他函数库在格式上没有特殊的区别 , 我们前面提到过 , 它们创建的时候是标准的object格式 。 主要的区别就是这些函数库不是在程序链接的时候或者启动的时候加载 , 而是通过一个API来打开一个函数库 , 寻找符号表 , 处理错误和关闭函数库 。 通常C语言环境下 , 需要包含这个头文件 。Linux中使用的函数和Solaris中一样 , 都是dlpoen() API 。 当然不是所有的平台都使用同样的接口 , 例如HP-UX使用shl_load()机制 , 而Windows平台用另外的其他的调用接口 。 如果你的目的是使得你的代码有很强的移植性 , 你应该使用一些wrapping函数库 , 这样的wrapping函数库隐藏不同的平台的接口区别 。 一种方法是使用glibc函数库中的对动态加载模块的支持 , 它使用一些潜在的动态加载函数库界面使得它们可以夸平台使用 。 具体可以参考 另外一个方法是使用libltdl , 是GNU libtool的一部分 , 可以进一步参考CORBA相关资料 。