简体   繁体   中英

C static libraries do not recognize each other - why do I get “undefined reference to `function`”? (linker error)

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.

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, eg 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.

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM