【问题标题】:Dynamic loading of Python C extension fails due to missing symbols由于缺少符号,Python C 扩展的动态加载失败
【发布时间】:2018-01-26 13:59:30
【问题描述】:

我的情况是我有一个可执行文件 (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

查看我验证的依赖项:

  1. 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)
    
  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 也突出了这个问题。


产生错误的例子

生成库/执行程序

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;
}

文件: py_plugin.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;
}

【问题讨论】:

  • 也许,问题就像您链接的论坛所说的那样简单。 Debian 和从它派生的发行版经常破坏 LSB,然后一切都以不同的方式和以意想不到的方式布局。但我很好奇!为什么要从 c 调用 python 插件?
  • 另外,如果你直接从 python 脚本import bz2 会发生什么?
  • @IharobAlAsimi 当我简单地导入“bz2”时一切正常,因为 python 可执行文件(静态)链接到 libpython,因此模块可以直接从可执行文件访问符号。至于“为什么使用 C 语言的 python?” C 库是为(科学)应用程序之间的可移植性而设计的,我们使用 python 来执行典型的动态任务。每当它想做一些“花哨的”事情时,想想 C 中的数据将自身映射到 Python 对象。
  • @IharobAlAsimi 你说得对,这个问题只是 Debian 特有的,Fedora 按预期处理了这个问题。

标签: python c plugins shared-libraries dynamic-loading


【解决方案1】:

关闭线程以供将来参考。

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

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-24
    • 2020-06-02
    相关资源
    最近更新 更多