http://linux-wiki.cn/wiki/zh-hans/%E5%8A%A8%E6%80%81%E5%BA%93%28.so%29
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]
在系统中,会存在一些符号链接, 如[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前面了。