运行程序时总是提示带so.xx.xx版本号共享库打不开(共享库的不同命名的含义)
运行程序时总是提示带so.xx.xx版本号共享库打不开(共享库的不同命名的含义)
- 写作目的
- 正文
- 总结
写作目的
最近在写 Redis c++客户端的demo,从github上下载了 hiredis库源码,自己编译 hiredis源码得到名为 ‘libhiredis.so’ 的共享库文件,放到我的demo工程中进行使用。编译成功后,运行demo的时候发现一直报错:
ubuntu@linux:~/test/common_redis/build$ ./main-test
./main-test: error while loading shared libraries: libhiredis.so.1.2.1-dev: cannot open shared object file: No such file or directory
我很奇怪,‘libhiredis.so’ 是存在的,编译也没问题,但运行怎么会报‘‘libhiredis.so.1.2.1-dev’’找不到的错误,想不通‘libhiredis.so.1.2.1-dev’是从哪里来的,CmakeList.txt和代码中都没有出现过‘‘libhiredis.so.1.2.1-dev’’字符串。 之前对共享库的链接原理一直朦朦胧胧,所以这次来探究一下,写下这篇文章记录一下。
正文
先了解动态库三个名字:
- linker-name
linker-name 是链接器链接共享库所用到的名字,其格式为 libxxx.so ,也就是说其不带任何版本号, gcc编译 链接时就是找它,可以是一个软链接。 - soname
soname 是一个很重要的名字,全称是 Shared Object Name(共享对象名称),其格式为 libxxx.so.a,也就是在 linker name 后面加上主版本号,可以是一个软链接。 - real-name
real-name 就是共享库实际文件的真实名称,传统意义上来说,一个共享库的真实名称应该是 libxxx.so.a.b.c 的格式,但实际上并不一定。
要注意的是,soname 是直接被保存在共享库的二进制文件中的,通过‘readelf -d’命令可以查看:
ubuntu@linux:~/test$ readelf -d libhiredis.so
Dynamic section at offset 0x13de0 contains 25 entries:标记 类型 名称/值0x0000000000000001 (NEEDED) 共享库:[libc.so.6]0x000000000000000e (SONAME) Library soname: [libhiredis.so.1.2.1-dev] ##### 看这行0x000000000000000c (INIT) 0x3000...
然而,在编译链接的时候,可执行文件链接的就是共享库的 soname 名称,虽然编译链接时查找的是 linker-name文件,但是可执行文件实际链接 linker-name中的‘SONAME’属性指定的共享库名称, 如下(main-test是C++可执行文件):
ubuntu@linux:~/test$ ldd main-test linux-vdso.so.1 (0x00007ffc53be2000)libhiredis.so.1.2.1-dev => /home/ubuntu/test/libhiredis.so.1.2.1-dev (0x000078740143a000) ##### 看这行...
因此,如果共享库的 soname 文件不存在,运行程序就报 "xxx.so.xx 无法打开!"的错误,导致程序无法启动,这时可以手动创建 soname 文件(软链接,指向 real-name 文件):
ubuntu@linux:~/test$ ln -s libhiredis.so libhiredis.so.1.2.1-dev
这样,程序就能正常运行了。
总结
real-name:共享库实际文件的真实名称,一般带有完整的版本号。
linker-name: 用于编译链接,其格式为 libxxx.so。
soname: 一般带有主版本号,用于兼容性控制,使得共享库的小更新(不影响版本兼容性)不需要重新编译可执行文件,只要重新链接到新的到新版本 real name文件,原来的可执行文件即可以自动加载新版的共享库。
linker-name 和 soname 文件一般都是 指向real-name文件的软链接。
参考链接:https://blog.csdn.net/qq_21746331/article/details/120333736