由于缺少符号,动态加载Python C扩展失败

由于缺少符号,动态加载Python C扩展失败,python,c,plugins,shared-libraries,dynamic-loading,Python,C,Plugins,Shared Libraries,Dynamic Loading,我的情况是,我有一个可执行文件(main.c),它动态加载一个共享对象(py_plugin.c),而该对象又链接到python 但是,当python插件尝试导入依赖项未链接到libpython的模块时,我会遇到以下错误: ImportError: /usr/lib/python2.7/lib-dynload/bz2.x86_64-linux-gnu.so: undefined symbol: PyExc_SystemError 据我推断,这意味着库bz2.x86_64-linux-gnu.so

我的情况是,我有一个可执行文件(
main.c
),它动态加载一个共享对象(
py_plugin.c
),而该对象又链接到python

但是,当python插件尝试导入依赖项未链接到libpython的模块时,我会遇到以下错误:

ImportError: /usr/lib/python2.7/lib-dynload/bz2.x86_64-linux-gnu.so: undefined symbol: PyExc_SystemError
据我推断,这意味着库
bz2.x86_64-linux-gnu.so
无法访问python符号

请注意,这个错误是特定于“bz2”包的,因为我使用问题末尾的最小工作示例强制它浮出水面。在那里,我执行了“bz2”的显式导入,它加载python插件(
py_plugin.c
)中的库
bz2.x86_64-linux-gnu.so

查看依赖项,我验证:

  • bz2.x86_64-linux-gnu.so
    没有链接到python

        usr@cmptr $ ldd /usr/lib/python2.7/lib-dynload/bz2.x86_64-linux-gnu.so
        linux-vdso.so.1 =>  (0x00007ffd511fb000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f8c63a0a000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f8c6362a000)
        libbz2.so.1.0 => /lib/x86_64-linux-gnu/libbz2.so.1.0 (0x00007f8c6341a000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f8c63e33000)
    
  • 但是我的python插件

        usr@cmptr $ ldd py_plugin.so
        linux-vdso.so.1 =>  (0x00007ffc1ef5c000)
        libpython2.7.so.1.0 => /usr/lib/x86_64-linux-gnu/libpython2.7.so.1.0 (0x00007f56ac01c000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f56abc3c000)
        libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007f56aba1d000)
        libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007f56ab800000)
        libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007f56ab5fc000)
        libutil.so.1 => /lib/x86_64-linux-gnu/libutil.so.1 (0x00007f56ab3f8000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f56ab0a2000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f56ac79a000)
    
  • 我的问题如下:
  • 问题本身很明显,但为什么当我的插件明确链接到libpython时,python符号不可用

  • 当我可以而不是将python链接到主可执行文件(它是预编译的二进制文件)时,有人知道如何解决这个问题吗

  • 据我所知,错误的根源可能是python发行理念的不同(我运行的是基于Ubuntu的发行版)。这也突出了这个问题


    示例生成错误 生成libs/execs

    gcc $(pkg-config --cflags python) -shared -o py_plugin.so py_plugin.c $(pkg-config --libs python)
    gcc -o main main.c -lltdl
    
    我的系统上pkg config的putput为:

    pkg-config --cflags python
    -I/usr/include/python2.7 -I/usr/include/x86_64-linux-gnu/python2.7
    
    pkg-config --libs python
    -lpython
    
    文件:main.c

    #include <ltdl.h>
    #include <stdio.h>
    typedef int(*dyn_fptr)();
    
    int main()
    {
      if(lt_dlinit()) {
        return -1;
      }
    
      lt_dlhandle handle = lt_dlopen("./py_plugin.so");
    
      dyn_fptr func = (dyn_fptr)lt_dlsym(handle, "func");
      int a = func(); // <----------------------------------- Call "func" in py_plugin
    
      lt_dlclose(handle);
      return 0;
    }
    

    关闭线程以供将来参考

    正如在对我的问题的评论中指出的,这里的问题是Python(目前)在不同的Linux发行版上的打包方式不同

    为了解决这个问题,必须确保Python符号对Python插件中加载的库可见

    据我所知,有三种处理方法:

  • 将主可执行文件链接到Python
  • 设置
    RTDL_GLOBAL
    选项(仅当使用dlopen、
    dlopen(“lib.so”、RTDL_NOW | RTDL_LAZY | RTDL_GLOBAL)
    时才可能,并且不保证在所有系统上都受支持)
  • 设置环境变量
    LD_PRELOAD
    export LD_PRELOAD=“/path/to/libpython2.7.so.1.0”
    ,强制在任何其他库之前加载某些库。对我来说,这对我的小示例插件有效,但在处理更大的软件框架时会导致有害的交互

  • 也许,问题就像你链接的论坛所说的那样简单。Debian和由此衍生的发行版经常会破坏LSB,然后一切都以一种意想不到的方式进行了不同的布局。但我很好奇!为什么要从c调用python插件?另外,如果直接从python脚本导入bz2会发生什么情况?@IharobAlAsimi当我简单地导入“bz2”时,一切都正常,因为python可执行文件(静态)链接到libpython,因此模块可以直接从可执行文件访问符号。至于“为什么python来自C?”C库是为(科学)应用程序之间的可移植性而设计的,我们使用python执行典型的动态任务。想想C语言中的数据,只要它想做一些“花哨”的事情,就将自己映射到Python对象中。@IharobAlAsimi你说得很对,这个问题只是特定于Debian的,Fedora会按预期处理这个问题。
    #include <Python.h>
    
    int func()
    {
    
      Py_Initialize();
      PyObject *pName = PyString_FromString("bz2");
    
      PyObject *pModule = PyImport_Import(pName); // <--------------------- ERROR
      Py_DECREF(pName);
    
      if(!pModule) {
        PyErr_Print();
      }
    
      Py_Finalize();
    
      return 0;
    }