Static libraries are just a bundle of .o
files. They're not "linked" in any meaningful way; just concatenated together. It's not until you perform a real link step that symbols are resolved.
There is basically no difference between linking a .a
with your executable and copying the equivalent source code into your executable's project. So there's no need to link with any additional frameworks or libraries until that time.
The following exercise may be educational:
Create the following comptest.c
:
#include <stdio.h>
int main() {
printf("Hello world.\n");
return 0;
}
See what the pre-processor does:
gcc -E comptest.c > comptest-cpp.c
This removes the #include
and replaces it with the contents of the referenced file. This file is what the compiler actually sees.
Now see what the compiler does (I'm using the >
syntax here and below so that things are parallel with -E
):
gcc -S comptest.c > comptest.s
This is the generated assembly language after pre-processing and compilation. Now we turn that into a .o:
gcc -c comptest.c > comptest.o
Now let's see what's in that .o:
$ nm comptest.o
0000000000000040 s EH_frame0
000000000000002d s L_.str
0000000000000000 T _main
0000000000000058 S _main.eh
U _puts
The important things here are _main
and _puts
. _main
is defined in this file at address 0. _puts
is undefined. So something we link with had better provide it. Let's try linking without anything:
$ 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
is implicit from the C runtime; it's not directly referenced in the .o)
OK, so now we're ready to put it all together. We'll be explicit:
gcc -nodefaultlibs comptest.o /usr/lib/libc.dylib -o comptest
This says to link together comptest.o
and the dynamic library libc
. It promises that every symbol referenced will be provided by one of these files. It makes a note in the resulting binary that it should dynamically load symbols from /usr/lib/libc.dylib
(this is a symlink to libSystem.B.dylib, which is itself an "umbrella framework" rather than a proper library, but that goes a little past what you need to know in most cases; you can pretend that puts()
is in libSystem):
$ otool -L comptest
comptest:
/usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 159.1.0)
If you link with a static library, it's identical to listing all the .o files included in it on the command-line.
Notice that at the link step, we just have .o and .dylib files (.a is just a package of .o). There are no .c files, no .h files, no .s files, no source code. Just object files that need symbols resolved. This is why header files don't matter here, but do matter when you're compiling.