以菱形链接(diamond link)为例,探讨Linux下连接器和加载器对Shared libarary兼容性的处理

相关代码在https://github.com/lzueclipse/learning/tree/master/c_cpp/diamond_link

1. 什么是菱形链接(diamond link)

菱形链接(diamond link)(参考文献 1)能十分清楚的描述出我们要讨论的问题。


如上图所示,我们的程序将要使用某厂家的共享库libvendor1.so,同时也要使用另外一个厂家的共享库libvendor2.so。

libvendor1.so和libvendor2.so都将使用某知名开源共享库libopensource.so.xxx(xxx表示版本)。

但是这两个厂家提供给我们的都是自己编译维护的libopensource.so.xxx。

我们遇到的问题是:

一个厂家使用了另外一个厂家提供libopensource.so.xxx,而不是自己提供的,出现兼容性问题。

这个问题扩展展开来:

1)如果libopensource.so.xxx的版本不相同,符号绑定(binding)的是哪个版本的?

2)如果libopensource.so.xxx的版本相同,符号绑定的是哪个版本的?

3)对于问题1)和2),采用系统默认加载和使用"dlopen"等API显式加载,又有什么不同?

如果这几个问题您没有答案或者觉得比较含糊,建议您跟随我的实验,我们一起探讨下。

因为我个人没看过连接器和加载器的源码,所以我们的探讨集中在我们看到的证据上,并试图给出一些粗浅的结论。

2.相关代码

具体代码在github中。 我们先弄清楚c文件的调用关系,以及编译脚本做了什么样的工作。

调用依赖: main.c<----vendor[1|2].c<--------opensource_v[1|2].c(函数opensource_print的不同实现)。

C源代码:

1)其中vendor1.c会被编译生成libvendor1.so,vendor2.c会被编译生成libvendor2.so;

opensource_v1.c会被编译生成./opensource_v1/libopensource.so.xxx(xxx值表示版本信息,后续实验会给定真实值),opensource_v2.c会被编译成./opensource_v2/libopensource.so.xxx;

libvendor1.so会依赖./opensource_v1/libopensource.so.xxx, libvendor2.so会依赖./opensource_v2/libopensource.so.xxx,

2)main.c链接libvendor1.so,libvendor2.so生成可执行文件

3)main.c有两种用法,一种"general"使用系统默认的加载共享库的方法,一种"dlopen"使用dlopen等API显式加载需要的共享库。

main.c

vendor1.c

vendor2.c

opensource_v1.c

opensource_v2.c

用于控制编译的Shell脚本(每个使用一个脚本,为了便于说清):

different_soname_without_default_symver.sh

different_soname_with_default_symver.sh

same_soname_without_default_symver.sh

same_soname_with_default_symver.sh

3. libopensource.so.xxx的版本不相同,系统如何查找依赖库和绑定符号

在这个实验里我们编译opensource_v1.c生成./opensource_v1/libopensource.so.1.0;编译opensource_v2.c生成./opensource_v2/libopensource.so.2.0。

libvendor1.so将依赖./opensource_v1/libopensource.so.1.0; libvendor2.so将依赖./opensource_v2/libopensource.so.2.0。

3.1符号表不带版本信息的

gcc编译的符号,默认是不带版本信息的。

3.1.1我们用different_soname_without_default_symver.sh 来编译


[root@node1 0004]# sh different_soname_without_default_symver.sh
Complile success

3.1.2 列出编译生成的文件

我们把libopensource.so.1.0相应3个文件放在"./opensource_v1"目录,把libopensource.so.2.0相应3个文件放在"./opensource_v2"目录:



3.1.3 用readelf查看编译生成的main,libvendor1.so,libvendor2.so

我们仅仅关注"NEEDED","RPATH"项。

"NEEDED"表示依赖的库,注意"NEEDED"并不是"libopensource.so.xxx.0"这样的完整名字,而是"libopensource.so.xxx"这样只包含大版本信息的名字(该名字术语叫"SONAME"),我们在编译libopensource共享库时通过"-Wl,-soname"指定"SONAME",参考文献 2中对"SONAME" "Major version" "Minor version" 有详细介绍; 编译时不指定""-Wl,-soname"的情况,这里不再讨论了(太多头绪,太乱),可以自己修改编译脚本做实验验证。

"RPATH"表示查找依赖库会从这些列出的路径查找(另外有个环境变量LD_LIBARARY_PATH也是类似的作用)。

更多细节所请自行Google。

[root@node1 0004]# readelf -d main

Dynamic section at offset 0xde8 contains 28 entries:
  Tag        Type                         Name/Value
  0x0000000000000001 (NEEDED)             Shared library: [libvendor1.so]
  0x0000000000000001 (NEEDED)             Shared library: [libvendor2.so]
  0x0000000000000001 (NEEDED)             Shared library: [libdl.so.2]
  0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
  0x000000000000000f (RPATH)              Library rpath: [./]
[root@node1 0004]# readelf -d libvendor1.so

Dynamic section at offset 0xde8 contains 27 entries:
  Tag        Type                         Name/Value
  0x0000000000000001 (NEEDED)             Shared library: [libopensource.so.1]
  0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
  0x000000000000000e (SONAME)             Library soname: [libvendor1.so]
  0x000000000000000f (RPATH)              Library rpath: [./opensource_v1]
[root@node1 0004]# readelf -d libvendor2.so

Dynamic section at offset 0xde8 contains 27 entries:
 Tag        Type                         Name/Value
 0x0000000000000001 (NEEDED)             Shared library: [libopensource.so.2]
 0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
 0x000000000000000e (SONAME)             Library soname: [libvendor2.so]
 0x000000000000000f (RPATH)              Library rpath: [./opensource_v2]

[root@node1 0004]# readelf -d opensource_v1/libopensource.so.1.0

Dynamic section at offset 0xe08 contains 25 entries:
  Tag        Type                         Name/Value
  0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
  0x000000000000000e (SONAME)             Library soname: [libopensource.so.1]
[root@node1 0004]# readelf -d opensource_v2/libopensource.so.2.0

Dynamic section at offset 0xe08 contains 25 entries:
  Tag        Type                         Name/Value
   0x0000000000000001 (NEEDED)             Shared library: [libc.so.6]
   0x000000000000000e (SONAME)             Library soname: [libopensource.so.2]

3.1.4 用nm|grep opensource_print查看编译生成的libvendor1.so和libvendor2.so, 可以看到使用相同符号"opensource_print"

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值