phl*_*max 12 python linker gcc cython
我有一个纯Python脚本,我想分发给具有未知Python配置的系统.因此,我想将Python代码编译为独立的可执行文件.
我cython --embed ./foo.py没有问题就跑了foo.c.然后,我跑了
gcc $(python3-config --cflags) $(python3-config --ldflags) ./foo.c
Run Code Online (Sandbox Code Playgroud)
哪里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
Run Code Online (Sandbox Code Playgroud)
并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
Run Code Online (Sandbox Code Playgroud)
这样我就可以获得一个没有问题的动态链接可执行文件.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)
Run Code Online (Sandbox Code Playgroud)
现在,我尝试将选项添加-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
Run Code Online (Sandbox Code Playgroud)
我检查了ldd给出的所有共享库也作为静态库安装.
那么,这与python3-config给出的选项有些不兼容吗?
经验丰富的问题显然是来自链接器(gcc在引擎盖下启动了一个链接器,看看它 - 只需用-v- 以详细模式启动gcc ).那么让我们先简要提醒链接过程如何工作:
链接器保留其需要解析的所有符号的名称.一开始它只是符号main.当链接器检查库时会发生什么?
如果它是一个静态库,链接器将查看此库中的每个目标文件,如果此目标文件定义了一些查找符号,则包含整个目标文件(这意味着某些符号会被解析,但是一些其他新的未解析符号可以加上).链接器可能需要在静态库上多次传递.
如果它是一个共享库,它会被链接器视为一个由一个巨大的目标文件组成的库(毕竟,我们必须在运行时加载这个库,而不必反复传递多次修剪未使用的符号):如果至少有一个需要的符号,整个库是"链接的"(不是真的链接发生在运行时,这是一种干运行),如果不是 - 整个库被丢弃,再也没有看过.
例如,如果您链接:
gcc -L/path -lpython3.x <other libs> foo.o
Run Code Online (Sandbox Code Playgroud)
无论python3.x是共享库还是静态库,你都会遇到问题:当链接器看到它时,它只查找符号main,但是这个符号没有在python-lib中定义,所以python-lib被丢弃了从来没有再看过.只有当链接器看到对象文件时foo.o,才意识到需要整个Python-Symbols,但现在已经太晚了.
有一个简单的规则来处理这个问题:首先放置目标文件!这意味着:
gcc -L/path foo.o -lpython3.x <other libs>
Run Code Online (Sandbox Code Playgroud)
现在,链接器在第一次看到python-lib时就会知道它需要什么.
还有其他方法可以实现类似的结果.
A)只要每次扫描添加至少一个新符号定义,就让链接器重复一组归档:
gcc -L/path --Wl,-start-group -lpython3.x <other libs> foo.o -Wl,-end-group
Run Code Online (Sandbox Code Playgroud)
链接器选项-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
Run Code Online (Sandbox Code Playgroud)
实际上,默认的ld-behavior是--no-as-needed,但是gcc-frontend用选项调用ld --as-needed,所以我们可以通过-no-as-needed在python-library之前添加来恢复行为,然后再将其关闭.
现在你的静态链接问题.我不认为使用所有标准库的静态版本(所有上面的glibc)是不可取的,你应该做的是静态链接python-library.
链接的规则很简单:默认情况下,链接器首先尝试打开库的共享版本而不是静态版本.即,对于图书馆libmylib和路径A和B,即
-L/A -L/B lmylib
Run Code Online (Sandbox Code Playgroud)
它尝试按以下顺序打开库:
A/libmylib.so
A/libmylib.a
B/libmylib.so
B/libmylib.a
Run Code Online (Sandbox Code Playgroud)
因此,如果文件夹A只有静态版本,则使用此静态版本(无论文件夹中是否存在共享版本B).
因为实际使用的库是非常不透明的 - 它取决于系统的设置,通常会通过-Wl,-verbose故障排除来切换链接器的日志记录.
通过使用该选项,-Bstatic可以强制使用库的静态版本:
gcc foo.o -L/path -Wl,-Bstatic -lpython3.x -Wl,-Bdynamic <other libs> -Wl,-verbose -o foo
Run Code Online (Sandbox Code Playgroud)
值得注意的事:
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!
Run Code Online (Sandbox Code Playgroud)
是的,如果你链接静态glibc(我不推荐),你需要-Xlinker -export-dynamic从命令行删除.
编译的可执行文件-Xlinker -export-dynamic将无法加载某些c-extension,这些扩展依赖于它们所加载的可执行文件的这个属性ldopen.