Reputation: 1494
So, I made one static library like this:
liba.h
-----
#ifndef LIBA_H
#define LIBA_H
void do_something();
#endif
and
liba.c
-----
#include "liba.h"
#include <stdio.h>
void do_something() {
printf("liba\n");
}
And one more similar static library:
libb.h
-----
#ifndef LIBB_H
#define LIBB_H
void do_something_else();
#endif
and
libb.c
-----
#include "liba.h"
#include "libb.h"
#include <stdio.h>
void do_something_else() {
do_something(); /* this is supposed to execute from liba */
printf("libb\n");
}
And a simple main file:
main.c
-----
#include "liba.h"
#include "libb.h"
int main() {
do_something_else();
}
The static libraries are compiled with:
gcc -c liba.c
gcc -c libb.c -I<path_to_liba.h>
ar rcs liba.a liba.o
ar rcs libb.a libb.o
And the main program is compiled with:
gcc main.c -I<path_to_liba.h> -I<path_to_libb.h>
-L<path_to_liba.a> -L<path_to_libb.a> -la -lb
When I try to compile the main program I get a link error:
libb/libb.a(libb.o): In function `do_something_else':
libb.c:(.text+0x14): undefined reference to `do_something'
collect2: error: ld returned 1 exit status
For some strange reason, when change my main.c
file to this it works:
main.c
-----
#include "liba.h"
#include "libb.h"
int main() {
do_something();
do_something_else();
}
This not only compiles, but also gives the expected output:
Output:
liba
liba
libb
How am I supposed to run do_something_else
without calling do_something
from the main file ? I'm obviously doing something wrong.
Upvotes: 1
Views: 462
Reputation: 16156
Change the order in which you specify the static libraries.
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.
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.
-Wl,--whole-archive
.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
-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.Upvotes: 2
Reputation: 11
When linking with static libraries - position matters! In your case libb
depends on liba
, so, dependent library should be place after whoever uses it.
So, try this way:
gcc main.c -I<path_to_liba.h> -I<path_to_libb.h>
-L<path_to_liba.a> -L<path_to_libb.a> -lb -la
Or even this way, if you don't want to care about the order:
gcc main.c -I<path_to_liba.h> -I<path_to_libb.h>
-L<path_to_liba.a> -L<path_to_libb.a>
-Wl,--whole-archive -la -lb -Wl,--no-whole-archive
However, it this case whole libraries will be linked, even if it is not used at all.
Upvotes: 1