_ctypes.cpython-39-x86_64-linux-gnu.so:未定义符号:使用 dlopen 加载的嵌入式 Python 中的 PyFloat_Type

我在 ubuntu 20.04 中使用嵌入式 Python (3.9) 并尝试导入产生错误 _ctypes.cpython-39-x86_64-linux-gnu.so: undefined symbol: PyFloat_Type 的 ctypes。

我正在编译一个共享对象,它是使用 dlopen() 动态加载的。

CMake 用于构建共享对象。我这样声明 Python3 依赖项: find_package(Python3 REQUIRED COMPONENTS Development Development.Embed) 并使用 target_link_libraries(${target_name} Boost::filesystem Python3::Python) 链接

如果我理解正确,这会告诉 CMake 直接与 libpython3.9.so 链接(我也尝试明确声明链接到 libpython3.9.so ,但这并没有解决问题)。我确实看到 libpython3.9.so 导出 PyFloat_Type_ctypes.cpython-39-x86_64-linux-gnu.so 没有。

导入由 PyRun_SimpleString() 函数简单地完成: PyRun_SimpleString("import ctypes")

我应该说我在网上看到了一些解决方案,但没有一个有效(比如导出 LD_FLAGS="-rdynamic" ,但也没有帮助)。

我还应该指出,使用解释器(python3.9)导入效果很好。

这是 CMake 生成的构建命令: /usr/bin/c++ -fPIC -g -Xlinker -export-dynamic -shared -Wl,-soname,mytest.python3.so -o mytest.python3.so CMakeFiles/mytest.python3.dir/[mydir]/[myobjects].o /usr/lib/x86_64-linux-gnu/libboost_filesystem.so.1.71.0 /usr/lib/x86_64-linux-gnu/libpython3.9.so /usr/lib/x86_64-linux-gnu/libpython3.9.so

提前感谢您的帮助!

stack overflow _ctypes.cpython-39-x86_64-linux-gnu.so: undefined symbol: PyFloat_Type in embedded Python loaded with dlopen
原文答案
author avatar

接受的答案

在 Linux 上的 CPython 中导入 C 扩展时,会在后台使用 dlopen (默认情况下使用 RTLD_LOCAL -flag)。

C 扩展通常需要来自 Python 库 ( libpythonX.Y.so ) 的功能,例如 PyFloat_Type 。但是,在 Linux 上,C 扩展没有与 libpythonX.Y.so 链接(Windows 上的情况不同,请参阅 thisthis 了解更多详细信息) - 缺少的函数定义/功能将是由 python 可执行文件提供。

为了能够做到这一点,可执行文件必须与 -Xlinker -export-dynamic 链接,否则加载程序将无法将可执行文件中的符号用于使用 dlopen 加载的共享对象。

现在,如果嵌入的 python 不是可执行文件,而是一个共享对象,它本身加载了 dlopen ,我们需要确保它的符号被添加到动态表中。用 -Xlinker -export-dynamic 构建这个共享对象没有多大意义(毕竟它不是可执行文件)但不会破坏任何东西——重要的部分是如何使用 dlopen

为了使 text.python.so 中的符号对稍后使用 dlopen 加载的共享对象可见,应使用标志 RTLD_GLOBAL 打开它:

RTLD_GLOBAL 此共享对象定义的符号将可用于随后加载的共享对象的符号解析。

IE。

shared_lib = dlopen(path_to_so_file, RTLD_GLOBAL | RTLD_NOW);

警告: RTLD_LAZY 应该使用。

RTLD_LAZY 的问题是 C 扩展不依赖于 libpython (可以在 ldd 的帮助下看到),所以一旦它们被加载和一个符号(例如 PyFloat_Type ) 来自 libpython 的尚未解决的必须查找,动态链接器不知道它必须查找 libpython

另一方面,使用 RTLD_NOW ,所有符号都被解析并且在加载 C 扩展时可见(这与在使用 {JXM= 链接步骤期间链接 libpython 的“通常”情况相同) }),因此没有问题找到例如 -Xlinker -export-dynamic -符号。


只要使用 PyFloat_Type 加载嵌入式 python,就不需要使用 dlopen 构建/链接主可执行文件。

但是,如果主可执行文件与嵌入的 python-shared-object 链接,则 -Xlinker -export-dynamic 是必需的,否则在导入 c-extension 期间使用 -Xlinker -export-dynamic 时,python-symbols 应该是可见的。


有人可能会问,为什么 C 扩展首先不与 libpython 链接?

由于使用了 dlopen ,每个 C 扩展都会有自己的(未初始化的)Python 解释器版本(因为不会插入来自 libpython 的符号)并在使用时立即崩溃。

为了使它工作, RTLD_LOCAL 应该用 dlopen -flag 打开 - 但这不是一个合理的默认选项。


答案:

相关问题