解决共享库版本不符的记录

2,266 阅读2分钟

问题的出现

今天在使用torm组件的时候需要mysql的共享库组件,但是发生了如下的报错信息:

./tormsvr: error while loading shared libraries: libmysqlclient_r.so.15: cannot open shared object file: No such file or directory

报错说找不到libmysqlclient_r.so.15共享库,这是mysql相关的库,可是我能启动mysql,说明应该是相关的库应该是存在的。通过以下命令可以查找该库安装的位置:

[guangmiwang@TENCENT64site ~/tsf4g_release/apps/torm/bin]$ ldconfig -p | grep libmysqlclient
	libmysqlclient_r.so.16 (libc6,x86-64) => /usr/lib64/mysql/libmysqlclient_r.so.16
	libmysqlclient.so.16 (libc6,x86-64) => /usr/lib64/mysql/libmysqlclient.so.16

可以看到确实安装了这个库,但是版本不对。

查找共享库版本不对的解决方案(my.oschina.net/qyh/blog/54…)

Linux 上的Dll ,叫shared library,其有三个名字,分别有不同的目的。

第一个是共享库本身的文件名(real name),其通常包含版本号,常常是是这样: libmath.so.1.1.1234 。 lib是Linux 上的库的约定前缀,math 是共享库名字,so 是共享库的后缀名,1.1.1234的是共享库的版本号,其主版本号+小版本号+build号。主版本号,代表当前动态库的版本,如果动态库的接口有变化,那么这个版本号就要加1;后面的两个版本号(小版本号 和 build 号)是告诉你详细的信息,比如为一个hot-fix 而生成的一个版本,其小版本号加1,build号也应有变化。 这个文件名包含共享库的代码。
第二个是动态库的soname( Short for shared object name),其是应用程序加载dll 时候,其寻找共享库用的文件名。其格式为

lib + math+.so + ( major version number)

其只包含major version number,换句话说,也就是只要其接口没有变,应用程序都可以用,不管你其后minor build version or build version。
问题来了,程序运行时怎么通过soname 找个real name? Soname 存在哪里?如果与real name 关联起来?什么时候存的?

这就是接下来要介绍的第三个共享库的名字,link name,顾名思义,就是在编译过程,link 阶段用的文件名。 其将sonmae 和real name 关联起来。

第三个名字,共享库的连接名(link name),是专门为build 阶段连接而用的名字。这个名字就是lib + math +.so ,比如libmath.so。其是不带任何版本信息的。在共享库编译过程中,连接(link) 阶段,编译器将生成一个共享库及real name,同时将共享库的soname,写在共享库文件里的文件头里面。可以用命令 readelf -d sharelibrary 去查看。

在应用程序引用共享库时,其会用到共享库的link name。在应用程序的link阶段,其通过link名字找到动态库,并且把共享库的soname 提取出来,写在自己的共享库的头文件里面。当应用程序加载时就会通过soname 去给定的路径下寻找该共享库。

共享库版本升级的解决办法

当升级主版本时,共享库的soname 就会加1.比如libhello.so.0.0.0 变为 libhello.so.1.0.0. 这时候再运行ldconfig 文件,就会发现生成两个连接 文件。

ln -s libhello.so.0---->libhello.so.0.0.0
ln -s libhello.so.1----->libhello.so.1.0.0

尽管共享库升级,但是你的程序依旧用的是旧的共享库,并且两个之间不会相互影响。
问题是如果更新的共享库只是增加一些接口,并没有修改已有的接口,也就是向前兼容。但是这时候它的主版本号却增加1. 如果你的应用程序想调用新的共享库,该怎么办? 简单,只要手工把soname 文件修改,使其指向新的版本就可以。(这时候ldconfig 文件不会帮你做这样的事,因为这时候soname 和real name 的版本号主板本号不一致,只能手动修改)。

比如: ln -s libhello.so.0 ---> libhello.so.1.0.0

但是有时候,主版本号增加,接口发生变化,可能向前不兼容。这时候再这样子修改,就会报错,“xx”方法找不到之类的错误。

尝试以上解决方案

想通过连接新的共享库到老的库版本soname上,但是在原本的共享库中没有权限。

[guangmiwang@TENCENT64site /usr/lib64/mysql]$ ln -s libmysqlclient_r.so.16.0.0 libmysqlclient_r.so.15
ln: creating symbolic link `libmysqlclient_r.so.15': Permission denied

因此只能在自己的目录下创建一个文件夹,链接到该目录下,然后添加共享库的路径。

ln -s libmysqlclient_r.so.16.0.0 /data/home/guangmiwang/mylib/libmysqlclient_r.so.15
export LD_LIBRARY_PATH=/data/home/guangmiwang/mylib

但是链接后还是报错:

./tormsvr: error while loading shared libraries: libmysqlclient_r.so.15: cannot open shared object file: No such file or directory

查看链接后的库文件,存在但是没有权限:

[guangmiwang@TENCENT64site ~/mylib]$ ls
libmysqlclient_r.so.15(红色)

试图修改权限,但是失败了:

[guangmiwang@TENCENT64site ~/mylib]$ chmod -R 007 libmysqlclient_r.so.15 
chmod: cannot operate on dangling symlink `libmysqlclient_r.so.15'

查找该问题出现的原因可能是link的时候没有用绝对路径创建,重新连接后权限对了:

ln -s /usr/lib64/mysql/libmysqlclient.so.16.0.0 /data/home/guangmiwang/mylib/libmysqlclient.so.15
[guangmiwang@TENCENT64site ~/mylib]$ ls
libmysqlclient.so.15  libmysqlclient_r.so.15(蓝色)

然而问题到这里还是没有结束,能找到这个共享库了,但是又报了新的错误:

/data/home/guangmiwang/mylib/libmysqlclient_r.so.15: version `libmysqlclient_15' not found

猜测出现的原因就是新的版本和旧的版本并不兼容,不能通过简单地把新的库链接到旧的soname上来解决这个依赖。

最终解决办法

冯俊鹏给我发了一个libmysqlclient.so.15,重命名成libmysqlclient_r.so.15这是他之前从网上下的,然后把LD_LIBRARY_PATH修改成该文件的路径就可以了。

总结

虽然这次问题的解决最后还是绕了一大圈才解决,但是在这个过程中链接了linux系统共享库相关的知识,以后碰到类似的问题应该能吸取教训。