動態庫鏈接踩坑記

問題

最近,在一系列的編寫代碼、編譯、正確的形成可執行文件後,將可執行文件運行在生成環境時,出現了應該是在編譯鏈接時最常見的錯誤:Symbol not defined。下面,將會逐一的從問題是如何產生,以及如何解決進行說明,就當做是自我的一個問題記錄。

原因

對於生產環境出現問題的可執行文件A,執行命令 nm A 發現的確存在着報錯的時候的函數有着標誌U(表明該函數未定義),可此函數在可執行文件A中並沒有任何的聲明,這隻能說明,該函數是來自於第三方的庫文件,經過檢查發現,的確存在了一個動態庫文件中使用了此函數。同樣的使用命令 nm A 查看發現該函數同樣在該動態庫中未定義。
起初,我們認爲是別人提供的這個動態庫.so不正確,因此,我們重新編譯了這個動態庫後,再重新放入生成環境,再次執行,同樣的錯誤繼續出現。這時候,很自然而然的我們懷疑到了應該是編譯動態庫的make文件出現了錯誤。
在仔細認真的檢查後make文件後,發現在編譯形成動態庫時,其最終形成目標集的命令類似下面的命令

> gcc -shared -fPIC libxxx.a xxxx.o -o libxxxx.so
如果認真的看,會發現該動態庫還鏈接了一個其他的靜態庫,而那個未定義符號函數正是在這個靜態庫中聲明定義的。很明顯,在編譯動態庫的時候,由於採用gcc的編譯器的版本查找符號是從前向後進行查找,而.o文件引用了前面.a文件,因此,.o文件 並沒有真正的鏈接到正確的函數符號。而在這個過程中,也並沒有報告錯誤,而是順利的產生了動態庫文件,這也就間接的導致了上述問題的發生。

解決

在gcc編譯動態庫的時候,即使出現了符號未正確的鏈接的情況,鏈接器也只是跳過該錯誤進行執行,這是由於gcc爲了某種原因允許了這種情況(具體的原因,大家可以上網查閱,在此就不贅述了),因此,gcc編譯器提供了下面的一些參數用於告知鏈接器對此種情況進行錯誤報告(很多的類似參數,區別建議大家去查閱gcc的編譯手冊)

-Wl,–no-undefined
-Wl,–unresolved-symbols=ignore-in-shared-libs
至此,解決上述的問題,我們可以將靜態庫文件和目標文件換個位置便可解決,但是我們還是建議大家在編譯動態庫的時候加入上述的參數,避免此類問題的出現。

總結

對於一個後端工程師來說,編譯、鏈接、ELF文件是一項必不可少的知識,這裏給出一些常用的命令,這些命令都可以幫助大家在碰到此類問題時候不會兩眼一抹黑。具體的用法,去man一下吧。

ldd –查看可執行文件鏈接那些動態庫
nm –查看可執行文件,庫文件符號表
readelf –查看elf文件的文件信息
objdump –顯示目標文件的信息

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