_ctypes.cpython-39-x86_64-linux-gnu.so:未定义符号:使用 dlopen 加载的嵌入式 Python 中的 PyFloat_Type
Posted
技术标签:
【中文标题】_ctypes.cpython-39-x86_64-linux-gnu.so:未定义符号:使用 dlopen 加载的嵌入式 Python 中的 PyFloat_Type【英文标题】:_ctypes.cpython-39-x86_64-linux-gnu.so: undefined symbol: PyFloat_Type in embedded Python loaded with dlopen 【发布时间】:2021-06-08 16:43:41 【问题描述】:我在 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
提前感谢您的帮助!
【问题讨论】:
我已经通过在 dl_open 中使用 RTLD_NOW|RTLD_GLOBAL 加载我的共享对象解决了这个问题 【参考方案1】:在 Linux 上的 CPython 中导入 C 扩展时,在后台使用 dlopen
(默认情况下使用 RTLD_LOCAL
-flag)。
C 扩展通常需要来自 Python 库 (libpythonX.Y.so
) 的功能,例如 PyFloat_Type
。但是,在 Linux 上,C 扩展名没有链接到 libpythonX.Y.so
(Windows 上的情况不同,请参阅 this 或 this 了解更多详细信息) - 缺少的函数定义/功能将由python-可执行文件。
为了能够做到这一点,可执行文件必须与-Xlinker -export-dynamic
链接,否则加载程序将无法将可执行文件中的符号用于使用dlopen
加载的共享对象。
现在,如果嵌入的python不是可执行文件,而是一个共享对象,它本身加载了dlopen
,我们需要确保它的符号被添加到动态表中。使用-Xlinker -export-dynamic
构建这个共享对象没有多大意义(毕竟它不是可执行文件)但不会破坏任何东西——重要的部分是如何使用dlopen
。
为了使text.python.so
中的符号对稍后使用dlopen
加载的共享对象可见,应使用标志RTLD_GLOBAL
打开它:
RTLD_GLOBAL 此共享对象定义的符号将被制作 可用于随后加载的符号解析 共享对象。
即
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 扩展时可见(这与在使用 @ 链接步骤期间链接 libpython 的“通常”情况相同) 987654347@),因此没有问题,例如PyFloat_Type
-符号。
只要用dlopen
加载嵌入式python,就不需要用-Xlinker -export-dynamic
构建/链接主可执行文件。
但是,如果主可执行文件与嵌入式python共享对象链接,则-Xlinker -export-dynamic
是必需的,否则在导入c-extension期间使用dlopen
时,python-symbols希望可见。
有人可能会问,为什么 C 扩展首先不与 libpython 链接?
由于使用了RTLD_LOCAL
,每个 C 扩展都会有自己的(未初始化的)Python 解释器版本(因为不会插入来自 libpython 的符号)并在使用时立即崩溃。
为了使它工作,dlopen
应该使用RTLD_GLOBAL
-flag 打开 - 但这不是一个合理的默认选项。
【讨论】:
dlopen( ) 不需要 RTLD_LAZY 或 RTLD_NOW 吗?哪个应该与 RTLD_GLOBAL 进行 OR'd? @Jiminion 我已经添加了显式调用(使用 RTLD_GLOBAL)以及为什么不能使用 RTLD_LAZY 的信息。以上是关于_ctypes.cpython-39-x86_64-linux-gnu.so:未定义符号:使用 dlopen 加载的嵌入式 Python 中的 PyFloat_Type的主要内容,如果未能解决你的问题,请参考以下文章
如何限制 Nativescript 仅针对 x86_64 进行编译?