Reputation: 458
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);
}
Upvotes: 1
Views: 1939
Reputation: 458
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:
printf()
in test_xprintf
test_xprintf
, it found the undefined symbol printf
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.
Upvotes: 1
Reputation: 2373
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.
Upvotes: 2
Reputation: 2605
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.
Upvotes: 0
Reputation: 2433
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.
Upvotes: 0