1

I always thought that libraries had to be listed after any object files that depended on them, but given this simple program:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>

int main(int argc, char **argv) {
    double res;

    res = acos(2);
    printf("res = %f\n", res);
    return 0;
}

If I build it without linking in libm, it fails as expected:

$ gcc -o mathtest mathtest.c 
/tmp/cc9x6HZA.o: In function `main':
mathtest.c:(.text+0x23): undefined reference to `acos'
collect2: error: ld returned 1 exit status

But I can list -lm either before or after the source file and it links successfully:

gcc -o mathtest -lm mathtest.c
gcc -o mathtest mathtest.c -lm

And I get the same behavior if I compile the source to an object file first and then link it as above.

Did this behavior change at some point? Or was it always like this, and I was just confused?

ldx.a.ldy.c
  • 687
  • 4
  • 9
  • This question seems to be a duplicate of http://stackoverflow.com/questions/8640642/gcc-link-order-changed?rq=1 See: http://stackoverflow.com/a/8640681/1733277 – Brandon Haston Aug 07 '15 at 00:30
  • Possible duplicate of [Why does the order of '-l' option in gcc matter?](https://stackoverflow.com/q/11893996/608639) and [Why does the order in which libraries are linked sometimes cause errors in GCC?](https://stackoverflow.com/q/45135/608639) – jww Aug 22 '19 at 17:23

1 Answers1

1

I confirm the unexpected link-order behaviour that you observe with gcc 5.1 and also that I have the traditional behaviour with gcc 4.9.2. I would infer that this novelty comes in with gcc 5.x, and that your toolchain is gcc 5.x. Possibly it is a distro-specific anomaly of the toolchain build. My toolchain is as per ubuntu 14.04.

The novel behaviour only affects dynamic libraries. I can see what is causing it if I link in verbose mode (-v) with gcc 4.9.2 and also with gcc 5.1, placing -lm before it is needed, and examine the differences in the verbose output.

From gcc 4.9.2 I have (with artifical line-wrapping):

/usr/lib/gcc/x86_64-linux-gnu/4.9/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/4.9/liblto_plugin.so 
-plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/4.9/lto-wrapper -plugin-opt=-fresolution=/tmp/ccsHNLNB.res 
-plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc 
-plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --sysroot=/ --build-id 
--eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker 
/lib64/ld-linux-x86-64.so.2 -z relro -o mathtest 
/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../x86_64-linux-gnu/crt1.o 
/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../x86_64-linux-gnu/crti.o 
/usr/lib/gcc/x86_64-linux-gnu/4.9/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/4.9 
-L/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../x86_64-linux-gnu
-L/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../../lib -L/lib/x86_64-linux-gnu 
-L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib 
-L/usr/lib/gcc/x86_64-linux-gnu/4.9/../../.. -lm mathtest.o -lgcc --as-needed 
-lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed 
/usr/lib/gcc/x86_64-linux-gnu/4.9/crtend.o 
/usr/lib/gcc/x86_64-linux-gnu/4.9/../../../x86_64-linux-gnu/crtn.o

And from gcc 5.1 I have:

/usr/lib/gcc/x86_64-linux-gnu/5/collect2 -plugin /usr/lib/gcc/x86_64-linux-gnu/5/liblto_plugin.so 
-plugin-opt=/usr/lib/gcc/x86_64-linux-gnu/5/lto-wrapper -plugin-opt=-fresolution=/tmp/cc5hI8vd.res 
-plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s -plugin-opt=-pass-through=-lc 
-plugin-opt=-pass-through=-lgcc -plugin-opt=-pass-through=-lgcc_s --sysroot=/ --build-id 
--eh-frame-hdr -m elf_x86_64 --hash-style=gnu -dynamic-linker 
/lib64/ld-linux-x86-64.so.2 -z relro -o mathtest 
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crt1.o 
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crti.o 
/usr/lib/gcc/x86_64-linux-gnu/5/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/5 
-L/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu 
-L/usr/lib/gcc/x86_64-linux-gnu/5/../../../../lib -L/lib/x86_64-linux-gnu 
-L/lib/../lib -L/usr/lib/x86_64-linux-gnu -L/usr/lib/../lib 
-L/usr/lib/gcc/x86_64-linux-gnu/5/../../.. -lm mathtest.o -lgcc --as-needed 
-lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed 
/usr/lib/gcc/x86_64-linux-gnu/5/crtend.o 
/usr/lib/gcc/x86_64-linux-gnu/5/../../../x86_64-linux-gnu/crtn.o

The relevant difference is that the gcc 4.9.2 linkage passes the option --as-needed in 4 places whereas the gcc 5.1 linkage passes it in only 3. gcc 4.9.2 passes --as-needed for the first time at the the 5th quoted "line":

--eh-frame-hdr -m elf_x86_64 --hash-style=gnu --as-needed -dynamic-linker

gcc 5.1 does not pass it there. Thereafter, both linkages pass this option at the same 3 places, starting as the 13th "line":

-L/usr/lib/gcc/x86_64-linux-gnu/4.9/../../.. -lm mathtest.o -lgcc --as-needed

This means that for gcc 4.9.2, the library option -lm on "line" 13 falls within the scope of the first --as-needed. For gcc 5.1 it does not. The effect of this option is to inform the linker that the dynamic library libm (or any dynamic library that is encountered in the option's scope) shall be linked only if it is encountered at a point in the linkage at which it can satisfy some unresolved symbols. In the absence of this option, at a given point in the linkage, a dynamic library is by default deemed to be needed for the linkage regardless of whether there are yet any unresolved symbols that it can satisfy. See the documentation of --as-needed

Thus the gcc 4.9.2 linkage will not link libm, although it appears on the commandline, because at the point where it appears - before mathtest.o - --as-needed is in force and there are as yet no unresolved references that libm satisfies. The gcc 5.1 linkage will link libm because it appears on the commandline at a point where --as-needed is not in force.

Mike Kinghan
  • 55,740
  • 12
  • 153
  • 182