问题描述
我使用的是ubuntu 20.04中的Embedded Python(3.9),尝试导入ctype时出现错误_ctypes.cpython-39-x86_64-linux-gnu.so: undefined symbol: PyFloat_Type
。
我正在编译共享对象,它是使用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")
即可完成。
我应该声明,我在Web上看到了一些解决方案,但没有一个奏效(如导出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)。
libpythonX.Y.so
)的功能,例如PyFloat_Type
。然而,在Linux上,C扩展没有链接到libpythonX.Y.so
(Windows上的情况不同,有关更多详细信息,请参阅this或this)--缺少的函数定义/功能将由python可执行文件提供。
若要执行此操作,可执行文件必须与-Xlinker -export-dynamic
链接,否则加载程序将无法将可执行文件中的符号用于通过dlopen
加载的共享对象。
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
看到),因此一旦它们被加载并且来自libpython
的尚未解析的符号(例如PyFloat_Type
)必须被查找,动态链接器不知道它必须查找libpython
。
另一方面,使用RTLD_NOW
时,所有符号都被解析并在加载C扩展时可见(这与在与-Xlinker -export-dynamic
的链接步骤中链接libpython时的情况相同),因此不会出现查找问题,例如PyFloat_Type
-符号。
只要用dlopen
加载嵌入的python,就不需要用-Xlinker -export-dynamic
构建/链接主可执行文件。
但是,如果主可执行文件是针对Embedded-python-Shared-Object链接的,则-Xlinker -export-dynamic
是必需的,否则在导入c-扩展时使用dlopen
时,希望可以看到python符号。
有人可能会问,为什么不首先将C扩展与libpython联系起来?
由于使用了RTLD_LOCAL
,每个C扩展都会有自己的(未初始化的)版本的Python解释器(因为不会插入来自libpython的符号),并且一旦使用就会崩溃。
若要使其正常工作,dlopen
应使用RTLD_GLOBAL
标志打开-但这不是合理的默认选项。
这篇关于_ctyes.cpython-39-x86_64-linux-gnU.S.so:未定义的符号:使用dlopen加载的Embedded Python中的PyFloat_Type的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!