簡體   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