免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 12656 | 回复: 4
打印 上一主题 下一主题

[Linux] linux动态库版本兼容小结 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2014-03-07 22:02 |只看该作者 |倒序浏览
本帖最后由 raindy1234 于 2014-03-07 22:05 编辑

前言
针对同一动态组件的不同版本链接和加载。

一、概念        
         DLL HELL字面意思是DLL"灾难",是由于com组件(动态库)升级引起的程序不能运行的情况。
        原因
         有三种可能的原因导致了DLL Hell的发生:
                一是由使用旧版本的DLL替代原来一个新版本的DLL而引起的。这个原因最普遍,是Windows 9X用户通常遇到的DLL错误之一。
                二是由新版DLL中的函数无意发生改变而引起。尽管在设计DLL时候应该向下兼容,然而要保证DLL完全向下兼容却是不能的。
                三是由新版DLL的安装引入一个新的Bug。

二、linux下的解决方案——命名规范      
       Linux 上的Dll ,叫sharedlibrary。Linux 系统面临和Window一样的问题,如何控制动态库的多个版本问题。为解决这个问题,Linux 为解决这个问题,引入了一套命名机制,如果遵守这个机制来做,就可以避免这个问题。但是这只事一个约定,不是强制的。但是建议遵守这个约定,否则同样也会出现 Linux 版的Dll hell 问题。

Real Name        
          首先是共享库本身的文件名:共享库的命名必须如 libname.so.x.y.z最前面使用前缀”lib”,中间是库的名字和后缀”.so”,最后三个数字是版本号。x是主版本号(Major Version Number),y是次版本号(Minor Version Number),z是发布版本号(Release Version Number)。

主版本号(不兼容):重大升级,不同主版本的库之间的库是不兼容的。所以如果要保证向后兼容就不能删除旧的动态库的版本。

次版本号(向下兼容): 增量升级,增加一些新的接口但保留原有接口。高次版本号的库向后兼容低次版本号的库。

发布版本号(相互兼容):库的一些诸如错误修改、性能改进等,不添加新接口,也不更改接口。主版本号和次版本号相同的前提下,不同发布版本之间完全兼容。

SO-NAME      
         严格遵守上述规定,确实能避免动态库因为版本冲突的问题,但是读者可能有疑问:在程序加载或运行的时候,动态链接器是如何知道程序依赖哪些库,如何选择库的不同版本?
Solaris和Linux等采用SO-NAME( Shortfor shared object name )的命名机制来记录共享库的依赖关系。每个共享库都有一个对应的“SO-NAME”(共享库文件名去掉次版本号和发布版本号)。比如一个共享库名为libtest.so.3.8.2,那么它的SO-NAME就是libtest.so.3。

在Linux系统中,系统会为每个共享库所在的目录创建一个跟SO-NAME相同的并且指向它的软连接(Symbol Link)。这个软连接会指向目录中主版本号相同、次版本号和发布版本号最新的共享库。也就是说,比如目录中有两个共享库版本分别为:/lib/libtest.so.3.8.2和/lib/libtest.so.3.7.5,么软连接/lib/libtest.so.3指向/lib/libtest.so.3.8.2。

建立以SO-NAME为名字的软连接的目的是,使得所有依赖某个共享库的模块,在编译、链接和运行时,都使用共享库的SO-NAME,而不需要使用详细版本号。在编译生产ELF文件时候,如果文件A依赖于文件B,那么A的链接文件中的”.dynamic”段中会有DT_NEED类型的字段,字段的值就是B的SO-NAME。这样当动态链接器进行共享库依赖文件查找时,就会依据系统中各种共享库目录中的SO-NAME软连接自动定向到最新兼容版本的共享库。

★  readelf -d sharelibrary 可以查看so-name
★  Linux提供了一个工具——ldconfig,当系统中安装或更新一个共享库时,需要运行这个工具,它会遍历默认所有共享库目录,比如/lib,/usr/lib等,然后更新所有的软链接,使她们指向最新共享库。

Link Name
       当我们在编译器里使用共享库的时候,如用GCC的“-l”参数链接共享库libtXXX.so.3.8.1,只需要在编译器命令行指定 -l XXX 即可,省略了前缀和版本信息。编译器会根据当前环境,在系统中的相关路径(往往由-L参数指定)查找最新版本的XXX库。这个XXX就是共享库的“链接名”。不同类型的库可能有相同的链接名,比如C语言运行库有静态版本(libc.a)也动态版本(libc.so.x.y.z)的区别,如果在链接时使用参数”-lc”,那么连接器就会根据输出文件的情况(动态/静态)来选择合适版本的库。eg. ld使用“-static”参数时吗,”-lc”会查找libc.a;如果使用“-Bdynamic”(默认),会查找最新版本的libc.so.x.y.z。

更详细可以参见
http://www.linuxidc.com/Linux/2012-04/59071.htm

三、指定流程       
         1、真实名:通过源码编译得到。
        2、soname:通过readelf –d 真实名 可得 Library soname。(包含在库头中)
        3、linkname: 跟据真实名 做一个对象为 soname的软连接符号。

编译连接过程:
可执行程序app通过 –lname –L./lib/ 指定动态库的真实文件(库头中包含soname)。
可执行文件的库相关部分会记录动态库的soname,而不是真实名。
运行app:
运行时,app会通过ld.so.conf 和 库的宏来顺序查找该库的soname。只到匹配。
名为soname的文件,可以使符号链接,也可以是真实库。一般为符号链接。
Ld.so.conf和库的宏(LD_LIBRARY_PATH)都可以手动修改。
查看库指定:
多个版本的动态库存在时,ldd出来的库链接未必是正确的。
readelf –d app 出来的是正确的,因为库的查找会根据app里记录的库的名字。
ldconfig –v 可以顺序查看动态库的指定。有更新库缓存的作用。
开源的一般有版本兼容考虑(有nameso)。
自己开发的库一般没有nameso,直接连就可以。
Ldconfig –p,输出所有soname对应的动态库的列表。
Ldd只显示默认的库搜索路径下的东西.
库的引用一般从.so(不带版本号的)开始.
Soname为so.N的做libname.so.N链接即可,soname为真实名的做libname.so链接即可。

评分

参与人数 1可用积分 +2 收起 理由
crazyhadoop + 2 很给力!

查看全部评分

论坛徽章:
11
技术图书徽章
日期:2014-03-01 14:44:34天蝎座
日期:2014-05-21 22:11:59金牛座
日期:2014-05-30 17:06:14
2 [报告]
发表于 2014-03-08 16:11 |只看该作者
支持总结分享)

论坛徽章:
0
3 [报告]
发表于 2014-03-11 00:51 |只看该作者
很不错,学习学习!

论坛徽章:
0
4 [报告]
发表于 2018-06-25 15:02 |只看该作者
总结的不错,学习一下
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP