Ubuntu20.04下pybind11调用C++库出现已定义共享库符号未找到错误
问题背景
在Ubuntu 20.04系统上开发通过pybind11封装、供Python 3.8调用的C++代码,运行时出现如下报错:
Traceback (most recent call last): snip... ImportError: <full-path>/<lib-name-A>.so: undefined symbol: <mangled symbol name>
已排查确认两点:
- 执行
ldd <lib-name-A.so路径>,输出显示该符号所属的依赖库libB的绝对路径无异常 - 执行
nm -s --demangle <libB路径>,可以看到该符号定义在text段,且其修饰名(mangled name)与报错信息中的完全一致
可能的故障原因
- 符号可见性配置问题
C++编译时默认符号隐藏,若libB编译时开启了-fvisibility=hidden参数,且该符号没有显式添加__attribute__((visibility("default")))导出标记,即使本地符号表(nm默认输出)可以查到该符号,动态链接器也无法识别到该导出符号。可执行nm -D <libB路径>查看动态符号表,若该表中无对应符号即可确认是此问题。 - 编译时链接参数顺序错误
ld链接器按从左到右的顺序处理链接参数,仅会加载当前已出现的未定义符号对应的库内容。如果编译libA时-lB参数放在了依赖该符号的目标文件之前,处理-lB时还没解析到libA的未定义符号,就会跳过加载libB的对应符号,最终导致libA的符号未定义。需调整参数顺序,将-lB放在所有依赖它的目标文件之后。 - ABI不兼容
libA和libB编译时使用了不一致的ABI相关编译参数,比如一个开启了_GLIBCXX_USE_CXX11_ABI=0使用旧版C字符串/列表ABI,另一个使用默认的新版ABI,或使用了不同的C标准、异常/RTTI开关等,即使符号修饰名完全一致,也会出现链接失败问题。 - 运行时加载的libB版本不匹配
ldd输出的是编译时记录的依赖路径,运行时动态链接器可能因为LD_LIBRARY_PATH优先级、ld.so.cache缓存、Pythonsys.path下存在同名libB等原因,加载了另一个版本的libB。可在执行Python脚本前添加LD_DEBUG=libs环境变量,查看实际加载的libB路径,与你校验的libB路径对比确认。 - 符号版本不匹配
若libB存在多版本符号定义,libA编译时链接的libB符号版本与运行时加载的libB符号版本不一致,即使符号名相同也会报错。可执行objdump -x <libA路径> | grep NEEDED查看libA依赖的libB版本,与运行时加载的版本做对比。
内容的提问来源于stack exchange,提问作者Uri Raz
相关产品推荐
相关产品推荐

