2012-06-15 106 views
52

考慮以下情形:動態鏈接庫具有依賴性

  • 共享庫libA.so,無依賴性。
  • 共享庫libB.so,libA.so作爲其依賴項。

我想編譯一個與libB鏈接的二進制文件。 我應該只用libB還是用libA鏈接二進制文件?

是否有任何方法只與直接依賴鏈接,讓從運行時依賴的解決未解決的符號?

我擔心庫libB實現可能會在將來發生變化,引入其他依賴關係(例如libC,libD,libE)。我會遇到問題嗎?

換句話說:

  • 力霸文件:a.cpp啊
  • libB文件:b.cpp BH
  • 主程序文件:main.cpp中

當然, b.cpp包含ah和main.cpp包含bh

編譯命令:

g++ -fPIC a.cpp -c 
g++ -shared -o libA.so a.o 

g++ -fPIC b.cpp -c -I. 
g++ -shared -o libB.so b.o -L. -lA 

我應該使用哪波紋管的選擇?

g++ main.cpp -o main -I. -L. -lB 

g++ main.cpp -o main -I. -L. -lB -lA 

我不能使用第一個選項。鏈接器抱怨來自庫libA的未解析符號。但這聽起來有點奇怪。

非常感謝。

- 更新註釋:

當我鏈接的二進制文件,鏈接器將嘗試解決從主和libB所有符號。但是,libB具有來自libA的未定義符號。這就是連接器抱怨的原因。

這就是爲什麼我需要與libA鏈接。 但是我發現了一種方法來忽略來自共享庫的未解析符號。 看起來我應該使用以下命令行來做到這一點:

g++ main.cpp -o main -I. -L. -lB -Wl,-unresolved-symbols=ignore-in-shared-libs 

看起來它仍然可以使用-rpath選項。 但是我需要更好地理解它。

使用-Wl,-unresolved-symbols=ignore-in-shared-libs選項時,有誰知道任何可能的陷阱?

- 更新註釋2:

-rpath不應該被用於此目的。強制在給定目錄中找到庫是有用的。 -unresolved-symbol方法看起來好多了。

再次感謝。

回答

27

看起來你已經是大部分路線了。你的調查做得很好。讓我們看看我能否幫助理清它背後的「爲什麼」。

以下是鏈接器正在做的事情。當你連接你的可執行文件('main')時,它有一些未解決的符號(函數和其他東西)。它會查看下面的庫列表,試圖解析未解決的符號。一路上,它發現一些符號是由libB.so提供的,所以它注意到它們現在由這個庫解決了。

但是,它還發現其中一些符號使用了其他符號,這些符號在您的可執行文件中尚未解析,因此現在需要解析這些符號。如果沒有鏈接到libA.so,你的應用程序將是不完整的。一旦鏈接到libA.so,所有的符號都被解析並且鏈接完成。

正如你所看到的,使用-unresolved-symbols-in-shared-libs,並不能解決問題。它只是推遲它,以便這些符號在運行時解決。這就是-rpath用於:指定要在運行時搜索的庫。如果這些符號無法解析,那麼您的應用程序將無法啓動。

這不是找出庫的依賴,因爲符號可以由多個庫來提供,並通過對他們中的任何一個連接來滿足一件容易的事情。

有這個過程在這裏的另一種描述:Why does the order in which libraries are linked sometimes cause errors in GCC?

+1

我知道這是很久以前,我忘記標記爲已解決。非常感謝您的回答。] – Marcus

+0

謝謝大家的驚人的問題,和一個合適的答案!我有幾乎完全相同的情況。我試圖將openCV共享對象包裝在自定義共享對象中。可以說C.so是我的習慣,所以CV.so就是一些openCV。我已經成功地鏈接了C.so和CV.so,並且我想將一個main.cpp(你可以!)鏈接到C.so,但是除了使用C.so的對象之外,main.cpp使用了一個在CV.so中定義的對象'cv :: Mat'。在這種情況下,main.cpp運行時是否需要鏈接C.so和CV.so?如果有人對此有所瞭解,我很樂意。謝謝! :) –

+0

爲什麼鏈接程序在生成libB.so時不能解析所有需要的libA.so符號? 主應用程序不應鏈接到libA,因爲它應該是透明的,例如,間接由libB屏蔽? – Wilson

11

動態鏈接只能用直接的依賴以後還可以-Wl,--as-needed使用-Wl,--as-needed與添加庫

gcc main.c -o main -I. -L. -Wl,--as-needed -lB -lA 

爲了檢查直接依賴你應該使用readelf而不是ldd因爲ldd al因此顯示間接依賴關係。

$ readelf -d main | grep library 
0x0000000000000001 (NEEDED)    Shared library: [libB.so] 
0x0000000000000001 (NEEDED)    Shared library: [libc.so.6] 

LDD也顯示了間接的依賴關係:

$ LD_LIBRARY_PATH=. ldd ./main 
linux-vdso.so.1 (0x00007fff13717000) 
libB.so => ./libB.so (0x00007fb6738ed000) 
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fb6734ea000) 
libA.so => ./libA.so (0x00007fb6732e8000) 
/lib64/ld-linux-x86-64.so.2 (0x00007fb673af0000) 

如果使用cmake的,您可以添加以下行只包括直接依賴:

set(CMAKE_EXE_LINKER_FLAGS "-Wl,--as-needed ${CMAKE_EXE_LINKER_FLAGS}") 
set(CMAKE_SHARED_LINKER_FLAGS "-Wl,--as-needed ${CMAKE_SHARED_LINKER_FLAGS}") 
+0

對不起,我已將您的示例作爲c程序而不是C++。因此我使用了gcc編譯器。但它也適用於g ++。 – user1047271

+0

這是非常有用的信息。我不知道使用redelf而不是ldd。非常感謝。 – Marcus

1

另一種選擇是使用libtool

如果更改g++調用libtool --mode=compile g++編譯源代碼,然後libtool --mode=link g++創造過的libB的應用程序,然後libA將自動鏈接。

0

這是一個有趣的帖子 - 我敲我的頭,這爲好,但我覺得你錯過這裏的一個點..

的思路如下,對不對?

main.cpp =(depends)=> libB.so =(depends)=> libA.so 

讓我們進一步考慮..

  • 在a.cpp(只有那裏)你定義一個類/變量,我們把它稱爲 「司馬」
  • 在b.cpp(和只有那裏)你定義了一個類/變量,我們稱之爲「symB」。
  • SYMB使用司馬
  • main.cpp中使用SYMB

現在,如果上述libB.so和libA.so已經編譯。在這之後,你的第一選擇應該工作,即:

g++ main.cpp -o main -I. -L. -lB 

我猜你的問題的事實,

在main.cpp中還指司馬

上午起源我糾正?

如果您在代碼中使用一個符號,那麼該符號必須在.so文件發現

的整體思路相互引用共享庫(即創建的API),是在更深的符號圖層隱藏(想到剝洋蔥),不使用。 ..即不要在你的main.cpp中引用symA,而只能引用symB(並讓symB僅引用symA)。

+0

你誤解了。顯然你需要解決直接需要的符號。問題與您的依賴可能需要的符號有關。開發庫供第三方使用時,圖書館用戶必須爲您的圖書館使用的功能提供實現,這是很常見的。你使用一個存根進行開發,完全沒有依賴關係,集成者將開發一個庫來替換存根,然後將所有東西鏈接在一起。加載器必須解決所有的依賴關係。然而,你的編譯腳本(和你的用戶)必須考慮它。 – Marcus