首页 > 解决方案 > 嵌入式 Python 解释器的未定义符号错误

问题描述

最初我在使用 pybind11 嵌入 anaconda Python 解释器的较大项目中遇到此错误。只需一个简单的最小示例,我就可以将其归结并重现错误。

当我运行我的可执行文件(嵌入 python)时,我得到这个错误:

Traceback (most recent call last):
  File "<string>", line 3, in <module>
  File "/app/Python-3.8.2-build/lib/python3.8/struct.py", line 13, in <module>
    from _struct import *
ImportError: /app/Python-3.8.2-build/lib/python3.8/lib-dynload/_struct.cpython-38-x86_64-linux-gnu.so: undefined symbol: PyByteArray_Type

起初,我从源代码构建 Python-3.8.2。然后我从以下 C 代码编译了一个可执行文件:

#include <Python.h>

int main(int argc, char *argv[])
{
    Py_Initialize();
    PyRun_SimpleString("import struct");
    if (Py_FinalizeEx() < 0) {
        exit(120);
    }
    return 0;
}

使用这个命令:

gcc -o execpy execpy.c \
-I/app/Python-3.8.2-build/include/python3.8 \
-Wno-unused-result -Wsign-compare  -DNDEBUG -g -fwrapv -O3 \
-L/app/Python-3.8.2-build/lib  -lcrypt -lpthread -ldl  -lutil -lm \
/app/Python-3.8.2/libpython3.8.a

然后简单地执行./execpy从上面给出错误......有什么想法吗?

编辑:在这个例子中,我想libpython静态链接,就像 python 解释器不依赖于任何 libpython.so。

编辑_struct.*.so似乎没有链接到 libpython 的依赖项(这与我的标准 anaconda python 解释器相同):

$ ldd /app/Python-3.8.2-build/lib/python3.8/lib-dynload/_struct.cpython-38-x86_64-linux-gnu.so
    linux-vdso.so.1 =>  (0x00007fff32bf0000)
    libpthread.so.0 => /lib64/libpthread.so.0 (0x00007f71a5634000)
    libc.so.6 => /lib64/libc.so.6 (0x00007f71a5266000)
    /lib64/ld-linux-x86-64.so.2 (0x00007f71a5a5c000)

我还在_struct.*.so另一台机器上检查了我的系统 python 解释器,它有它:

    linux-vdso.so.1 =>  (0x00007ffe2b3d9000)
    libpython3.6m.so.1.0 => /lib64/libpython3.6m.so.1.0 (0x00007febe24fd000)
    libpthread.so.0 => /lib64/libpthread.so.0 (0x00007febe22e1000)
    libc.so.6 => /lib64/libc.so.6 (0x00007febe1f13000)
    libdl.so.2 => /lib64/libdl.so.2 (0x00007febe1d0f000)
    libutil.so.1 => /lib64/libutil.so.1 (0x00007febe1b0c000)
    libm.so.6 => /lib64/libm.so.6 (0x00007febe180a000)
    /lib64/ld-linux-x86-64.so.2 (0x00007febe2c30000)

标签: pythonc++shared-librariesdynamic-linking

解决方案


静态链接 libpython

简短的回答:添加-rdynamic到标志使它对我有用。

-rdynamic标志文件:

-rdynamic
    Pass the flag -export-dynamic to the ELF linker, on targets that support it. This 
    instructs the linker to add all symbols, not only used ones, to the dynamic symbol 
    table. This option is needed for some uses of dlopen or to allow obtaining 
    backtraces from within a program.

动态链接 libpython

我还发现:如果你想动态嵌入 Python 3.8 解释器(libpython3.8.so),自 3.8 版以来有一些变化

在 Unix 上,C 扩展不再链接到 libpython,除了 Android 和 Cygwin。嵌入 Python 时,libpython 不能加载 RTLD_LOCAL,而是 RTLD_GLOBAL。以前,使用 RTLD_LOCAL 已经无法加载未链接到 libpython 的 C 扩展,例如由 Modules/Setup 的共享部分构建的标准库的 C 扩展。(由 Victor Stinner 在 bpo-21536 中贡献。)

另请注意(参见此处):

要将 Python 嵌入到应用程序中,必须将新的 --embed 选项传递给 python3-config --libs --embed 以获取 -lpython3.8(将应用程序链接到 libpython)。要同时支持 3.8 和更早版本,请先尝试 python3-config --libs --embed,如果前面的命令失败,则回退到 python3-config --libs(不带 --embed)。

添加一个 pkg-config python-3.8-embed 模块以将 Python 嵌入到应用程序中:pkg-config python-3.8-embed --libs 包括 -lpython3.8。要同时支持 3.8 和更早版本,请先尝试 pkg-config python-XY-embed --libs,如果前面的命令失败(将 XY 替换为 Python 版本),则回退到 pkg-config python-XY --libs(不带 --embed) )。

所以像这样动态编译和链接现在也适用于我:

gcc -o execpy execpy.c -I/app/Python-3.8.2-build/include/python3.8 \
    -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 \
    -lcrypt -lpthread -ldl  -lutil -lm -lpython3.8\
    -L/app/Python-3.8.2-build/lib/ -Wl,-rpath,/app/Python-3.8.2-build/lib/

推荐阅读