Ubuntu20.04下dlopen加载嵌入式Python3.9导入ctypes报PyFloat_Type错误
问题根因
该错误的核心是dlopen加载你的共享对象时,默认不会将共享对象依赖的libpython3.9.so的符号导出到全局符号表。ctypes依赖的扩展模块_ctypes.cpython-39-x86_64-linux-gnu.so加载时会从全局符号表查找PyFloat_Type这类Python核心符号,找不到就会抛出未定义符号错误。你直接运行官方python3.9解释器时,解释器进程启动时会提前将libpython的符号加载到全局符号表,因此导入ctypes正常。
你之前尝试的-rdynamic参数仅用于导出当前编译目标自身的符号,无法将其依赖的第三方库符号导出到全局,因此无法解决该问题。
可行解决方案
按改造成本从低到高排序:
- 调整dlopen加载参数:加载你的
mytest.python3.so时,在flag中添加RTLD_GLOBAL,将当前库及依赖库的所有符号导出到全局符号表,示例代码:
该方案不需要修改现有CMake配置,改造成本最低。void* handle = dlopen("mytest.python3.so", RTLD_NOW | RTLD_GLOBAL); - 调整CMake链接配置:如果你无法修改dlopen的调用逻辑,可以在CMakeLists.txt中给你的目标添加链接标记,强制加载所有libpython符号:
该参数会强制链接器不做按需符号裁剪,将set_target_properties(${target_name} PROPERTIES LINK_FLAGS "-Wl,--no-as-needed")libpython3.9.so的所有符号都纳入导出范围。 - 手动预加载libpython:如果前两种方案都不适用,可以在初始化Python解释器、加载你的共享对象之前,手动预加载
libpython3.9.so并开启全局符号导出:// 提前加载libpython到全局符号表 dlopen("/usr/lib/x86_64-linux-gnu/libpython3.9.so", RTLD_NOW | RTLD_GLOBAL); // 后续再执行dlopen加载你的共享对象、初始化Python的逻辑
验证方法
修改完成后可通过以下方式确认修复效果:
- 执行
nm -D mytest.python3.so | grep PyFloat_Type确认符号已导出 - 运行程序时添加环境变量
LD_DEBUG=symbols,查看PyFloat_Type的查找路径是否匹配libpython3.9.so
内容的提问来源于stack exchange,提问作者TCS
相关产品推荐
相关产品推荐

