動態庫(.so)

此頁由 Linux Wiki用戶Chenxing 於 2011年3月27日 (星期日) 07:11 的最後更改。

出自Linux Wiki

提示:此文已超过 13 年(4750 天)未更新,如发现内容过时或有误,欢迎改进:)

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前面了。

參考資料

  1. How To Write Shared Libraries
  2. ChinaUnix.net: Linux so文件命名規則的討論
  3. LSB-Spec郵件列表中的一封信:Shared Libraries and naming conventions
  4. Static, Shared Dynamic and Loadable Linux Libraries

本文对您有帮助?分享给更多朋友!

反馈与讨论

发现文档不全面、有错误却没时间编辑文档?想分享自己的经验或见解?欢迎在此留言、讨论。
简体繁体转换