背景
開發多個動態庫鏈接在一起的程序,編譯生成動態鏈接庫后,調用時出現 "undefined symbol" 問題:
#011加載動態庫 /backupsoft/AnyBackupServer/EOSSService/components/libeossdataservice.so 失敗,錯誤原因:/backupsoft/AnyBackupServer/EOSSService/components/libeossdataservice.so: undefined symbol: _ZN6ncMaskD1Ev
定位與解決方法
(1)使用 nm 命令:確認是否真的有未定義的函數,導致在函數符號表中找不到
1 [root@model components]# nm -u libeossdataservice.so | grep _ZN6ncMaskD1Ev 2 U _ZN6ncMaskD1Ev
(2)使用file 命令查看 so庫的架構,看看是否與平台一致
可以看到,當前so庫架構為x86-64,可以在GNU/Linux平台下使用。平台與架構一致
[root@model components]# file libeossdataservice.so
libeossdataservice.so: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, BuildID[sha1]=c43127eb5590ae5238f9f04a7dc530a8acfe586d, not stripped
接下來,需要定位一下 undefined symbol的具體信息
(3)通過 ldd -r xxx.so 命令查看so庫鏈接狀態和錯誤信息
ldd命令,可以查看對應的可執行文件或庫文件依賴哪些庫,但可執行文件或庫文件要求與操作系統的編譯器類型相同,即電腦是X86的GCC編譯器,那么無法通過ldd命令查看ARM交叉編譯器編譯出來的可執行文件或庫文件。
如果想在Ubuntu等Linux宿主機上查看ARM交叉編譯好的可執行程序和庫文件的相關依賴關系,可以通過以下命令:
readelf -d xxx.so | grep NEEDED
[root@model components]# ldd -r libeossdataservice.so
linux-vdso.so.1 => (0x00007ffce43fa000)
libimmtrans.so => /backupsoft/AnyBackupServer/EOSSService/libimmtrans.so (0x00007f20123fe000)
libmask.so => /backupsoft/AnyBackupServer/EOSSService/libmask.so (0x00007f20121fc000)
libesafec.so => /backupsoft/AnyBackupServer/EOSSService/libesafec.so (0x00007f2011fe9000)
libbasecore.so => /backupsoft/AnyBackupServer/EOSSService/libbasecore.so (0x00007f2011c05000)
libappcore.so => /backupsoft/AnyBackupServer/EOSSService/libappcore.so (0x00007f20118ed000)
libkeymgm.so => /backupsoft/AnyBackupServer/EOSSService/libkeymgm.so (0x00007f20116cb000)
libstdc++.so.6 => /lib64/libstdc++.so.6 (0x00007f20113c4000)
libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f20111ae000)
libc.so.6 => /lib64/libc.so.6 (0x00007f2010de0000)
libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f2010bc4000)
libdl.so.2 => /lib64/libdl.so.2 (0x00007f20109c0000)
librt.so.1 => /lib64/librt.so.1 (0x00007f20107b8000)
librecrypto.so => /backupsoft/AnyBackupServer/EOSSService/librecrypto.so (0x00007f20103a9000)
/lib64/ld-linux-x86-64.so.2 (0x00007f2012a19000)
libressl.so => /backupsoft/AnyBackupServer/EOSSService/libressl.so (0x00007f2010148000)
libm.so.6 => /lib64/libm.so.6 (0x00007f200fe46000)
undefined symbol: _ZN6ncMaskD1Ev (./libeossdataservice.so) // 擴展名D1Ev是C++ 析構函數
undefined symbol: _ZN6ncMaskC1Ev (./libeossdataservice.so) // 擴展名C1Ev是C++ 構造函數
[root@model components]#
可以看到有兩個 undefined symbol ,其中就有提到的 _ZN6ncMaskD1Ev 和 _ZN6ncMaskC1Ev 錯誤
(4)使用 c++filt <symbol> 定位錯誤在那個C++文件中:c++filt 可查看函數被 name mangling (名字修飾) 前的函數名字符
1 [root@model components]# c++filt _ZN6ncMaskD1Ev 2 ncMask::~ncMask() 3 4 [root@model components]# c++filt _ZN6ncMaskD1Ev 5 ncMask::~ncMask()
原因總結
1.檢查Makefile/CMakeList.txt,查看是否包含所有需要包含的文件/文件夾。 錯誤原因:有時候由於新添加了一個文件/文件夾,而該文件/文件夾又沒有被Makefile/CMakeList.txt掃描到,這時候就會在實際運行時出現undefined symbol,原因是編譯時找到了對應的頭文件,卻在鏈接時未找到需要的頭文件。
2.查看對應函數在頭文件和Cpp文件中是否有相同的函數結構。 錯誤原因:有時候往往是先把一堆頭文件先寫好,結果在寫實現時容易忘記寫一兩個函數實現,然后就容易編譯過了,但鏈接時沒過,因為鏈接時需要把頭文件和Cpp文件鏈接在一起。