第八章 Linux
共享库的组织
1
共享库版本
共享库兼容性
导致
C
语言共享库
ABI
改变的行为有
4
:
(1)
导出函数行为改变
(2)
导出函数被删除
(3)
导出数据的结构发生改变
(4)
导出函数的接口发生改变
不同版本的编译器、操作系统和硬件平台都
ABI
兼容都有影响。
共享库版本命名
使用共享库版本是有效保证共享库兼容的方法之一。
Linux
的命名规则:
libname.so.x.y.z
x
主版本号:不同主版本号的库之间不兼容
y
次版本号:增加一些新的借口,原来的符号不变
z
发布版本号:库的一些错误的修正、性能的改进
C
库由于历史原因,命名为
libc-x.y.z.so
,动态链接器
ld-x.y.z.so
SO-NAME
在
linux
中,每个共享库都有一个对应的
SO-NAME
,这个
SO-NAME
即共享库的文件名去掉次版本号好发布版本号,保留主版本号。
libname.so.x.y.z
à
libname.so.x
C
库的
SO-NAME
跟标准一样
libc-x.y.z.so
à
libc.so.x
动态链接器
ld-x.y.z.so
à
ld-linux.so.x
实际上
SO-NAME
是哥软链接,执行目录中主版本号相同,次版本号和发布版本号最新的共享库。
连接名:
gcc
中可以直接使用
-lXXX
,在相应的目录中搜索
libXXX.so.x.y.z
库的最新版本。
2
符号版本
使用共享库版本不能解决次版本号交会问题。于是引入了符号的版本机制。
当我们在构建(编译或者链接)应用程序时,链接器可以在程序的最终输出文件中记录下它所用到的版本号集合,注意:程序记录的不是构建时共享库中版本最新的符号集合,而是程序所依赖的集合中版本最小的那个。
在程序运行时,动态链接器会通过程序内记录的的它所依赖的所有共享库的符号集合版本信息,然后判定当前系统共享库符号版本是否满足这些被依赖的符号集合。
Linux
中符号版本的实现
在
linux
下,可以如下将符号版本的信息传递到库中。
gcc -shared -fPIC lib.c -Xlinker --version-script lib.ver -o lib.so
lib.ver
是一个符号脚本文件
VERS_1.2{
global:
foo;
local:
*;
};
即
foo
的符号版本为
1.2
当引用这个库生成可执行文件时:
gcc main.c ./lib.so -o main
于是
main
中所引用的
foo
为
VER_1.2
。这个这个
main
程序拿到一台只包含地狱
VER_1.2
的
foo
的
lib.so
系统中运行,则会报错。
3
共享库系统路径
目前大多数包括
linux
在内的开源操作系统都遵守
FHS(File Hierarchy Standard)
标准。标准规定,一个系统中主要有
3
个存放共享库的位置:
(1)
/lib
,存放系统关键和基础的共享库,如动态链接、
C
语言运行库和数学库。
(2)
/usr/lib
,非系统运行时所需要的关键库,如一些开发时用到的库
(3)
/usr/local/lib
,操作系统本身并不十分相关的库,主要是一些第三方应用程序库。
4
共享库查找
动态链接器会按照如下顺序依次装载或者查找共享库:
(1)
由环境变量
LD_LIBRARY_PATH
指定的路径
(2)
由路径缓存文件
/etc/ld.so.cache
指定的路径
(3)
默认共享目录,先
/usr/lib,
再
/lib
ld.so.cache
由
/etc/ld.so.conf
生成,
ldconfig
5
环境变量
LD_LIBRARY_PATH
LD_PRELOAD
LD_DEBUG
可以打开动态链接器的调试功能
6
共享库的创建与安装
共享库的创建
gcc -shared -fPIC -Wl, -soname, my_soname -o library_name source_file library_files
-Wl
将信息传递给链接器,这里表示建立
SO-NAME
创建共享库注意事项:
(1)
不要输出共享库总的符号和调试信息去掉,也不要使用
GCC
的
-fomit-frame-pointer
,这会影响共享库的调试。
(2)
测试新的共享库,可以使用
LD_LIBRARY_PATH
或者
-Wl,-rpath
,没必要删掉原有的系统库。
(3)
如果库有对可执行文件的反向引用,则可执行文件用
-Wl,-export-dynamic
,这样原动态符号表不会被删减,
dlopen()
不会找不到所需的符号。
清除符号信息
$strip libfoo.so
gcc
中使用
-Wl,-S
或者
-Wl,-s
-S
消除调试符号信息,
-s
消除所有符号信息
共享库的安装
直接拷贝到
/lib
或者
/usr/lib
,运行
ldconfig
或者运行
$ldconfig -n shared_library_directory
共享库的构建与析构
GCC
提供了一种共享库的构造函数和析构函数,只要在函数声明时加上如下的属性:
__attribute__((constructor))
__attribute__((destructor))
共享库脚本
共享库可以是动态链接的
ELF
共享对象文件(
.so
),也可以是符合一定规则的链接脚本。与
LD
链接脚本非常相似。
GROUP( /lib/libc.so.6 /lib/libm.so.2)