代码之家  ›  专栏  ›  技术社区  ›  Sigve Karolius

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

  •  -1
  • Sigve Karolius  · 技术社区  · 6 年前

    我的情况是我有一个可执行文件( 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”,加载库 bz2.x86\u 64-linux-gnu。所以 ,位于python插件内部( py\u插件。c ).

    查看依赖关系,我验证:

    1. 图书馆 bz2.x86\u 64-linux-gnu。所以 链接到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)
      
    2. 但是我的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)
      

    我的问题如下:

    1. 问题本身很明显,但为什么当我的插件明确链接到libpython时,python符号不可用?

    2. 如果我可以的话,有人知道如何解决这个问题吗 将python链接到主可执行文件(它是预编译的二进制文件)?

    从…起 this e-mail thread 我理解错误的根源可能是python发行理念的不同(我运行的是基于Ubuntu的发行版)。这 bug report 还强调了这个问题。


    示例生成错误

    生成libs/exec

    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的输入为:

    pkg-config --cflags python
    -I/usr/include/python2.7 -I/usr/include/x86_64-linux-gnu/python2.7
    
    pkg-config --libs python
    -lpython
    

    文件: 主要的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;
    }
    

    文件: py\u插件。c

    #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;
    }
    
    1 回复  |  直到 6 年前
        1
  •  1
  •   Sigve Karolius    6 年前

    关闭线程以供将来参考。

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

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

    据我所知,有三种方法可以解决这个问题:

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