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

_ctypes.cpython-39-x86_64-linux-gnu.so: undefined symbol: PyFloat_Type in embedded Python loaded with dlopen

我在 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) 和 link 使用 target_link_libraries(${target_name} Boost::filesystem Python3::Python)

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

导入仅由 PyRun_SimpleString() 函数完成:PyRun_SimpleString("import ctypes").

我应该声明我在网上看到了一些解决方案,但其中 none 有效(例如导出 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

提前感谢您的帮助!

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

C 扩展通常需要来自 Python 库 (libpythonX.Y.so) 的功能,例如 PyFloat_Type。但是,在 Linux 上,C 扩展没有链接到 libpythonX.Y.so(在 Windows 上情况有所不同,请参阅 or 了解更多详细信息)- 缺少 function-definition/functionality 将由 python-executable.

提供

为了能够这样做,executable 必须与 -Xlinker -export-dynamic 链接,否则加载程序将无法使用 executable 中的符号使用 dlopen.

加载的共享对象

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

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

打开它

RTLD_GLOBAL The symbols defined by this shared object will be made available for symbol resolution of subsequently loaded shared objects.

shared_lib = dlopen(path_to_so_file, RTLD_GLOBAL | RTLD_NOW);

警告: RTLD_LAZY 应该 被使用。

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

另一方面,对于 RTLD_NOW,所有符号都被解析并且在加载 C 扩展时可见(这与 libpython 时的“通常”情况相同)在链接步骤中与 -Xlinker -export-dynamic) 链接在一起,因此没有问题发现例如PyFloat_Type-符号.


只要嵌入式python加载dlopen,主executable不需要built/linked和-Xlinker -export-dynamic

但是,如果主 executable 链接到嵌入式-python-共享对象,则 -Xlinker -export-dynamic 是必需的,否则 python-符号需要在导入 c 扩展期间使用 dlopen 时可见。


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

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

要使其正常工作,dlopen 应使用 RTLD_GLOBAL-标志打开 - 但这不是一个合理的默认选项。