问题大概率是动态库未导出目标符号:用nm -D lib.so | grep func或objdump -T lib.so | grep func确认符号是否在.dynsym中;若无输出,检查是否启用-fvisibility=hidden、C++ name mangling不匹配或RPATH导致加载了错误版本库。

用 nm -D 或 objdump -T 直接看动态库导出了哪些符号
运行时报 symbol not found,但你确认库文件存在、路径也对,问题大概率出在“库根本没把你要的函数暴露出来”。nm -D(或等价的 objdump -T)能直接读取动态符号表 .dynsym,只显示运行时可见的符号,不被干扰。
-
nm -D libmylib.so | grep MyFunction—— 如果没输出,说明该符号未导出 -
objdump -T libmylib.so | grep MyFunction—— 输出中若无T(全局函数)或D(全局变量)标记,也说明不可见 - 注意区分:
nm默认查.symtab(可能含调试符号),加-D才查.dynsym,这才是动态链接器真正能看到的
检查编译时是否用了 -fvisibility=hidden 或版本脚本
很多项目为减少符号污染,默认开启 -fvisibility=hidden,这时所有符号默认不导出,必须显式标注 __attribute__((visibility("default"))) 才能被外部调用。如果忘了加,就变成“函数写了,但藏起来了”。
- 查编译命令历史或
Makefile:搜-fvisibility、visibility、version-script - 有版本脚本(
.map文件)时,readelf -V libmylib.so可看是否限制了导出范围 - C++ 类成员函数容易被忽略:即使类声明加了 visibility default,构造/析构/模板实例化仍可能因编译器行为未导出,需额外验证
确认符号名是否被 C++ name mangling 扭曲
报错里出现一长串下划线开头的符号名(如 _ZN7MyClass9DoWorkEv),基本是 C++ 函数被修饰过。但你的头文件声明和实现若不一致(比如头文件用 extern "C" 而实现没加),或者链接时混用了 C/C++ 约定,就会导致“名字对不上”。
- 用
c++filt _ZN7MyClass9DoWorkEv还原成可读名,确认是否真对应你要调用的函数 - 如果头文件里声明为
extern "C",实现也必须加,否则导出的是 mangled 名,而调用方找的是 unmangled 名 - 检查
nm -D libmylib.so输出中是否同时存在 mangled 和 unmangled 形式 —— 若只有一种,说明约定不匹配
readelf -d 查 RPATH/RUNPATH 是否意外覆盖了正确库
有时候符号明明在 A 库里,但程序却加载了 B 库(同名但旧版),因为 RPATH 指向了一个错误目录,而它优先级高于 LD_LIBRARY_PATH 和系统路径。
-
readelf -d your_program | grep -E 'RPATH|RUNPATH'—— 看是否硬编码了某个路径 -
ls -l /path/from/rpath/libmylib.so—— 确认该路径下库的修改时间、大小,是否真为你期望的版本 - 若发现 RPATH 指向测试环境或旧构建产物,需重新链接:加
-Wl,-rpath,$ORIGIN/../lib或彻底清除:-Wl,--no-as-needed -Wl,-rpath,
ldd -r 定位哪个符号缺失 → 再用 nm -D 查目标库是否导出 → 若没导出,回头检编译选项和 C++ 语言约定 → 若导出了但还是找不到,立刻查 RPATH 和实际加载的库文件是否一致。最容易被跳过的,是以为“库文件放对了就行”,却没意识到动态链接器根本没加载你认为的那个文件。

















