You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
$ ldd main
linux-vdso.so.1 (0x00007fff71525000)
libsay.so.1 => not found
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff5c8729000)
/lib64/ld-linux-x86-64.so.2 (0x00007ff5c8cca000)
首先了解下共享库的命名约定
共享库的命名规则:
libname.so.x.y.z
主版本号表示库的重大升级,不同主版本号之间不兼容,旧的接口符号进行了改动
次版本号表示库的增量升级,即增加一些新的接口符号,且保持原来的符号不变
发布版本号表示库的一些错误的修正,性能的改进等,并不添加任何新的接口,也不对旧的接口进行修改
共享库的编译和运行过程涉及到3个名称:
real name
,soname
和linker name
real name
: 编译出来的库文件的真实名字。比如libfoo.so.1.0.0soname
: 描述共享库功能的一个逻辑名称(logic name)。命名规则为libname.so.x,只保留了主版本号,常用于提供版本向后兼容性信息。比如程序a在编译时使用了共享库libx.1.0.0,而现在系统中只包含了libx.1.1.0,那么程序a会根据它所依赖的soname,即libx.1,去使用比编译时更高版本的库libx.1.2.0,因为该库是兼容1.1.0版本的linker name
: 链接器使用该名称去寻找共享库,一般不包含版本号。比如使用链接选项-lfoo,那么链接器就会去寻找名称为libfoo.so的文件,libfoo.so就是linker name通常情况下,linker name软链接到soname, soname软链接到real name
这三个名称会在下面实际例子中体现各自的作用
接下来以一个实际的例子来演示共享库的版本控制实践
say.c
say.h
main.c
共享库的编译
这里指定libsay.so.1.0.0的soname为libsay.so.1,soname可以通过查看.dynamic段确认
共享库的安装和使用
libsay.so.1.0.0库使用者拿到.so和.h文件后,执行以下命令生成可执行程序main
ldconfig -n .
会生成一个软链接libsay.so.1链接到libsay.so.1.0.0,如果有多个版本,那么该软链接会指向最libsay.so.1.x.y中的最新版本第二条指令
$ ln -sf libsay.so.1 libsay.so
是创建一个名为libsay.so软链接,指向libsay.so.1。为什么需要创建libsay.so呢,因为链接时-lsay会去搜索libsay.so,也就是linker name,否则会报找不到库的错。查看最终生成的可执行程序的依赖信息
可以看到执行程序main记录的是共享库的soname,而不是real name或linker name(也可以通过readelf -d查看依赖的共享库),所以装载时会去找名称为soname的库
由于动态链接器查找共享库默认是从/lib/,/usr/lib和/ld.so.conf文件指定的路径中寻找,而我们没有把libsay.so.1拷贝到其中某个路径下,所以输出结果中libsay.so.1显示not found,这里将so库所在路径添加到环境变量LD_LIBRARY_PATH后即可显示,这里我的so文件和可执行文件在同一级目录下,所以执行如下命令
共享库的版本升级
当共享库更新版本时,共享库提供者还是按照上面步骤进行编译生成so,而使用者只需要重新执行
$ ldconfig -n .
就可以让soname指向最新版本soname机制存在的问题
如下面示例
假设现在代码如下
say.c
say.h
进行编译链接,生成libsay.so.1.0.0
然后我增加了了一个say_hello_v2接口,现在代码如下
say.c
say.h
进行编译链接,生成libsay.so.1.1.0
假设此时用户B在编译可执行程序main的时候,所在的编译环境使用的是libsay.so.1.1.0,且程序使用了say_hello_v2符号,但是main程序的运行环境的机器上却使用了libsay.so.1.0.0,由于程序在进行动态链接时soname只判断主版本号,因此可以正常装载1.0.0版本的库,但是可能造成的后果就是
很明显这种运行时出现未知行为我们是不允许的,解决这种问题可以通过使用**基于符号的版本控制机制(symbol versioning)**来解决。
基于符号的版本控制
基于符号的版本控制实际上就是让每个导入和导出的符号都有一个相关联的版本
我们为say.c中的接口符号指定版本
say.c
say.h
编写符号版本脚本say.ver
编译链接,生成libsay.so.1.0.0
接下来修改say.c添加say_hello_v2接口,并修改符号版本脚本,最后生成libsay.so.1.1.0
say.ver
编译链接
接下来编写main.c
main.c
把main.c和libsay.so.1.1.0在放在同级目录,生成依赖libsay.so.1.1.0的可执行程序main
把libsay.so.1.1.0和main拷贝到同一目录下,运行:
正常。
接下来把libsay.so.1.0.0和main拷贝到同一目录下,运行
可以看到main程序直接被阻止运行,而不是等到运行时才崩溃,避免了上面提到的运行时出现symbol lookup error的错误。
参考:
Shared objects for the object disoriented!
《程序员的自我修养》
soname
The text was updated successfully, but these errors were encountered: