Quick solution
Change the order in which you specify the static libraries.
Explanation
With static libraries the order in which you specify the libraries matters. The reason is that the linker will only include an object file from within a static library (remember, those are basically just archives of object files) if that object file resolves a previously unresolved symbol.
Taking your example and the order liba.a libb.a
:
Compiling main.c
gives an object file with an unresolved symbol do_something_else
. Now the linker opens liba.a
, looks at the symbols exported in the single object file liba.o
contained within and finds no match for do_something_else
(as only do_something
is exported from liba.o
). Thus, still needing to resolve do_something_else
, the linker looks at the next library, libb.a
, looks at the symbols exported from libb.o
, finds do_something_else
and thus adds libb.o
to the object files which will be linked. libb.o
contains an unresolved reference of the symbol do_something
, thus the linker tries to resolve that. First, it looks in the library from which it took the object file libb.o
to see if there's another object file which resolves the symbol. Then it tries to resolve the symbol with one of the next libraries / object files. Since there's neither another object file in the library libb.a
nor further libraries / object files to look at, the linker fails with an unresolved symbol do_something
.
When you use the order libb.a liba.a
:
The unresolved symbol do_something_else
from main.o
is resolved by including libb.o
from libb.a
. This adds the unresolved symbol do_something
, which then can be resolved by including liba.o
from liba.a
. Thus, linking succeeds.
Rationale
Why not let the linker search all libraries for new unresolved symbols? I can only guess on this part here, but also provide a sensible use case:
Searching only once through the libraries in the order they were specified is simple. Both to implement as well as to understand. Also, consider this:
// main.c
#include <stdio.h>
int calculate(int value);
int main() {
printf("%d\n", calculate(42));
}
Now there's a library libsuperfast.a
that contains (among other code) an implementation of calculate
which depends on some platform specific stuff, and which is conditionally compiled only when on that platform. But you want to support other platforms, too. Thus you implement calculate
by hand and put it into libslowashell.a
. Linking like
$CC -o app main.o libsuperfast.a libslowashell.a
will include the fast code if its available and the slow code otherwise. Similar stuff is done for portability, e.g. in gnulib.
Other solutions
- Unconditionally include whole static libraries, using
-Wl,--whole-archive
.
- Use libtool. Allows you to create both static and shared libraries without worrying about platform specifics too much. And handles dependencies between libraries for you.
Resolve dependencies to liba.a
manually when creating libb.a
. To do this, first create an relocatable object file from the file(s) whose references to liba.a
must be resolved, linking with liba.a
, then create the library from that partially linked file (and possibly others):
ld -r libb.o liba.a -o libb-partial.o
ar rcs libb.a libb-partial.o # possibly others
- Use
-Wl,--start-group libb.a liba.a -Wl,--end-group
. This causes the linker to repeatedly search the libraries until either there are no more unresolved symbols or no more symbols can be resolved from the libraries.