2015-11-03 14 views
1

我正在使用一個共享庫,其中有幾個全局變量, 幾乎用於所有導出的函數 ,因此庫函數不是線程安全的。 我的應用程序創建多個線程,每個線程動態地打開這個 庫,並避免使用並行調用 到導出 功能之間的同步,我的圖書館多次以不同的名稱複製磁盤與 每個線程打開自己的副本。爲了避免這種情況,現在我正在使用dlmopen來代替,但是我正面臨一個問題。未解決的符號只有dlmopen而不是dlopen

當我使用的dlopen在我的應用程序中打開庫,應用程序工作正常

libHandle = dlopen(ip->pathname, (RTLD_LAZY |RTLD_LOCAL|RTLD_DEEPBIND|RTLD_NODELETE)); 

當我在應用程序而不是使用dlmopen,我得到錯誤:

ip->libHandle = dlmopen(LM_ID_NEWLM, ip->pathname, 
       (RTLD_LAZY |RTLD_LOCAL|RTLD_DEEPBIND|RTLD_NODELETE)); 

的dlerror獲得是:

error(libfoo.so.0: undefined symbol: _ZTIN6google8protobuf11MessageLiteE) 

做nm顯示符號undefined U _ZTIN6google8protobuf11MessageLiteE

問題1:我想知道如何解決此問題,以便我可以使用dlmopen。

原因是因爲當使用LM_ID_NEWLM時,在libc中沒有任何符號的情況下會創建一個新的空名稱空間。所以這個庫應該是自包含的或者與任何依賴關係重新鏈接。

問題2:我的主應用程序導出libfoo將使用的一些符號。由於在新命名空間中打開libfoo,主應用程序的符號對libfoo不可見,因此無法解析它們。 有沒有什麼辦法告訴鏈接器創建一個新的命名空間NEWLM,通過複製現有的基本命名空間,並使用新創建的命名空間的dlmopen + lmid來打開libfoo,其中所有其他必需的符號已經存在?

問題3:我可以自己mmap libfoo的不同部分,並提供一個指向libc的mmaped部分的指針。意味着需要打開文件並將其從libc中取出並讓其執行符號解析的工作?這樣我就不需要調用dlopen,多個文本部分的問題就可以解決。

回答

1

how can I resolve this issue

當您使用dlopen,新加載的庫可以使用所有已經加載庫,以解決其符號。我猜測libprotobuf.so是這種已經加載的庫之一。

當您使用dlmopen(LM_ID_NEWLM, ...)時,您新加載的庫必須是必須完全自包含。

dlmopen失敗的事實告訴你它不是。您應該將libfoo.so.0libprotobuf.so(以及任何其他需要的庫)重新鏈接。

使用ldd -r libfoo.so.0驗證其中的所有符號都已解析。連接libfoo.so.0時使用-Wl,--no-undefined也是一個好主意。

更新:

My main application exports some symbols which libfoo will use. Due to opening libfoo in new namespace, the symbols of main application are not visible to libfoo and hence it is not able to resolve them.

這是預期的行爲。如果有這種符號的相當小的數字,你可以明確地libfoo註冊它們:

void *h = dlmopen(...); 
void (*init)(void *, void *) = dlsym(h, 'init'); 
(*init)(&main_fn1, &main_fn2); 

Is there any way to tell linker to create a new namespace NEWLM, by making replica of existing base namespace and than use dlmopen + lmid of newly created namespace to open libfoo with all other required symbols being already present?

我不這麼認爲。這是一個有趣的想法。請隨時打開glibc bugzilla中的功能請求。

With dlmopen it seems plausible (although max limit of 16)

在我看來,雖然中libfoo 16個實例是比一個好,你仍然嚴重這條道路上的限制,這將是更好的重寫libfoo在一開始就不使用全局變量。

更新2:

Can I myself mmap the different section of libfoo and provide a pointer to the mmaped sections to libc

你可以,如果GLIBC bug 11767實施。但事實並非如此。

+0

感謝您的答覆。將libfoo與libproto以及所有其他所需的庫重新鏈接是可能的。但是我有更大的問題。我的主應用程序導出libfoo將使用的一些符號。由於在新命名空間中打開libfoo,主應用程序的符號對libfoo不可見,因此無法解析它們。有沒有什麼辦法告訴鏈接器創建一個新的命名空間NEWLM,通過複製現有的基本命名空間並使用新創建的命名空間的dlmopen + lmid來打開libfoo,其中所有其他必需的符號已經存在? – ashish

+0

此活動的關鍵是避免由於每個副本實例具有不同的inode號而多次複製同一個庫所導致的文本部分的多個副本。將這與inode相同的情況進行比較。在這種情況下,文本部分將在每個dlopen實例之間共享,而每個都有自己的私人數據副本。不幸的是,dlopen不允許多重打開,因爲libc代碼檢查inode是否已經在當前命名空間中打開。使用dlmopen似乎是合理的(雖然最大限制爲16),但空的命名空間給我的問題。 – ashish

+0

@ user2270067我已經更新了答案。 –

1

Is there any way to tell linker to create a new namespace NEWLM, by making replica of existing base namespace and than use dlmopen + lmid of newly created namespace to open libfoo with all other required symbols being already present?

這是我解決了類似的問題的方法:

  1. 動態加載的protobuf到一個新的命名空間:

    void* pb_handle = dlmopen(LM_ID_NEWLM, "libprotobuf.so", RTLD_LAZY); 
    
  2. 搶命名空間ID:

    Lmid_t lmid; 
    dlinfo(dl_handle, RTLD_DI_LMID, &lmid); 
    
  3. 開富到您的新protobuf的命名空間:

    void* foo_handle = dlmopen(lmid, 「libfoo.so.0」, RTLD_LAZY);