clinuxlinkerundefined-symbollto

Why can't ld ignore an unused unresolved symbol?


Consider the following source files:

a.c:

extern int baz();

int foo() { return 123; }
int bar() { return baz() + 1; }

b.c:

extern int foo();

int main() { return foo(); }

Now, when I try to build a program using these sources, here's what happens:

$ gcc -c -o a.o a.c
$ gcc -c -o b.o b.c
$ gcc -o prog a.o b.o
/usr/bin/ld: a.o: in function `bar':
a.c:(.text+0x15): undefined reference to `baz'
collect2: error: ld returned 1 exit status

This is on Devuan GNU/Linux Chimaera, with GNU ld 2.35.2, GCC 10.2.1.

Why does this happen? I mean, one does not need any complex optimization to know that baz() is not really needed in foo() - ld naturally notices this at some point - e.g. when finishing its traversal of foo() without noticing a location where baz() is used.

Now, you could say "einpoklum, you didn't ask the compiler to go to any trouble for you" - and that's fair, I guess, but even if I use -O3 with these instructions, I get the same error.

Note: with LTO and optimization enabled, we can circumvent this issue:

$ gcc -c -flto -O1 -o b.o b.c
$ gcc -c -flto -O1 -o a.o a.c
$ gcc -o prog -O1 -flto a.o b.o
$ /prog ; echo $?;
123

Solution

  • If you use gcc and binutils ld to build your programs you need to place functions in separate sections. It is archived by -fdata-sections & -ffunction-sections command line options.

    Same with data. Then if you do not want dead code to be included in your executable you need to enable it by using --gc-sections ld option.

    Putting this all together:

    $ gcc -fdata-sections -ffunction-sections -c -o a.o a.c
    $ gcc -c -o b.o b.c
    $ gcc -Wl,--gc-sections -o prog a.o b.o
    $ /prog ; echo $?
    123
    

    If you want to enable it by default simple build GCC with those options enabled.