2015-06-19 206 views
1

TL;博士鏈接共享庫可執行與另一個共享庫

是否有這樣的Linux負載的任何分歧和鏈接共享庫,如果庫是由可執行文件或通過其他共享庫鏈接?

背景

假設我有包含具有靜態std::map一個類共享庫(例如libA.so)和一組單類。每個單身人士課程都可以訪問地圖並靜態地將自己的一個實例添加到地圖中。

有兩種情況:

  1. 我使用共享庫(libA.so)中的可執行文件讀取所有註冊類從全球地圖。
  2. 我使用共享庫(libA.so)在另一個共享庫(libB.so),並使用在可執行這個新的。在這種情況下,libB.so使用來自libA.so的地圖爲可執行文件提供一些功能(如外觀)。

問題

如果使用(即鏈接)此共享庫中的可執行(方案1)中,上述地圖包含單類的列表,然而,如果使用這個庫在另一個共享庫中,然後在可執行文件中使用新文件(場景2),地圖似乎是空的。

我似乎無法理解鏈接器如何處理共享庫,在任何情況下。

更新

事實證明libB.so不鏈接到正確libA.so即使使用的g++-lA標誌作爲明確指示。雖然我不能看到libA.so通過libB.so使用lddpmapobjdump聯繫,我使用的libA.so類時,沒有得到運行時錯誤。如果我運行與clang++相同的命令,我可以看到所有所需的庫都列出。

+3

在UNIX系統上,共享庫僅爲其代碼頁共享內存。除非您編寫特殊代碼以使庫使用共享內存,否則數據頁仍駐留在使用庫的進程中。我相信Windows有一個DLL將共享數據和代碼的模式。 – jxh

+0

@jxh我在Linux上,但仍然無法理解當我通過另一個共享庫鏈接共享庫時爲什麼會發生所有這些靜態初始化,以及爲什麼它可以正常工作,如果直接通過可執行文件鏈接它? –

+0

當你生成你的第二個庫時,你是否100%確定你是第一個共享庫的靜態鏈接? –

回答

4

我將描述一個可能產生您所看到的行爲的場景。

  1. 單體對象的全局構造函數被調用,並且它們會根據地圖進行註冊。
  2. 地圖的全局構造函數被調用,導致地圖被初始化爲一個空的數據結構。

另一種情形:

  • 如果你正在閱讀從libB.so從全球構造的地圖,它可以在任何物體都有機會進行自我註冊之前調用。

一般來說,沒有保證從不同的翻譯單元執行全局構造函數的順序,當然也不是來自不同的共享庫。

上述第一個問題的解決方案是爲地圖使用singleton樣式模式,以便在使用時初始化它,而不是通過全局構造函數初始化。

TheMap & GlobalMap() { 
    static TheMap instance; 
    return instance; 
} 

上述第二個問題的解決方案將禁止從全局構造函數訪問全局映射。也就是說,將libB.so中的所有全局構造函數更改爲初始化,而不是在全局構造函數中進行初始化。

+0

我認爲全球構造者的概念正是我所缺少的。我更新了這個問題,以澄清我到底做了什麼。我想說,你的回答只是解釋我陷入的局面。 –

+0

我認爲在場景2中執行得遲的地圖的全局構造函數是導致你的問題的原因,正如我所說的,沒有保證執行順序全球建設者。您可以添加一些打印語句來證明在地圖構建之前註冊正在發生。至於爲什麼順序在被另一個庫鏈接時會發生變化,這可能與鏈接器構建的某個對象依賴關係樹有關,但我沒有確定的答案。 – jxh

+0

我剛剛用'LD_DEBUG = libs'進行了檢查,發現在可執行文件的情況下,我有這行'調用init:/ PATH/TO/LIB/libA.so',如果我在第二次嘗試調試場景。所以你是完全正確的! –

4

Drepper的論文How To Write a Shared Library關於這個問題的一個很好的參考,並應回答你的問題。

你應該確保在第二個方案中,庫被鏈接只有一次(例如,使用完全相同的路徑)。見ld-linux(8)並使用例如LD_DEBUG等等

您可能還strace您的執行,瞭解正在發生的事情。

而且您應該檢查(使用pmap或使用cat /proc/$ThePid/maps)該庫只加載一次。

+0

請編輯不可訪問的鏈接。 +1提到'pmap'。 –

+0

謝謝,更正了鏈接。 –

+0

@BasileStarynkevitch事實證明,當使用'ldd'或'objdump -p'時,庫不會被初始化,甚至不會被視爲需要DSO。 **然而**這隻有在使用'g ++'時纔會發生。通過'clang',所有必需的庫都能正確鏈接。 –