2

Could some good soul help me understand why do I have to use -lm gcc option (like so
gcc main.c -o main -lm) in case (1) and do not have in case (2)?

Case (1)

#include <math.h>
int main() {
  float x = 4;
  sqrt(x);
  return 0;
}

Case (2)

#include <math.h>
int main() {
  sqrt(4);
  return 0;
}

I'm using Ubuntu 16.04, with gcc. I know why I should use -lm, but I don't know why the case (2) works without it.

user3386109
  • 34,287
  • 7
  • 49
  • 68
  • 1
    Cause the compiler knows that `sqrt(4)` is 2. In the first case, try `gcc -O3 main.c -o main` and see if that works. – user3386109 May 17 '16 at 04:22

1 Answers1

1

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.

Shachar Shemesh
  • 8,193
  • 6
  • 25
  • 57