静态库只是一组.o 文件。它们没有以任何有意义的方式“链接”;只是串联在一起。直到您执行真正的链接步骤才能解析符号。
将.a 与可执行文件链接和将等效源代码复制到可执行文件的项目中基本上没有区别。因此,在此之前无需链接任何其他框架或库。
以下练习可能具有教育意义:
创建以下comptest.c:
#include <stdio.h>
int main() {
printf("Hello world.\n");
return 0;
}
看看预处理器做了什么:
gcc -E comptest.c > comptest-cpp.c
这将删除#include 并将其替换为引用文件的内容。这个文件是编译器实际看到的。
现在看看编译器做了什么(我在这里和下面使用>语法,以便与-E平行):
gcc -S comptest.c > comptest.s
这是经过预处理和编译后生成的汇编语言。现在我们把它变成一个 .o:
gcc -c comptest.c > comptest.o
现在让我们看看 .o 中有什么:
$ nm comptest.o
0000000000000040 s EH_frame0
000000000000002d s L_.str
0000000000000000 T _main
0000000000000058 S _main.eh
U _puts
这里重要的是_main 和_puts。 _main 在此文件中的地址 0 处定义。_puts 未定义。所以我们链接的东西最好提供它。让我们尝试在没有任何内容的情况下进行链接:
$ gcc -nodefaultlibs comptest.o
Undefined symbols for architecture x86_64:
"_exit", referenced from:
start in crt1.10.6.o
"_puts", referenced from:
_main in comptest.o
ld: symbol(s) not found for architecture x86_64
collect2: ld returned 1 exit status
(_exit 隐含在 C 运行时中;它没有在 .o 中直接引用)
好的,现在我们准备好将它们放在一起。我们将明确:
gcc -nodefaultlibs comptest.o /usr/lib/libc.dylib -o comptest
这表示将comptest.o 和动态库libc 链接在一起。它保证引用的每个符号都将由这些文件之一提供。它在生成的二进制文件中说明它应该从 /usr/lib/libc.dylib 动态加载符号(这是 libSystem.B.dylib 的符号链接,它本身是一个“伞形框架”而不是一个适当的库,但这已经过去了一点大多数情况下你需要知道的;你可以假装puts()在libSystem中):
$ otool -L comptest
comptest:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 159.1.0)
如果您链接到静态库,则与在命令行中列出其中包含的所有 .o 文件相同。
请注意,在链接步骤中,我们只有 .o 和 .dylib 文件(.a 只是 .o 的一个包)。没有 .c 文件,没有 .h 文件,没有 .s 文件,没有源代码。只是需要解析符号的目标文件。这就是为什么头文件在这里无关紧要,但在编译时却很重要。