经验丰富的问题显然来自于链接器(gcc在引擎盖下启动了一个链接器,看吧-只需使用
-v
-在详细模式下)。因此,让我们从一个简短的提示开始,提示链接过程是如何工作的:
链接器保留它需要解析的所有符号的名称。一开始它只是一个符号
main
. 当链接器检查库时会发生什么?
-
如果是静态库,则链接器会查看该库中的每个对象文件,如果该对象文件定义了一些查找符号,则会包括整个对象文件(这意味着某些符号已解析,但可以添加一些新的未解析符号)。链接器可能需要在静态库上传递多次。
-
如果它是一个共享库,则链接器会将其视为一个由单个大型对象文件组成的库(毕竟,我们必须在运行时加载此库,而不必反复多次删除未使用的符号):如果至少有一个所需的符号,则整个库都是“链接的”(实际上链接不是在运行时发生的,这是一种干运行),如果没有-整个库将被丢弃,不再查看。
例如,如果您链接到:
gcc -L/path -lpython3.x <other libs> foo.o
不管你是否
python3.x
是共享库还是静态库:当链接器看到它时,它只查找符号
主要的
,但该符号未在python库中定义,因此它将被丢弃,不再查看python库。仅当链接器看到对象文件时
foo.o
,它意识到需要整个Python符号,但现在已经太迟了。
有一个简单的规则来处理这个问题:将对象文件放在第一位!这意味着:
gcc -L/path foo.o -lpython3.x <other libs>
现在,链接器在第一次看到python库时就知道它需要什么了。
还有其他方法可以达到类似的效果。
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行为是
--根据需要无
,但gcc前端调用ld with option
--as-needed
,因此我们可以通过添加
-no-as-needed
然后再次将其关闭。
现在来看看静态链接的问题。我认为不建议使用所有标准库的静态版本(都在glibc之上),您可能应该做的是只静态链接python库。
链接的规则很简单:默认情况下,链接器首先尝试打开库的共享版本,而不是静态版本。一、 e.对于图书馆
libmylib
和路径
A
和
B
,即。
-L/A -L/B lmylib
它尝试按以下顺序打开库:
A/libmylib.so
A/libmylib.a
B/libmylib.so
B/libmylib.a
因此,如果文件夹
A.
只有静态版本,因此使用此静态版本(无论文件夹中是否有共享版本
B
).
因为真正使用哪个库是非常不透明的,这取决于系统的设置,通常会通过
-Wl,-verbose
解决问题。
通过使用选项
-Bstatic
可以强制使用库的静态版本:
gcc foo.o -L/path -Wl,-Bstatic -lpython3.x -Wl,-Bdynamic <other libs> -Wl,-verbose -o foo
值得注意的是:
-
富。o
在库之前链接。
-
直接在python库之后关闭静态模式,以便动态链接其他库。
现在:
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-导出动态
将无法加载某些依赖于其加载到的可执行文件的此属性的c-extension
ldopen
.
由于隐含
-pie
选项
Recent versions of gcc
使用生成
pie
-option
按默认值。通常/有时,较旧的python版本使用较旧的gcc版本构建,因此
python-config --cflags
会错过现在需要的
-no-pie
,因为当时不需要它。在这种情况下,链接器将生成如下错误消息:
在以下情况下,无法针对符号“XXXXX”重新定位R\u X86\u 64\u 32S
制作饼图对象;使用-fPIC重新编译
在这种情况下,
-没有派
选项应添加到
<cflags>
.