阅读了Executing Mach-O files的Apple文档后,它说:
The two-level namespace feature of OS X v10.1 and later adds the
module name as part of the symbol name of the symbols defined within
it. This approach ensures a module’s symbol names don’t conflict with
the names used in other modules.
所以在我的例子中,我将python2和python3加载到同一个进程中.两个Python库(默认情况下)都是使用两级命名空间选项编译的.两个库也通过dlopen(..)加载了RTLD_GLOBAL标志,因此具有相同名称的符号不应相互干扰,因为这两个模块具有不同的名称(python27和python36).
例:
<code>#include <{...}/include/python2.7/Python.h>
int main(int argc, const char * argv[])
{
auto* py3 = dlopen(".../python36", RTLD_GLOBAL | RTLD_NOW);
if (py3 == nullptr)
return 0;
auto* py2 = dlopen(".../python27", RTLD_GLOBAL | RTLD_NOW);
if (py2 == nullptr)
return 0;
auto* init = ((decltype(Py_Initialize)*)dlsym(py2, "Py_Initialize"));
if (init)
{
init();
}
return 0;
}
</code>
问题是,在python2导入/path/to/python2/lib/lib-dynload/_locale.so之后,python3的函数PyModule_GetDict被调用.这是为什么?怎么会发生这种情况?两级命名空间不应该阻止吗?
附: lib-dynload是一个在macOS上具有Python附加C模块的目录.我验证了python2环境中正确的_local.so lib被加载了.
编辑:
在做了一些实验之后,我看到第一个加载的python lib的符号总是获得更高的优先级,但不确定它是否适用于第一个加载的库或仍然是“未定义的行为域”.
调用python27的Py_Initialize() – 成功:
<code>1. Loading python27 first 2. Loading python36 second 3. PYTHONHOME to python27 4. cal Py_Initialize() of python27 </code>
调用python27的Py_Initialize() – 崩溃:
<code>1. Loading python36 first 2. Loading python27 second 3. PYTHONHOME to python27 4. cal Py_Initialize() of python27 </code>
我反过来得到了相同的结果.
调用python36的Py_Initialize() – 成功:
<code>1. Loading python36 first 2. Loading python27 second 3. PYTHONHOME to python36 4. cal Py_Initialize() of python36 </code>
调用python36的Py_Initialize() – 崩溃:
<code>1. Loading python27 first 2. Loading python36 second 3. PYTHONHOME to python36 4. cal Py_Initialize() of python36 </code>
解决方法:
正在正确解析libpython中的符号(例如libpython2.7.dylib).例如,在上面描述的场景中,我看到PyModule_GetDict()在错误解析的调用之前被调用了155次.
问题是python本身正在编译共享库,它正在使用dlopen()来加载它们.通过在运行时设置环境变量PYTHONVERBOSE,可以看到发生的dlopen():
<code>$PYTHONVERBOSE=1 ./main 2>&1 | grep dlopen </code>
产生:
<code>dlopen(".../lib/python2.7/lib-dynload/_locale.so", 2);
</code>
2参数对应于RTL_NOW,但这并不重要.问题是这个单独的库无法指示应该针对libpython2.7.dylib库解析它的符号.然而,它确实有几个Python符号;特别是,这个最终导致问题:
<code>$nm prefix/lib/python2.7/lib-dynload/_locale.so | grep GetDict
U _PyModule_GetDict
</code>
因此,当python dlopen()是库时,它所能做的就是解析没有限定条件的符号.显然,正如您所指出的,dl功能的语义是基于库的加载顺序来解析这些符号.
因此,在我们加载_locale.so之前一切正常,正如您从以下backtrace中看到的那样:
<code>* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x50)
* frame #0: 0x00000001003f3fc1 libpython3.6m.dylib`PyErr_FormatV [inlined] PyErr_Restore at errors.c:42 [opt]
frame #1: 0x00000001003f3fb7 libpython3.6m.dylib`PyErr_FormatV [inlined] PyErr_Clear at errors.c:355 [opt]
frame #2: 0x00000001003f3fb7 libpython3.6m.dylib`PyErr_FormatV(exception=0x00000001004cba18, format="%s:%d: bad argument to internal function", vargs=0x00007fff5fbfdcb0) at errors.c:841 [opt]
frame #3: 0x00000001003f2c39 libpython3.6m.dylib`PyErr_Format(exception=<unavailable>, format=<unavailable>) at errors.c:860 [opt]
frame #4: 0x0000000100358220 libpython3.6m.dylib`PyModule_GetDict(m=0x0000000101a5a868) at moduleobject.c:450 [opt]
frame #5: 0x00000001000f491c _locale.so`init_locale at _localemodule.c:703 [opt]
frame #6: 0x00000001018d1176 libpython2.7.dylib`_PyImport_LoadDynamicModule(name="_locale", pathname=".../lib/python2.7/lib-dynload/_locale.so", fp=<unavailable>) at importdl.c:53 [opt]
</code>
另外值得注意的是,_locale.so只是第一个失败的库.如果你以某种方式超越它,还有很多其他的库可能会在… / lib / python2.7 / lib-dynload中遇到类似的问题.
【说明】:本文章由站长整理发布,文章内容不代表本站观点,如文中有侵权行为,请与本站客服联系(QQ:254677821)!