当前位置: 首页 > 面试题库 >

使用Cython将Python代码编译为静态链接的可执行文件

孙莫希
2023-03-14
问题内容

我有一个纯Python脚本,我想分发给具有未知Python配置的系统。因此,我想将Python代码编译为独立的可执行文件。

我奔波cython --embed ./foo.py没有问题foo.c。那我跑

gcc $(python3-config --cflags) $(python3-config --ldflags) ./foo.c

哪里python3-config --cflags

-I/usr/include/python3.5m -I/usr/include/python3.5m  -Wno-unused-result -Wsign-compare -g -fdebug-prefix-map=/build/python3.5-MLq5fN/python3.5-3.5.3=. -fstack-protector-strong -Wformat -Werror=format-security  -DNDEBUG -g -fwrapv -O3 -Wall -Wstrict-prototypes

python3-config --ldflags

-L/usr/lib/python3.5/config-3.5m-x86_64-linux-gnu -L/usr/lib -lpython3.5m -lpthread -ldl  -lutil -lm  -Xlinker -export-dynamic -Wl,-O1 -Wl,-Bsymbolic-functions

通过这种方式,我可以获得一个动态链接的可执行文件,该文件可以正常运行。ldd a.out产量

 linux-vdso.so.1 (0x00007ffcd57fd000)
 libpython3.5m.so.1.0 => /usr/lib/x86_64-linux-gnu/libpython3.5m.so.1.0 (0x00007fda76823000)
 libpthread.so.0 => /lib/x86_64-linux-gnu/libpthread.so.0 (0x00007fda76603000)
 libdl.so.2 => /lib/x86_64-linux-gnu/libdl.so.2 (0x00007fda763fb000)
 libutil.so.1 => /lib/x86_64-linux-gnu/libutil.so.1 (0x00007fda761f3000)
 libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fda75eeb000)
 libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fda75b4b000)
 libexpat.so.1 => /lib/x86_64-linux-gnu/libexpat.so.1 (0x00007fda7591b000)
 libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007fda756fb000)
 /lib64/ld-linux-x86-64.so.2 (0x00007fda77103000)

现在,我尝试将选项添加-static到gcc,但这会导致错误:

/usr/bin/ld: dynamic STT_GNU_IFUNC symbol `strcmp' with pointer equality in `/usr/lib/gcc/x86_64-linux-gnu/6/../../../x86_64-linux-gnu/libc.a(strcmp.o)' can not be used when making an executable; recompile with -fPIE and relink with -pie
collect2: error: ld returned 1 exit status

我检查了ldd给定的所有共享库是否也都安装为静态库。

那么,这与python3-config提供的选项是否有些不兼容?


问题答案:

遇到的问题显然来自链接器(gcc在后台启动了一个链接器,看到它-只需-v以详细模式启动gcc-
)。因此,让我们从简短的提醒开始,说明链接过程的工作原理:

链接器保留需要解析的所有符号的名称。一开始它只是符号main。链接器检查库时会发生什么?

  1. 如果它是静态库,则链接器将查看该库中的每个目标文件,并且如果此目标文件定义了一些查找符号,则将包括整个目标文件(这意味着某些符号已解析,但是一些新的未解析符号可以被添加)。链接器可能需要在静态库上传递多次。

  2. 如果它是一个共享库,则链接器将其视为由一个巨大的目标文件组成的库(毕竟,我们必须在运行时加载该库,而不必一遍又一遍地传递修剪未使用的符号):如果至少有一个需要的符号,则整个库是“链接的”(实际上链接不是在运行时发生的,这是一种试运行),如果不是-整个库被丢弃,再也没有看过。

例如,如果您链​​接:

gcc -L/path -lpython3.x <other libs> foo.o

无论python3.x是共享库还是静态库,您都会遇到问题:链接器看到它时,它只会查找symbol main,但在python-
lib中未定义此符号,因此将python-lib丢弃再也没有看过
仅当链接器看到目标文件时foo.o,它才意识到需要整个Python符号,但是现在已经来不及了。

有一个简单的规则可以处理此问题:将目标文件放在第一位!这意味着:

gcc -L/path  foo.o -lpython3.x <other libs>

现在,链接器在第一次看到它时就从python-lib知道了它的需求。

还有其他方法可以达到类似的结果。

A)只要每次扫描至少添加一个新的符号定义,链接器就可以重申一组存档:

gcc -L/path --Wl,-start-group -lpython3.x <other libs> foo.o -Wl,-end-group

链接程序选项,-Wl,-start-group-Wl,-end- group说链接程序在该组归档中重复进行多次,因此链接程序有第二次(或多次)机会包含符号。此选项可导致更长的链接时间。

