引言
做 Pwn 題時,經常會需要切換 libc,總會看見諸如 libc.so.6 的名稱,一直不理解它背後的含義,今天學習了 Linux 下共享庫的組織才恍然大悟。
在說 libc.so.6 之前,需要先了解共享庫版本號的概念和 so-name 命名機制。
共享庫版本號
版本號類型
共享庫一般會不斷更新以修復 bug、更改介面。有些更新是向後兼容的;有些更新是不兼容的,會導致依賴該庫的程序無法運行或需要重新編譯才能運行。
根據這些更新的兼容性,劃分為不同的版號:主版本號(Major Version Number)、次版本號(Minor Version Number)和發布版本號(Release Version Number)。
主版本號表示庫的重大升級。不同主版本號的庫之間互不兼容,需要更改介面,並重新編譯。依賴於舊共享庫的程序可能需要在系統中保留一份舊版的共享庫才能運行。
次版本號表示庫的增量升級,即增加一些新的介面符號,並保持原有符號不變。依賴於低次版本號共享庫的程序可以在高次版本號的共享庫下正常運行。
發布版本號表示庫的一些錯誤的修正、性能的改進等,介面不做變化。不同發布版本號之間完全兼容。
共享庫文件命名規則
Linux 有一套規則來命名系統中的每一個共享庫,它規定共享庫的文件命名規則如下:
libname.so.x.y.z
即前綴"lib"+庫名稱+後綴".so"+三個數字組成的版本號,其中,x 表示主版本號,y 表示次版本號,z 表示發布版本號。
例如,libfoo.so.2.6.1 表示的就是版本號為 2.6.1 的共享庫 foo。
但也存在一些不遵守這套命名規定的,比如最基本的 C 語言庫 Glibc 使用 libc-x.y.z.so 這種命名方式。
SO-NAME 命名機制
新的操作系統,包括 Solaris 和 Linux,普遍採用一種叫做 SO-NAME 的命名機制,就是把共享庫的文件名去掉次版本號和發布版本號,只保留主版本號。
例如,一個共享庫叫 libfoo.so.2.6.1,那麼它的 SO-NAME 就是 libfoo.so.2。
在 Linux 系統中,系統會為每個共享庫在它所在的目錄創建一個跟它的 」SO-NAME」 一樣的軟鏈接指向它。當共享庫更新時,這個軟鏈接會始終指向主版本號相同、次版本號和發布版本號最新的那個庫文件。
例如,目錄中同時有 /lib/libfoo.so.2.6.1 和 /lib/libfoo.so.2.5.3 的兩個不同版本的共享庫,軟鏈接 /lib/libfoo.so.2 就會指向 /lib/libfoo.so.2.6.1 的那個共享庫。
依賴於某個共享庫的文件只需要保存這個共享庫的 SO-NAME,就可以保證始終在使用系統中最新的主版本號的共享庫了,而無需在系統中保存各種版本的共享庫。
總結
說回 libc.so.6,我們知道現在我們使用的 libc 是 GNU 的 libc,也叫 glibc,glibc 目前版本是 2.x,按之前說的 SO-NAME 機制,應該是 libc.so.2 才對,但實際上卻是 libc.so.6。
這是因為,早期 Linux 內核開發者們 fork 了一份 glibc,獨立維護了第 2 版到第 5 版的 Linux libc(第 1 版的 libc 還是是 glibc)。但因為版權歸屬的問題,沒有合併回去。後來 glibc 2.0 發布了,把 POSIX 標準實現得很好,他們就又用回 glibc 了。繼此之後,glibc 自己的版本號雖然是 2.x,但由於最後使用的 Linux libc 的 soname 已經叫 libc.so.5 了,它就只能從 .6 開始計數了。
參考
- 《程序員的自我修養》第8章
- glibc - 維基百科, https://en.wikipedia.org/wiki/Glibc
本文鏈接: https://linuxstory.org/soname-of-linux-shared-library
LinuxStory 原創文章,轉載請註明出處,否則必究相關責任。