简体   繁体   中英

How does the linker determine which function to link to?

The code below is a simplified version of the problem I am seeing; basically the external function testprint() ended up calling the printf() defined in test_xprintf.cpp instead of the standard printf() .

(Yes, the code looks odd, but it is meant to represent the problem, so it does not necessarily makes sense by itself.)

Why did the linker link to the printf() defined in test_xprintf ? Is this expected behaviour or tool dependent?

//
// test_xprintf.cpp
//

#include <iostream>
#include <stdio.h>
#include <stdarg.h>
#include "test_dbgprintf.h"

/**
 *
 * There are 3 files in total:
 * - test_xprintf.cpp
 * - test_dbgprintf.h
 * - test_dbgprintf.cpp
 *
 * Create a static C lib from test_dbgprintf.c and link with test_xprintf.cpp
 *
 * gcc -Wall -g -c -o test_dbgprintf.o test_dbgprintf.c && 
 * ar -rcs  libtest_dbgprintf.a test_dbgprintf.o &&
 * g++ -Wall test_xprintf.cpp -L. -ltest_dbgprintf -I.
 */

extern "C" int printf(const char *format, ...)
{
    va_list ap;
    va_start(ap, format);
    vprintf(format, ap);
    va_end(ap);

    return -1;
}

int main()
{
    // testprint() is a shell function which simply calls printf. 
    // If the printf function above is called, the return value will be -1. 
    int ret = testprint(4);
    std::cout << "Ret value is " << ret << std::endl;
    return ret;
}

//
// test_dbgprintf.h
//

#ifndef TEST_DBGPRINTF_H
#define TEST_DBGPRINTF_H

#if defined (__cplusplus)
extern "C" {
#endif

int testprint(int num);

#if defined (__cplusplus)
}
#endif


#endif

//
// test_dbgprintf.c
//

#include <stdio.h>

int testprint(int num)
{
    // By right this should be calling the std printf but it is linked to the printf in test_printf.cpp instead.
    return printf("This is called from testprint %d\n", num);
}

It is a known behavior with GNU linker. When resolving a symbol, it will only detect multiple definitions between the .o's; it only will resort to libraries if no definition was found in the .o's; and then it will stop search after the first match.

That's the default behavior. You may override it with --whole-archive , though that may bloat your resulting module.

Okay I think I found a plausible explanation of the situation after reading this very useful blog .

It is dependent on the linking order. This is something I glossed over but looking at the way I link the library:

g++ -Wall test_xprintf.cpp -L. -ltest_dbgprintf -I.

expanding it to two steps:

g++ -Wall -g -c -o test_xprintf.o test_xprintf.cpp
g++ -L. test_xprintf.o -ltest_dbgprintf -I.

I think what happened was the linker:

  • first exported symbol printf() in test_xprintf
  • when it encountered the lib test_xprintf , it found the undefined symbol printf
  • looked up the current list of exported symbols, found the printf() and happily linked them together.

libC is linked last I believe which explains why it doesn't see it.

Based on the explanation, I believe the behaviour is expected.

Your extra printf symbol, visible as a C function, is confusing the linker. Chances are, you should also be seeing linker warnings about multiply defined symbols.

The standard does not even consider multiply-defined symbols in this sense (let alone much of anything outside a translation unit).

A typical linker will bind the first matching symbol (in some sense of "first") and emit warnings for any matching extras.

To sum it up: This behavior is entirely dependent on the linker's implementation.

In some compilers, the std library is treated as having weak linkage. Your defined symbols has always strong linkage, except defined otherwise with some attribute or similar, but by default they are linked "strong".

This means that any conflicting symbols between your definde symbols and the standard symbols will be resolved to the ones you provide, which are the "strong" ones.

When I faced this problem (using CodeWarrior's ColdFire compiler), I got always a linker warning:

Symbol _sprintf multiply defined in printf.c() and libc.a(printf.o       )
Ignoring the definition in libc.a(printf.o       )

Probably you see a similar warning.

And why sometimes is needed this kind of linkage in the std lib? Well, in embedded systems, maybe some functions are just too heavy for the processor to execute, one example is the printf function (not printf by itself, since it is just a wrapper, I think it was vsprintf or similar, but I don't remember which one), which is pretty long (sometimes it has eaten me almost 1/4 of the entire memory), so I had to provide my own simplified version.

I don't know if this is standard, or just linker dependant.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM