【问题标题】:Undefined Symbol Error with embedded Python interpreter嵌入式 Python 解释器的未定义符号错误
【发布时间】:2020-03-10 14:33:46
【问题描述】:

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

【问题讨论】:

  • ldd /app/Python-3.8.2-build/lib/python3.8/lib-dynload/_struct.cpython-38-x86_64-linux-gnu.so 说什么?它可能会加载不同的 libpython3.8 吗?
  • 我在上面的问题中添加了 ldd 输出。
  • 你能在 python 可执行文件上试试 ldd 吗?

标签: python c++ shared-libraries dynamic-linking


【解决方案1】:

静态链接 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),有一些changes since version 3.8

在 Unix 上,C 扩展不再链接到 libpython,除了 安卓和 Cygwin。嵌入 Python 时,libpython 不得 加载了 RTLD_LOCAL,但改为 RTLD_GLOBAL。以前,使用 RTLD_LOCAL,已经无法加载 C 扩展 没有链接到 libpython,比如标准的 C 扩展 由 Modules/Setup 的 shared 部分构建的库。 (贡献了 Victor Stinner 在 bpo-21536 中。)

另请注意(参见here):

要将 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-X.Y-embed --libs 首先并回退到 pkg-config python-X.Y --libs(没有 --embed) 如果上一个命令失败(将 X.Y 替换为 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/

【讨论】:

    【解决方案2】:

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

    -Wl,--no-gc-sections:

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

    -Wl,--gc-keep-exported:

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

    【讨论】:

    • 我试过了,但是没有用。 “gc-keep-exported”被报告为 ld 不知道,“no-gc-sections”没有任何效果
    猜你喜欢
    • 2016-02-03
    • 2022-01-20
    • 1970-01-01
    • 1970-01-01
    • 2021-05-20
    • 2011-06-13
    • 2018-09-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多