Linux动态链接

问题

曾经不止一次遇到过这样的情况:从机器A拷贝一个二进制文件到另一台机器B,两台机器的操作系统版本一样,可是在机器A能正常运行,在机器B却提示错误。最常见的就是提示动态链接库找不到,如:

./test: error while loading shared libraries: libstdc++.so.6: cannot open shared object file: No such file or directory

其实就是说,找不到动态链接库libstdc++.so.6

最近又有一次碰到类似的问题,所以顺便把动态链接库的基本原理了解了一遍。

静态链接

静态链接库,在Linux下文件名后缀为.a,如libstdc++.a。在编译链接时直接将目标代码加入可执行程序。

动态链接

动态链接库,在Linux下是.so文件,在编译链接时只需要记录需要链接的号,运行程序时才会进行真正的“链接”,所以称为“动态链接”。如果同一台机器上有多个服务使用同一个动态链接库,则只需要加载一份到内存中共享。因此,动态链接库也称共享库

命名规则

动态链接库与应用程序之间的真正链接是在应用程序运行时,因此很容易出现开发环境和运行环境的动态链接库不兼容或缺失的情况。

Linux通过规定动态链接库的版本命名规则来管理兼容性问题。

Linux规定动态链接库的文件名规则比如如下:

libname.so.x.y.z

  • lib:统一前缀。
  • so:统一后缀。
  • name:库名,如libstdc++.so.6.0.21的name就是stdc++。
  • x主版本号。表示库有重大升级,不同主版本号的库之间是不兼容的。如libstdc++.so.6.0.21的主版本号是6。
  • y次版本号。表示库的增量升级,如增加一些新的接口。在主版本号相同的情况下,高的次版本号向后兼容低的次版本号。如libstdc++.so.6.0.21的次版本号是0。
  • z发布版本号。表示库的优化、bugfix等。相同的主次版本号,不同的发布版本号的库之间完全兼容。如libstdc++.so.6.0.21的发布版本号是21。

另外,Linux下的一个动态链接库会有下面三个名字:

libstdc++.so -> libstdc++.so.6.0.21*
libstdc++.so.6 -> libstdc++.so.6.0.21*
libstdc++.so.6.0.21*
  • libstdc++.so:linker name,程序编译链接时如果依赖了共享库,链接器只认不带任何版本的共享库。如果存在多个同名(上面命名规则中的name)动态链接库,linker name会指向最新的一个。
  • libstdc++.so.6:SO_NAME, 程序运行时会按照这个名称去找真正的库文件。也就是说,ELF可执行文件中保存的动态库名就是SO_NAME。如果存在多个同一主版本号的动态链接库,SO_NAME会指向最新的一个。
  • libstdc++.so.6.0.21:real name,这是动态链接库的真正名称。

相关路径

  • /lib:最关键和基础的动态链接库。
  • /usr/lib:关键的动态链接库。
  • /usr/local/lib:第三方动态链接库。
  • 由/etc/ld.so.conf配置文件指定的目录。

ldconfig

动态链接器不可能在每次查找动态链接库都去遍历所有动态链接库的目录,这样速度太慢了。因此,在系统启动时会通过ldconfig为动态链接库生成SO_NAME/etc/ld.so.cache存放系统动态链接库的路径信息,加速动态链接库的查找。

程序启动查找动态链接库的路径顺序如下:

  1. LD_LIBRARY_PATH指定的路径。
  2. 由路径缓存文件/etc/ld.so.cache指定的路径。
  3. 默认共享库目录,先/usr/lib,然后/lib

注意,安装动态链接库后,需要重启系统或运行ldconfig生成SO_NAME和刷新/etc/ld.so.cache文件。

ldd

通过ldd elf_file可以查看ELF文件依赖哪些动态链接库,如

$ ldd test
linux-vdso.so.1 =>  (0x00007ffc89b46000)
libstdc++.so.6 => /usr/lib/x86_64-linux-gnu/libstdc++.so.6 (0x00007f6e20ec7000)
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f6e20bc1000)
libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f6e209ab000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f6e205e3000)
/lib64/ld-linux-x86-64.so.2 (0x00007f6e211cb000)
  • linux-vdso.so.1是内核提供的一个动态链接库,所以这里只有一个内存地址。
  • /lib64/ld-linux-x86-64.so.2是一个动态链接库的绝对路径。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • LevelDB 完全解析(3):SSTable

    SSTable 全称 Sorted String Table,顾名思义,里面的 key-value 都是有序保存的。除了两个 MemTable,LevelDB ...

    linjinhe
  • LevelDB:读操作

    前面写了两篇文章介绍 LevelDB 的整体架构和接口使用。这篇文章,我们从代码的角度看看 LevelDB 的设计与实现,先从读操作开始。

    linjinhe
  • epoll

    epoll是Linux提供的I/O event notification facility。在需要监听的fd数量很多(成千上万)而同一时刻可读/可写的数量又比较...

    linjinhe
  • Postgresql部署及简单操作

    PostgreSQL是一个功能强大的开源对象关系数据库管理系统(ORDBMS),在开源数据库使用上与MySQL各领风骚。但也有不少人质疑postgresql的未...

    July
  • 动态链接库

    动态链接库,又称为共享链接库。采用动态链接库实现链接操作时,程序文件中哪里需要库文件的功能模块,GCC 编译器不会直接将该功能模块的代码拷贝到文件中,而是将功能...

    zy010101
  • Ubuntu 安装Postgresql及配置

    飞奔去旅行
  • java 使用jdbc连接Greenplum数据库和Postgresql数据库

    1、公司使用的Greenplum和Postgresql,确实让我学到不少东西。简单将使用jdbc连接Greenplum和Postgresql数据库。由于使用ma...

    别先生
  • 在Linux中如何查找最大的10个文件方法汇总

    众所周知当系统的磁盘空间不足时,您可能会使用 df、du 或 ncdu 命令进行检查,但这些命令只会显示当前目录的文件,并不会显示整个系统范围的文件。

    砸漏
  • bootstrap-table 父子表 联动表 完整例子

    版权声明:本文为博主原创文章,欢迎转载。 ...

    程裕强
  • Stata | 字符型和数值型转换

    刚接触 Stata 不久的朋友都容易把数值型和字符型弄混,导致在条件筛选和运算过程中报错。数值型和字符型是什么意思呢?可以把它们理解为 Excel 中的单元格格...

    PyStaData

扫码关注云+社区

领取腾讯云代金券