如何解決庫鏈接出現符號重定義問題

下面是一組和GPT的對話,通過逐個排查,我們知道只有一種情況會導致符號重定義。以下假設在 CMake 配置環境。

Q:如果一個靜態庫a,鏈接了b,這個時候 c 鏈接了 ab ,會導致 b 符號重複定義麼?

A:
任何庫(靜態或動態)本身都不鏈接其他庫。他們可能有對其他庫的依賴性,但實際上並不包含其他庫的代碼。

在你給出的情況下,假設你有三個靜態庫ab和一個可執行目標c

a使用了庫b中的一些符號(函數或變量);
目標c鏈接了庫a和庫b
在這種情況下,不會出現符號重複定義的問題,因爲庫a並不會“包含”庫b,只是有對它的依賴性。鏈接器在鏈接可執行文件c時,它會從庫a和庫b中提取所需的對象文件,如果同一符號在兩個庫中都找到了,鏈接器會選擇其中一個(具體選擇哪個取決於鏈接器的行爲)。

然而需要注意的是,靜態庫的鏈接順序是重要的。在上面的例子中,由於庫a依賴於庫b,所以在鏈接目標c時,應該先鏈接庫a,然後再鏈接庫b,這樣鏈接器才能正確解析庫a中對庫b的依賴。以下是在CMakeLists.txt中進行鏈接的示例:

target_link_libraries(c a b)

如果鏈接命令寫成了 target_link_libraries(c b a),那麼可能會出現未定義的符號錯誤。這是因爲當鏈接器處理庫b時,它並不知道後面還有對這些符號的引用。

Q: 如果一個靜態庫a,鏈接了b,這個時候可執行程序 c 鏈接了 ab ,會導致 b 符號重複定義麼?

A:
在你的情況下,即使靜態庫a有對靜態庫b的依賴性,並且可執行程序c同時鏈接了庫a和庫b,通常也不會出現符號重複定義的問題。

這是因爲靜態庫並沒有真正地“鏈接”其他靜態庫。當靜態庫a被編譯時,它可能需要庫b中的符號(例如函數或變量),但是庫b的代碼並沒有被合併到庫a中,庫a只是有對庫b的依賴性。

然後,當鏈接可執行程序c時,鏈接器將查看c以及所有傳遞的庫(在本例中爲ab)所需的所有符號,並從這些庫中拉取需要的對象文件來滿足這些需求。這意味着每個必要的符號只會被包含一次,而不會被重複包含。

請注意,雖然不會出現符號重複定義的問題,但可以會出現未解析的符號錯誤。這可能發生在庫b先於庫a被鏈接時。由於庫a依賴庫b,庫b應該在庫a之後鏈接,以便鏈接器能正確解析所有符號。所以正確的鏈接順序應該是:

target_link_libraries(c a b)

而不是:

target_link_libraries(c b a)

Q: 如果一個動態庫 a 鏈接了 b,這個時候可執行程序 c 鏈接了 ab,會導致 b 符號重複定義麼?

A:
在你的情況下,動態庫(Shared Library)a 依賴於另一個動態庫 b,而可執行程序c同時鏈接了ab,這通常不會導致符號重複定義的問題。

對於動態庫來說,它們在編譯時並不包含其依賴庫的代碼。相反,動態庫在運行時才解析它們的依賴關係。也就是說,如果動態庫a依賴於動態庫b,那麼當程序加載庫a時,操作系統(通過動態鏈接器)將負責加載庫b(如果尚未加載)。

因此,即使可執行程序c同時鏈接了ab,也只有一份庫b的實例被加載到內存中。庫ac中引用庫b的符號都將解析到這同一份實例,所以不會有符號重複定義的問題。

然而,請注意確保動態庫的正確路徑,特別是在運行可執行程序c時,庫ab必須能夠被找到。一般來說,這需要將它們放置在標準庫路徑下,或者修改環境變量(如LD_LIBRARY_PATH在Linux下,DYLD_LIBRARY_PATH在MacOS下),以包含這些庫的路徑。

Q: 如果一個動態庫a 鏈接了靜態庫 b,這個時候可執行程序 c 鏈接了 ab,會導致 b 符號重複定義麼?

A:
在你的情況下,動態庫 a 鏈接了靜態庫 b ,然後可執行程序 c 又鏈接了動態庫 a 和靜態庫 b 。這種情況可能會導致符號重複定義。

當一個動態庫(如 a )鏈接一個靜態庫(如 b )時,靜態庫 b 中的所有對象代碼實際上都會被包含到動態庫 a 中。所以當你分別將動態庫 a 和靜態庫 b 鏈接到可執行文件 c 時,因爲動態庫 a 已經包含了靜態庫 b 的所有代碼,所以相同的符號實際上被包含了兩次,這就可能導致符號重複定義。

解決這個問題的一種方法是隻在一個地方鏈接靜態庫 b。也就是說,要麼只在動態庫 a 中鏈接靜態庫 b(然後在可執行程序 c 中只鏈接動態庫 a ),要麼在動態庫 a 和可執行程序 c 中都不鏈接靜態庫 b(而是隻在鏈接可執行程序 c 時鏈接靜態庫 b )。這樣可以確保靜態庫 b 的代碼只被包含一次,從而避免符號重複定義。

--end--

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章