繁体   English   中英

带有嵌入式 Python 解释器的未定义符号错误

[英]Undefined Symbol Error with embedded Python interpreter

最初我在一个使用 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)

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

    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)

静态链接 libpython

简短回答:将-rdynamic添加到标志使其对我-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 上,除 Android 和 Cygwin 外,C 扩展不再链接到 libpython。 当嵌入 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 并在上一个命令失败时回退到 pkg-config python-XY --libs(不带 --embed)(将 XY 替换为 Python 版本) )。

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

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/

我认为这是因为PyByteArray_Type未在execpy.c引用,因此链接器使用其默认的--gc-sections逻辑来删除未使用的符号。 尝试添加以下选项之一:

-Wl,--no-gc-sections :

启用未使用的输入节的垃圾收集。 它在不支持此选项的目标上被忽略。 默认行为(不执行此垃圾收集)可以通过在命令行上指定 --no-gc-sections 来恢复。 请注意,支持 COFF 和 PE 格式目标的垃圾收集,但该实现目前被认为是实验性的。

-Wl,--gc-keep-exported :

启用 --gc-sections 时,此选项可防止对包含具有默认或受保护可见性的全局符号的未使用输入节进行垃圾收集。 此选项旨在用于未引用部分将被垃圾收集的可执行文件,而不管所包含符号的外部可见性如何。 请注意,此选项在链接共享对象时无效,因为它已经是默认行为。 此选项仅支持 ELF 格式目标。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM