遇到的问题显然来自链接器(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
值得注意的是:
现在:
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
。