_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
-标志打开 - 但这不是一个合理的默认选项。
我在 ubuntu 20.04 中使用嵌入式 Python (3.9) 并尝试导入产生错误 _ctypes.cpython-39-x86_64-linux-gnu.so: undefined symbol: PyFloat_Type
.
我正在编译一个共享对象,它是使用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 上情况有所不同,请参阅
为了能够这样做,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
-标志打开 - 但这不是一个合理的默认选项。