库期望平面命名空间中的符号,尽管使用两级命名空间编译
Library expects symbol in flat namespace although compiled with two-level namespace
我用dlopen
和RTLD_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问题都涉及动态加载器解析它加载的共享库中未定义的符号的语义。我希望找到一个明确的文档参考来解释你所看到的内容,但我无法做到。尽管如此,我可以做一个观察,也许可以解释正在发生的事情。
如果我们以冗长的方式运行,我们可以看到 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);
鉴于第一个成功,我们知道通常动态加载器会根据调用库的命名空间解析未定义的符号。事实上,正如您在另一个问题的评论中指出的那样,当有两个版本的 python 库时,这甚至有效,即 python 库完成的dlopen()
针对它们各自的命名空间进行解析。到目前为止,这听起来正是您想要的。但是,为什么_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
代码符号解析未定义的符号,而不是RTLD_LOCAL
数据符号。也许有人可以指出一个明确的参考。
- 如何使用 soong 命名空间来有条件地编译模块
- 使用 make 编译 MPI,几个命名空间错误,例如"错误:未知类型名称'使用'?
- 无法获取 wig %ignore 以忽略命名空间中的类,从而导致编译错误
- C++ 使用命名空间时,模板函数无法使用 g++ 进行编译
- 非命名空间范围内的显式专用化不会在 GCC 中编译
- 库期望平面命名空间中的符号,尽管使用两级命名空间编译
- 在命名空间中使用自己的类比较 std::vector 不会编译
- 使用命名空间别名编译 OpenCV
- 包含命名空间的类模板的前向声明会导致编译错误
- C2653:不是没有预编译标头的类或命名空间
- 每个编译单元是否只有一个未命名的命名空间
- 命名空间 X 中具有类 X 的模板类 Y 无法在 VS2010 中编译
- 是否可以在编译时获得一个包含命名空间和类名的字符串
- C++中的编译错误可能是命名空间问题
- clang 3.3 可以在 VS 2010 中使用命名空间编译代码
- 为什么 rand() 在不包含 cstdlib 或使用命名空间 std 的情况下编译
- 什么是“使用命名空间 std;”,为什么我需要它来编译具有最新C++编译器的程序
- C++ 全局命名空间编译问题
- 为什么结构必须与模板类位于同一命名空间中才能编译
- 命名空间编译问题