c++靜態庫鏈接順序引發的bug

最近做個OpenCV的項目,搞了半個月一直解決不了,最後發現是靜態庫鏈接順序的問題

問題描述以及解決辦法

最近用emsdk將c/c++轉到wasm,用到emsdk裏面./emcc來編譯c/c++,注意emsdk自帶的clang版本是clang3.x

# Makefile
-lopencv_core -lopencv_highgui -lopencv_imgproc

編譯後出現一下錯誤,很多網友也有此問題Building with opencv works well until I use cv::resizeopencv-3-4-3-in-emscripten-cannot-work

error: undefined symbol: _Z7cvFloorRKN2cv10softdoubleE
warning: To disable errors for undefined symbols use `-s ERROR_ON_UNDEFINED_SYMBOLS=0`
error: undefined symbol: _Z7cvRoundRKN2cv10softdoubleE
error: undefined symbol: _Z9cvRound64RKN2cv10softdoubleE
error: undefined symbol: _ZN2cv10softdoubleC1Ei
error: undefined symbol: _ZN2cv10softdoubleC1Ex
error: undefined symbol: _ZNK2cv10softdoubledvERKS0_
error: undefined symbol: _ZNK2cv10softdoublemiERKS0_
error: undefined symbol: _ZNK2cv10softdoublemlERKS0_
error: undefined symbol: _ZNK2cv10softdoubleplERKS0_
Error: Aborting compilation due to previous errors

一直無法用cvresize函數,很是奇怪,最後無意發現一個鏈接 undefined reference to cv::softdouble::operator/,試着調整了一下靜態庫的鏈接順序,改爲

# Makefile
-lopencv_highgui -lopencv_imgproc -lopencv_core 

編譯成功!

Why

  • 傳統的Unix編譯環境下, 靜態庫的加載是順序搜索一遍, 遇到沒鏈接的函數就記下, 如果這個函數在後面的庫或obj中出現了, 鏈接器就會把這個函數所在的obj鏈接過去, 不包含未鏈接函數的obj就會被忽略過去(靜態庫也只是obj的簡單打包而已).
  • 因爲lopencv_imgproc依賴lopencv_core,而當lopencv_imgproc中的cv::resize()需要lopencv_core的cvFloor時已經不可用了,很腦殘的設計,不知道爲什麼在前面鏈接過就失效了
  • 所以如果庫A依賴庫B, 鏈接的順序就應該寫爲A B, 如果相互依賴就應該爲A B A或者B A B的順序

而我在本地用clang時這個順序就沒啥問題,而且caffe提供的順序也是lopencv_core在前面,我猜是後面高版本的gcc/clang都已經“修復”了這個傻傻的設計。

大多現代的編譯鏈都不會有這個問題, 但是傳統, 標準以及我手裏的這個編譯鏈(emsdk的低版本clang)卻都是這樣的, 爲了通用, 以後還是注意點吧.


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