Linux 动态库版本管理

说明

  • 由于动态库支持动态链接和动态装载,使用中具有很强的动态性,在大型项目中如果缺乏约束,很容易出现版本问题,导致程序运行异常,例如:
  1. 不小心使用旧版本的动态库文件替代了新版本的动态库。
  2. 新版本动态库中的接口发生变化而引起,尽管在设计动态库时应该向下兼容,然而要保证动态库完全向下兼容却是不可能的。
  3. 新版动态库引入了一些新的Bug。

解决方法

  • 解决版本问题的首要工作就是标识每一个版本,这就是版本号的作用,但是仅仅使用版本号并不能解决问题,甚至破坏了动态库的动态性,因为版本号不同,不同版本的动态库具有不同的库名,使用包含版本号的库名进行编译链接,这样无法更新动态库,完全丢弃了动态库的动态性。

linux解决方案

Real Name - 命名规范

  • Linux 采用版本号来标识每一个版本,并且对版本号的命名做了些规范,共享库文件的命名必须如"libname.so.x.y.z",最前面使用前缀"lib",中间是库的名字和后缀”.so”,最后三个数字是版本号, x是主版本号(Major Version Number),y是次版本号(Minor Version Number),z是发布版本号(Release Version Number), 并且它们具有以下要求。
  1. 主版本号(不兼容):重大升级,不同主版本的库之间的库是不兼容的,所以如果要保证向后兼容就不能删除旧的动态库的版本。
  2. 次版本号(向下兼容): 增量升级,增加一些新的接口但保留原有接口,高次版本号的库向后兼容低次版本号的库。
  3. 发布版本号(相互兼容):库的一些诸如错误修改、性能改进等,不添加新接口,也不更改接口,主版本号和次版本号相同的前提下,不同发布版本之间完全兼容。

SO-NAME

  • 为了解决直接链接特定版本号的动态库导致破坏动态性的问题,linux引入了SO-NAME(Shortfor shared object name)机制。
  • 生成动态库时,如果设置了SO-NAME,系统会在其所在的目录创建一个以SO-NAME命名并且指向它的软连接(Symbol Link),如果两个版本可以兼容它们的SO-NAME需要设置成一样的,不能兼容则设置成不同的值,例如:有两个版本的动态库分别为libtest.so.3.8.2和libtest.so.3.7.5,如果它们设置的SO-NAME都为libtest.so.3,编译app时链接的库名是libtest.so.3,这样就能兼容使用,如果它们设置的SO-NAME一个为libtest.so.3.8,另一个设置的libtest.so.3.7,编译app时链接的库名也是不同的(老版本是libtest.so.3.7,新版本是libtest.so.3.8),因此也不会导致异常。
  • 简单来说该机制就是:SO-NAME是一个链接过程的中间介质,应用程序先链接到该软链接,再链接到真实的动态库,如果两个版本可以兼容则只需要将原有软链接指向最新的库即可,如果两个版本不能兼容,使用新版本动态库时,会链接到新的的SO-NAME软链接,也不会导致异常。
  1. 设置方式
gcc -g -Wall -fPIC -c hello.c -o hello.o
gcc -shared -W,soname,-libhello.so.0 -o libhello.so.0.0.0 hello.o
  1. 查看方式
  • 如果编译动态库时设置了其SO-NAME,会在其ELF文件结构中的SONAME段保存其值,可通过以下命令查看:
readelf -d libhello.so.0.0.0 
....
ox00000000000e(SONAME)    library soname: [libhello.so.0]
....

Link name - APP链接名

不使用 SO-NAME 机制
  • 一般情况下,使用动态库,为了保证动态性,我们不会直接链接到真实的动态库,而是创建一个不带版本号的软链接指向真正的动态库,链接到该软链接,如下:
ln -s libhello.so.0.0.0 libhello.so
gcc -o main main.o -lhello 
  • 如上操作,如果动态库版本更新(不同版本号),用户会发现无法动态更新到新版本动态库,APP运行时链接的依然是老版本号动态库,除非重新编译app,原因是APP的ELF文件结构中保存的NEEDED信息如下:
readelf -d main | grep libhello
....
ox000000000001(NEEDED)    shared library: [libhello.so.0.0.0]
....
# 保存了真正的动态库名,所以无法链接到新版本的动态库,只能重新编译。
  • 链接过程:
  1. 链接器通过编译命令-L. -lhello在当前文件夹查找libhello.so文件
  2. 读取libhello.so链接指向的实际文件。这里是libhello.so.0.0.0
  3. 将libhello.so.0.0.0记录到main程序的ELF文件结构中
使用 SO-NAME 机制
  • 如果libhello.so.0.0.0 有设置SO-NAME(libhello.so.0),我们需要创建软链接链接到该SO-NAME,如下:
ln -s libhello.so.0 libhello.so
gcc -o main main.o -lhello 
* 创建软链接链接到真实的动态库文件也是可以的
  • 这样操作,APP的ELF文件结构中保存的NEEDED信息如下:
readelf -d main | grep libhello
....
ox000000000001(NEEDED)    shared library: [libhello.so.0]
....
* APP链接到了SO-NAME软链接,这样动态库版本更新了,只需要SO-NAME一致就能链接到新版本动态库
  • 链接过程:
  1. 链接器通过编译命令-L. -lhello在当前文件夹查找libhello.so文件
  2. 读取libhello.so链接指向的实际文件。这里是libhello.so.0.0.0
  3. 读取libhello.so.0.0.0中的SONAME,这里是libhello.so.0
  4. 将libhello.so.0记录到main程序的ELF文件结构中
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值