Let's have a look at a couple of functions. Here's the first one:
#include <math.h>
double func(double num) {
return sqrt(num);
}
Compile with gcc -O3 -c so.c
and disassemble with objdump -d so.o
:
0000000000000000 <func>:
0: f2 0f 51 c8 sqrtsd %xmm0,%xmm1
4: 66 0f 2e c9 ucomisd %xmm1,%xmm1
8: 7a 05 jp f <func+0xf>
a: 66 0f 28 c1 movapd %xmm1,%xmm0
e: c3 retq
f: 48 83 ec 08 sub $0x8,%rsp
13: e8 00 00 00 00 callq 18 <func+0x18>
18: 48 83 c4 08 add $0x8,%rsp
1c: c3 retq
So we have a bunch of FPU register operations, and a call. Let's try to see what that call calls. Run nm so.o
:
0000000000000000 T func
U sqrt
So we are defining (T
) a function called "func", and importing (U
) a symbol called sqrt
. The eventual program will have to be linked with -lm
, as that's where sqrt
is actually defined.
Now let's try a second function:
#include <math.h>
double func() {
return sqrt(4);
}
Again, compile with gcc -O3 -c so.c
and disassemble with objdump -d so.o
:
0000000000000000 <func>:
0: f2 0f 10 05 00 00 00 movsd 0x0(%rip),%xmm0 # 8 <func+0x8>
7: 00
8: c3 retq
No FPU register operations. No function calls. Let's verify with nm so.o
:
0000000000000000 T func
0000000000000000 r .LC0
Yep. No importing of symbols.
You get a hint of what's going on here if you omit #include <math.h>
:
$ gcc -O3 -g -c so.c
so.c: In function ‘func’:
so.c:2:10: warning: incompatible implicit declaration of built-in function ‘sqrt’
return sqrt(4);
^
sqrt
is a built-in function. The compiler does not treat it as some opaque construct. It understands what it does. In the second case, the compiler understands that it does not need to place a call to the function at run time. It knows what the answer is during compilation. It simply places the answer in the return registers and calls it a day. Since the run time does not contain a call to sqrt
, there is no need to link with -lm
.