B)启用该选项--no-as-needed将导致链接共享库(并且仅共享库),无论是否需要在该库中定义符号。

gcc -L/path -Wl,-no-as-needed -lpython3.x -Wl,-as-needed <other libs> foo.o

实际上,默认的ld-behavior是--no-as-needed,但是gcc-frontend用option调用ld --as- needed,因此我们可以通过-no-as-needed在python-library之前添加来恢复行为,然后再次将其关闭。

现在到您的静态链接问题。我不建议使用所有标准库的静态版本(在glibc之上),您应该做的只是静态地链接python-library。

链接的规则很简单:默认情况下,链接器尝试首先打开库的共享版本,而不是打开静态版本。即,对于图书馆libmylib和路径AB,即

 -L/A -L/B lmylib

它尝试按以下顺序打开库:

A/libmylib.so
A/libmylib.a
B/libmylib.so
B/libmylib.a

因此,如果文件夹A只有一个静态版本,则使用此静态版本(无论folder中是否有共享版本B)。

因为真正使用哪个库是非常不透明的-它取决于您的系统设置,因此通常会通过以下方式打开链接器的日志记录-Wl,-verbose以进行故障排除。

通过使用该选项,-Bstatic可以强制使用库的静态版本:

gcc  foo.o -L/path -Wl,-Bstatic -lpython3.x -Wl,-Bdynamic <other libs>  -Wl,-verbose -o foo

值得注意的是:

  1. foo.o 在库之前链接。
  2. 直接在python-library之后关闭静态模式,以便动态链接其他库。

现在:

 gcc <cflags> L/paths foo.c -Wl,-Bstatic -lpython3.X -Wl,-Bdynamic <other libs> -o foo -Wl,-verbose
...
attempt to open path/libpython3.6m.a succeeded
...
ldd foo shows no dependency on python-lib
./foo
It works!

是的,如果您针对静态链接glibc(我不推荐),则需要-Xlinker -export-dynamic从命令行中删除。

编译为不带的可执行文件-Xlinker -export-dynamic将无法加载某些c-extension,这取决于将它们加载到的可执行文件的此属性ldopen



 类似资料:
  • 以下是我的cmake代码: 我尝试从链接中合并步骤,但没有工作,并得到以下错误: /usr/bin/ld:尝试动态对象的静态链接`/usr/lib/x86_64-linux-gnu/libglu.so'collect2:错误:ld返回1退出状态src/cmakefiles/wwest-export-app.out.dir/build.make:774:recipe for targe'bin/ww

  • 本文向大家介绍Cython编译python为so 代码加密示例,包括了Cython编译python为so 代码加密示例的使用技巧和注意事项,需要的朋友参考一下 1. 编译出来的so比网上流传的其他方法小很多。 2. language_level  是python的主版本号,如果python版本是2.x,目前的版本Cython需要人工指定language_level. 3. python setup

  • 问题内容: 现在,我已经在Windows 7上成功安装了Cython,我尝试使用Cython编译一些Cython代码,但是gcc使我的生活变得艰难。 使用gcc编译代码时,会抛出数十个 对 -erros的 未定义引用 ,并且我很确定src是可用的(如安装教程所述,如果缺少此文件,则会抛出 对 -errors的 未定义引用 )。 奇怪的是,使用*或-script可以很好地工作,但是当仍然在模块上工作

  • 问题内容: 我试图将静态库(与gcc一起编译)链接到C 程序,但出现了“未定义引用”。我在ubuntu 12.04服务器计算机上使用了gcc和g 版本4.6.3。例如,这是阶乘方法的简单库文件: mylib.h mylib.c 我使用gcc为此mylib.c创建了对象: 再次使用AR实用工具从目标文件创建了静态库: 我用C程序(test.c)和C ++程序(test.cpp)测试了这个库 C和C

  • 问题内容: 我正在使用Ubuntu 13.04,并使用安装了mingw-w64 。我可以使用以下命令来编译和链接程序的64位工作版本: 生成一个64位的 app.exe 文件。 我使用哪些二进制或命令行标志来生成32位版本的app.exe? 问题答案: 这取决于您当前正在使用哪种工具链。既 DWARF 和_SEH_变体(它们来自GCC4.8.0开始)仅单一目标。您可以通过检查其发行版的目录结构来自

  • 问题内容: 如何在Flask中使用引用文件夹中的文件?例如,我的文件夹中有一些静态文件,其中一些可能位于子文件夹中。 当我尝试从提供文件时,出现错误。 我可以使用此功能来引用不在子文件夹中的文件。 引用静态文件的正确方法是什么?如何使用任何级别的静态文件生成URL? 问题答案: 默认情况下,你具有静态文件的端点。还应用有以下参数: :可用于为网络上的静态文件指定其他路径。默认为文件夹的名称。 :包