gpt4 book ai didi

python - 尽管使用两级命名空间编译,库仍期望平面命名空间中的符号

转载 作者:搜寻专家 更新时间:2023-10-31 02:06:59 25 4
gpt4 key购买 nike

我使用 dlopenRTLD_LOCAL 动态加载 Python,以避免与另一个库发生冲突,该库恰好包含一些同名符号。使用 Xcode 在 macOS 上执行上面的 MVCE 失败,因为它需要全局命名空间中的 _PyBuffer_Type

Traceback (most recent call last):
File "...lib/python2.7/ctypes/__init__.py", line 10, in <module>
from _ctypes import Union, Structure, Array
ImportError: dlopen(...lib/python2.7/lib-dynload/_ctypes.so, 2):
Symbol not found: _PyBuffer_Type
Referenced from: ...lib/python2.7/lib-dynload/_ctypes.so
Expected in: flat namespace
in ...lib/python2.7/lib-dynload/_ctypes.so
Program ended with exit code: 255

但是为什么? RTLD_LOCAL 是否覆盖二级命名空间?

我使用 otool -hV 检查 _ctypes.so 是否使用两级命名空间选项编译。据我了解,符号解析需要库名+符号名本身。为什么它期望平面命名空间中的 _PyBuffer_Type 和/或为什么找不到它?向右滚动查看 TWOLEVEL

> otool -hV /System/Library/Frameworks/Python.framework/Versions/2.7/lib/python2.7/lib-dynload/_ctypes.so
Mach header
magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
MH_MAGIC_64 X86_64 ALL 0x00 BUNDLE 14 1536 NOUNDEFS DYLDLINK TWOLEVEL

知道这里发生了什么吗?

MVCE

可以拷贝到一个新的Xcode工程中,简单的编译执行。

#include </System/Library/Frameworks/Python.framework/Versions/2.7/include/python2.7/Python.h>
#include <dlfcn.h>

int main(int argc, const char * argv[])
{
auto* dl = dlopen("/System/Library/Frameworks/Python.framework/Versions/2.7/Python", RTLD_LOCAL | RTLD_NOW);
if (dl == nullptr)
return 0;

// Load is just a macro to hide dlsym(..)
#define Load(name) ((decltype(::name)*)dlsym(dl, # name))

Load(Py_SetPythonHome)("/System/Library/Frameworks/Python.framework/Versions/2.7");
Load(Py_Initialize)();

auto* readline = Load(PyImport_ImportModule)("ctypes");
if (readline == nullptr)
{
Load(PyErr_Print)();
dlclose(dl);
return -1;
}

Py_DECREF(readline);
Load(Py_Finalize)();
return 0;
}

最佳答案

这个问题和你的相关RTLD_GLOBAL question两者都涉及动态加载器解析其加载的共享库中 undefined symbol 的语义。我希望找到一个明确的文档引用来解释你所看到的,但我一直没能做到。尽管如此,我还是可以通过观察来解释正在发生的事情。

如果我们以冗长的方式运行,我们可以看到 python 库在失败之前尝试加载两个共享库:

bash-3.2$ PYTHONVERBOSE=1 ./main 2>&1 | grep -i dlopen
dlopen(".../python2.7/lib-dynload/_locale.so", 2);
dlopen(".../python2.7/lib-dynload/_ctypes.so", 2);

鉴于第一个成功,我们知道动态加载器通常会根据调用库的 namespace 解析 undefined symbol 。事实上,正如您在其他问题的评论中指出的那样,这甚至在有两个版本的 python 库时也有效,即 python 库完成的 dlopen() 解决了各自的问题 namespace 。到目前为止,这听起来正是您想要的。但是,为什么 _ctypes.so 加载失败?

我们知道 _PyModule_GetDict 是导致 _locale.so 加载失败的符号;而且它显然在这里有效。我们还知道符号 _PyBuffer_Type 在这里失败了。这两个符号有什么区别?在 python 库中查找它们:

bash-3.2$ nm libpython2.7.dylib | grep _PyModule_GetDict
00000000000502c0 T _PyModule_GetDict
bash-3.2$ nm libpython2.7.dylib | grep _PyBuffer_Type
0000000000154f90 D _PyBuffer_Type

_PyModule_GetDict 是一个 Text(代码)符号,而 _PyBuffer_Type 是一个 Data 符号。

因此,根据这个经验数据,我怀疑动态加载器将根据调用库的 RTLD_LOCAL 代码符号解析 undefined symbol ,而不是 RTLD_LOCAL 数据符号。也许有人可以指出一个明确的引用。

关于python - 尽管使用两级命名空间编译,库仍期望平面命名空间中的符号,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49393611/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com