当前位置:首页 > 操作系统 > MacOs

python – macOS上的RTLD_GLOBAL和两级命名空间

阅读了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)!

相关教程推荐

其他课程推荐