Linux中的.so文件类似于Windows中的DLL,是动态链接库,也有人译作共享库(因so的全称为Shared Object)。当多个程序使用同一个动态链接库时,既能节约可执行文件的大小,也能减少运行时的内存占用。[1]

对于用户而言,经常遇到的问题是某些应用程序找不到其需要的.so文件:

error while loading shared libraries: ...: cannot open shared object file: No such file or directory

本文将主要围绕该问题展开,介绍so文件存放位置、版本命名方案等。欢迎补充其它信息。

存放位置

Linux中绝大多数.so文件都存放在/lib/usr/lib/(见Linux目录结构),对于64位和32位共存的系统,32位的动态库可能会放在/lib32/usr/lib32,完整的动态库存放路径列表可通过/etc/ld.so.conf文件配置。(如果修改了配置,需要用 /sbin/ldconfig 命令更新缓存)

应注意动态库搜寻路径并不包括当前文件夹,所以当即使可执行文件和其所需的so文件在同一文件夹,也会出现找不到so的问题,如

./chrome: error while loading shared libraries: libnss3.so.1d: cannot open shared object file: No such file or directory

此时可用LD_LIBRARY_PATH环境变量做临时设置,如:

LD_LIBRARY_PATH=.:$LD_LIBRARY_PATH ./chrome

也有些so文件是在程序执行时临时加载的(如插件),它们的路径就比较灵活,只要可执行文件能找到它就行了。

程序链接的动态库

一个可执行文件链接了哪些动态库呢?在遇到“error while loading shared libraries”时,我们难免会对此产生好奇。

查看该信息的方法是通过ldd,如

$ ldd chrome
   linux-vdso.so.1 =>  (0x00007fff52dff000)
   libX11.so.6 => /usr/lib/libX11.so.6 (0x00007f0caebe4000)
   libdl.so.2 => /lib/libdl.so.2 (0x00007f0cae9e0000)
   libXrender.so.1 => /usr/lib/libXrender.so.1 (0x00007f0cae7d6000)
   libXss.so.1 => /usr/lib/libXss.so.1 (0x00007f0cae5d3000)
   libXext.so.6 => /usr/lib/libXext.so.6 (0x00007f0cae3c1000)
   librt.so.1 => /lib/librt.so.1 (0x00007f0cae1b9000)
   ....(略)

要想看系统还没找到的动态库,可以借用grep:

$ldd chrome | grep 'not found'
  libnss3.so.1d => not found
   libnssutil3.so.1d => not found
   libsmime3.so.1d => not found
   libplc4.so.0d => not found
   libnspr4.so.0d => not found


版本

动态库的版本总是个问题,如果编译时链接的库和执行时提供的不一样,难免会导致程序的执行发生诡异的错误。为解决此问题,Linux系列的做法是这样的:

首先,每个so文件有一个文件名,如libABC.so.x.y.z,这里ABC是库名称,x.y.z是文件的版本号,一般来说:[2]

  • 第一位x表示了兼容性,x不一样的so文件是不能兼容的。
  • 第二位y的变化表示可能引入了新的特性(Feature),但总的来讲还是兼容的。
  • 第三位z的变化一般表示仅是修正了Bug。
  • 并非所有.so文件都遵循此规则,但其应用确实很普遍。

在系统中,会存在一些符号链接, 如[3]

libpam.so -> libpam.so.0.83.0
libpam.so.0 -> libpam.so.0.83.0

其中第一个主要在使用该库开发其它程序时使用,比如gcc想连接PAM库,直接连libpam.so就行了,不必在链接时给出它的具体版本。第二个则主要用在运行时,因为前面说了第一位版本一样的库是互相兼容的,所以程序运行时只要试图连接libpam.so.0就够了,不必在意其具体的版本。ldconfig可以自动生成这些链接。

那么编译程序时gcc在链接一个so文件(如libpam.so)时,如何知道该程序运行时链接哪个文件呢(上例中是libpam.so.0)?原来产生so文件时,可以指定一个soname,一般形如libABC.so.x[4]人们编译可执行文件时,如果链接了某个so,存在可执行文件里的.so文件名并不是其全名,而是这个soname。比如上例中,这个soname就是libpam.so.0。回头看一下上节ldd的结果,可以印证这一现象。

有时还会看到形如libABCn.so,即版本号出现在.so前面的库文件,如libXaw6.so。此类文件一般是为开发者着想,比如GTK+ 3已经推出,但很多开发者还是想用GTK+ 2开发软件。由于编译时只连接无版本号的.so文件,就只有把版本号放在.so前面了。

Logo

更多推荐