The problem is coming from the linker, ld
, rather than gcc (hence the exit status message). In general ld requires objects and libraries to be specified in the order user
supplier
, where user
is an object that uses a library function and supplier
is the object which provides it.
When your test.c
is compiled to an object the compiler states that fmod is an undefined reference
$ gcc -c test.c
$ nm test.o
U fmod
0000000000000000 T main
(nm lists all the functions referred to by an object file)
The linker changes the undefined references to defined ones, looking up the references to see if they are supplied in other files.
$ gcc -lm test.o
$ nm a.out
0000000000600e30 d _DYNAMIC
0000000000600fe8 d _GLOBAL_OFFSET_TABLE_
00000000004006a8 R _IO_stdin_used
w _Jv_RegisterClasses
0000000000600e10 d __CTOR_END__
...
0000000000601018 D __dso_handle
w __gmon_start__
...
U __libc_start_main@@GLIBC_2.2.5
0000000000601020 A _edata
0000000000601030 A _end
0000000000400698 T _fini
0000000000400448 T _init
0000000000400490 T _start
00000000004004bc t call_gmon_start
0000000000601020 b completed.7382
0000000000601010 W data_start
0000000000601028 b dtor_idx.7384
U fmod@@GLIBC_2.2.5
0000000000400550 t frame_dummy
0000000000400574 T main
Most of these refer to libc functions that are run before and after main to set the environment up. You can see that fmod now points to glibc, where it will be resolved by the shared library system.
My system is set up to use shared libraries by default. If I instead force static linking I get the order dependency you see
$ gcc -static -lm test.o
test.o: In function `main':
test.c:(.text+0x40): undefined reference to `fmod'
collect2: ld returned 1 exit status
Putting -lm
later in the linker command, after test.o
, allows it to link successfully.
Checking the symbols fmod should now be resolved to an actual address, and indeed it is
$ gcc -static test.o -lm
$ nm a.out | grep fmod
0000000000400480 T __fmod
0000000000402b80 T __ieee754_fmod
0000000000400480 W fmod