Linux、GNU GCC、ld、版本腳本和 ELF 二進制格式——它是如何工作的?
我正在嘗試更多地了解 Linux 中的庫版本控制以及如何將其全部投入使用。這是上下文:
我有兩個版本的動態庫,它們公開相同的一組介面,比如
libsome1.so
和libsome2.so
.應用程序連結到
libsome1.so
.– 此應用程序用於
libdl.so
動態載入另一個模組,例如libmagic.so
.
現在
libmagic.so
是針對libsome2.so
. 顯然,如果不使用連結器腳本來隱藏符號libmagic.so
,在執行時所有對介面的呼叫都會libsome2.so
解析為libsome1.so
. 這可以通過libVersion()
對照宏的值檢查返回的值來確認LIB_VERSION
。所以我接下來嘗試編譯和連結
libmagic.so
一個連結器腳本,該腳本隱藏除 3 之外的所有符號,這些符號在其中定義libmagic.so
並由它導出。這有效……或者至少libVersion()
和LIB_VERSION
值匹配(並且它報告版本2而不是1)。– 但是,當一些資料結構被序列化到磁碟時,我注意到了一些損壞。在應用程序的目錄中,如果我刪除
libsome1.so
並在其指向的位置創建一個軟連結libsome2.so
,則一切都按預期工作,並且不會發生相同的損壞。我不禁認為這可能是由於執行時連結器對符號的解析中的一些衝突引起的。我已經嘗試了很多事情,比如嘗試連結
libsome2.so
以使所有符號都指向symbol@@VER_2
(我仍然對此感到困惑,因為該命令nm -CD libsome2.so
仍然將符號列為symbol
而不是symbol@@VER_2
)……似乎沒有任何效果!幫助!!!!!!
這並不能完全回答您的問題,但是…
首先,ELF 是 Linux 對執行檔(程序)、共享庫以及編譯軟體時發現的中間文件的目標文件的規範。目標文件以 .o 結尾,共享庫以 .so 結尾,後跟零個或多個用句點分隔的數字,執行檔通常沒有任何副檔名。
通常有三種形式來命名共享庫,第一種形式只是以 .so 結尾。例如,名為 readline 的庫儲存在名為 libreadline.so 的文件中,通常位於 /lib、/usr/lib 或 /usr/local/lib 之一下。該文件位於使用 -lreadline 等選項編譯軟體時。-l 告訴編譯器連結以下庫。由於庫不時更改,它可能會過時,因此庫嵌入了稱為 SONAME 的東西。readline 的 SONAME 可能類似於 libreadline 的第二個主要版本的 libreadline.so.2。也可能有許多次要版本的 readline 是兼容的並且不需要重新編譯軟體。readline 的次要版本可能被命名為 libreadline.so.2.14。通常是 libreadline。so 只是到最新主要版本的 readline 的符號連結,在這種情況下是 libreadline.so.2。libreadline.so.2 也是 libreadline.so.2.14 的符號連結,它實際上是正在使用的文件。
庫的 SONAME 嵌入在庫文件本身中。文件 libreadline.so.2.14 中的某處是字元串 libreadline.so.2。當一個程序被編譯並與 readline 連結時,它將查找文件 libreadline.so 並讀取其中嵌入的 SONAME。稍後,當程序實際執行時,它將載入 libreadline.so.2,而不僅僅是 libreadline.so,因為這是第一次連結時讀取的 SONAME。這允許系統安裝多個不兼容的 readline 版本,並且每個程序都將載入與其連結的適當主要版本。此外,當將 readline 升級到 2.17 時,我可以在現有庫旁邊安裝 libreadline.so.2.17,一旦我將符號連結 libreadline.so.2 從 libreadline.so.2.13 移動到 libreadline.so.2.17,