我有一个纯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
。链接器检查库时会发生什么?
如果它是静态库,则链接器将查看该库中的每个目标文件,并且如果此目标文件定义了一些查找符号,则将包括整个目标文件(这意味着某些符号已解析,但是一些新的未解析符号可以被添加)。链接器可能需要在静态库上传递多次。
如果它是一个共享库,则链接器将其视为由一个巨大的目标文件组成的库(毕竟,我们必须在运行时加载该库,而不必一遍又一遍地传递修剪未使用的符号):如果至少有一个需要的符号,则整个库是“链接的”(实际上链接不是在运行时发生的,这是一种试运行),如果不是-整个库被丢弃,再也没有看过。
例如,如果您链接:
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
和路径A
和B
,即
-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
值得注意的是:
foo.o
在库之前链接。现在:
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? 问题答案: 默认情况下,你具有静态文件的端点。还应用有以下参数: :可用于为网络上的静态文件指定其他路径。默认为文件夹的名称。 :包