northsideknight
northsideknight

Reputation: 1557

How does a compiler choose which function to link?

I have a program (main.c):

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

int main() {
    int result = sqrt(9);
    printf("result: %d\n" ,result);
    return 0;
}

double sqrt(double blah) {
    return 0;
}

When I run this, my result is

result: 3

This would tell me the linker is choosing the libm library's sqrt function instead of mine to call in my main function.

I don't get any errors or warnings when I compile this program with all warnings enabled:

gcc main.c -Wall

My questions:

The only thing I can think of is when I run gcc --precompile, I see this function declaration:

extern double sqrt(double);

Does this tell the linker that sqrt is defined outside of this file? And since this already satisfies the definition of sqrt, my own definition is ignored when linking?

gcc info (I know it is really clang, since I am on mac, not sure if that makes a difference in terms of this question)

gcc --version
Configured with: --prefix=/Library/Developer/CommandLineTools/usr --with-gxx-include-dir=/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/4.2.1
Apple clang version 11.0.0 (clang-1100.0.33.17)
Target: x86_64-apple-darwin19.6.0
Thread model: posix
InstalledDir: /Library/Developer/CommandLineTools/usr/bin

Edit:

The assembly output:

    .section    __TEXT,__text,regular,pure_instructions
    .build_version macos, 10, 15    sdk_version 10, 15
    .section    __TEXT,__literal8,8byte_literals
    .p2align    3               ## -- Begin function main
LCPI0_0:
    .quad   4621256167635550208     ## double 9
    .section    __TEXT,__text,regular,pure_instructions
    .globl  _main
    .p2align    4, 0x90
_main:                                  ## @main
    .cfi_startproc
## %bb.0:
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    subq    $16, %rsp
    movsd   LCPI0_0(%rip), %xmm0    ## xmm0 = mem[0],zero
    movl    $0, -4(%rbp)
    sqrtsd  %xmm0, %xmm0
    cvttsd2si   %xmm0, %eax
    movl    %eax, -8(%rbp)
    movl    -8(%rbp), %esi
    leaq    L_.str(%rip), %rdi
    movb    $0, %al
    callq   _printf
    xorl    %esi, %esi
    movl    %eax, -12(%rbp)         ## 4-byte Spill
    movl    %esi, %eax
    addq    $16, %rsp
    popq    %rbp
    retq
    .cfi_endproc
                                        ## -- End function
    .globl  _sqrt                   ## -- Begin function sqrt
    .p2align    4, 0x90
_sqrt:                                  ## @sqrt
    .cfi_startproc
## %bb.0:
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    movsd   %xmm0, -8(%rbp)
    xorps   %xmm0, %xmm0
    popq    %rbp
    retq
    .cfi_endproc
                                        ## -- End function
    .section    __TEXT,__cstring,cstring_literals
L_.str:                                 ## @.str
    .asciz  "result: %d\n"


.subsections_via_symbols

Upvotes: 3

Views: 557

Answers (2)

Luis Colorado
Luis Colorado

Reputation: 12708

There's a problem you have not acquainted for:

When you linked your executable, you specified -lm in order to provide both versions of the sqrt() function, the one you provided, and the libm.so shared executable.

The problem is that the shared executable probably will take preference because linking is done at load time, your function is an instrinsic function, and the compiler has not resolved the reference before closing the compiled .o. This makes the dynamic linker to select one of the functions at shared object loading time.

When there were no shared binaries, the sqrt() function was indeed in a sqrt.o file that was stored in libm.a, and the linker selected only the binaries from the archive that were resolving some unresolved references, in that case, the sqrt.o from libm.a should have not been included, and your sqrt() should have been referenced instead.

By the way, there's also an issue in how the compiler treats the sqrt() function since the compiler accepts to support what is called an intrinsic function (as sqrt() is) They normally take preference and so, you are somehow advised not to name your functions like the intrinsic ones. This aspect, which is very extended in FORTRAN, has not been an issue until now. The compiler treats completely different the intrinsic functions from others... and you will see that with other library functions doesn't happen.

You will see this thing to emerge as a warning from the compiler if you try to define sqrt() as a different function (for example, an unsigned sqrt(unsigned n); integer version of the square root) The compiler will emit a warning suggesting you to rename your function so you don't use the same name of an instrinsic function. I have not digged this deeply, but somehow, the compiler knows the existence of some functions before any definition is #included.

Upvotes: 1

Eric Postpischil
Eric Postpischil

Reputation: 224546

The names of standard C library function are reserved for use as identifiers with external linkage. You are not supposed to use them for your own functions. This means that when you use a reserved name, the compiler may assume it is the standard function, not your own implementation. Then, to optimize your program (even without full optimization turned on), the compiler may replace a call to sqrt with the processor’s square root instruction. Or the compiler could even calculate the result itself and build that into the assembly code.

Upvotes: 3

Related Questions