背景
由于动态链接的诸多优点,大量的程序使用动态链接机制,系统里有着庞大数量的共享对象,为了维护和成绩,操作系统的共享库通过一定的规则来组织和使用共享对象。
共享库兼容
共享库的开发者会不停的更新共享库的版本,各个版本之间存在兼容性的问题。
共享库版本更新时有两种方式
- 兼容更新
所有的更新知识在原有的共享库基础上添加了一些内容,所有原有的接口(ABI)保持不变
- 不兼容更新
共享库更新改变了原有的接口(ABI),使用该共享库原有的接口的程序可能不能正常运行或运行不正常
C 语言不兼容
导致 C 语言的共享库接口(ABI)改变的行为如下:
- 导出函数的行为发生变化,调用函数后产生的结果与以前不一样
- 导出函数被删除
- 导出数据的结构发生变化,如共享库定义的结构体变量的结构发生改变
- 导出函数的接口发生变化,函数返回值、参数被更改
C++ 不兼容
对于 C++ 来说,ABI 的问题更严重,完全不推荐开发一个导出接口为 C++ 的共享库
共享库版本
使用共享库版本可以解决共享库兼容的问题
- 主版本号
不同主版本号的库之间是不兼容的,依赖于旧的版版本号的程序需要改动部分代码,重新编译,才能在新版的共享库中运行,或系统保留旧版的共享库
- 次版本号
库的增量升级,增加新的接口符号,且保持元来的符号不变,高次版本号的库向后兼容性低此版本号的库。
- 发布版本号
表示库的一些错误的修正,性能的改进,不添加任何新的接口,也不对接口进行更改
原理
链接器在系统中搜索共享库时,找到的共享库的次版本号高于或等于依赖的版本,链接器默认共享库满足要求;找到的共享库的次版本号低于所依赖的版本,链接器有两个选择,警告或禁止运行。
实现
每个共享库都有一个对应的 『SO-NAME』 文件,文件中保存的文件名只有主版本号,去掉了次版本号和发布版本号。
系统会在共享库所在的目录创建一个跟 『SO-NAME』 相同的且指向它的软链接。软链接会指向目录中主版本号相同,次版本号和发布版本号最新的共享库。
所有依赖某个共享库的模块,在编译、链接和运行时,都使用共享库的 SO-NAME,而不使用详细的版本号。
兼容性升级时,新的共享库替换掉旧的共享库,修改 SO-NAME 的软链接指向新的共享库
不兼容升级时,系统会保留多个 SO-NAME
次版本号交会问题
如果找到的共享库次版本号低于所需要的版本,链接器有两个选择,警告或禁止运行。警告可能会导致程序运行错误,禁止会阻止那些实际能运行的程序。
符号版本: 为新版本中添加的全局符号打上标记,如『VERS_1.3』,共享库中的每个符号都有相应的标签。
共享库系统路径
FHS(File Hierarchy Standard)
规定了一个系统中的系统文件应该如何存放,各个目录的结构、组织和作用,有利于促进各个开源操作系统之间的兼容性
共享库的路径(前两个)
- /lib
系统最关键和基础的共享库、动态链接库、C 语言运行库、数学库等,和系统启动时需要的库 - /usr/lib
非系统运行时所需要的关键性的共享库,主要是开发时用到的共享库,一般不会被用户的程序直接用到 - /usr/local/lib
跟操作系统本身不十分相关的库,第三方应用程序的库
共享库的创建和安装
创建共享库的过程跟创建一般的共享对象过程基本一致
安装
复制到标准的共享库目录,运行 ldconfig。需 root 权限
使用 ldconfig 建立相应的 SO-NAME 软链接,指定共享库的位置。不需要 root 权限
其他
共享库文件类型
- 动态链接的ELF 共享对象文件
- 符合一定格式的链接脚本文件
脚本将几个已有的共享库组合成新的共享库。
清除符号信息
正常编译的共享库或可执行文件里带有符号和调试信息,对于发布版来说这些信息是多余的,会使文件变大。去除掉符号和调试信息后的文件约有原来一半大小。
strip 工具
可以清楚掉共享库或可执行文件的所有符号和调试信息