[英]How does the linker determine which function to link to?
下面的代码是我所看到的问题的简化版本; 基本上,外部函数testprint()
最终调用了test_xprintf.cpp
定义的printf()
,而不是标准的printf()
。
(是的,代码看起来很奇怪,但是它只是用来表示问题,因此它本身并不一定有意义。)
为什么链接器链接到test_xprintf
定义的printf()
? 此预期行为或工具是否相关?
//
// 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);
}
这是GNU链接器的已知行为。 解析符号时,它将仅检测.o之间的多个定义。 如果.o文件中没有定义,它只会求助于库; 然后它将在第一个匹配项后停止搜索。
这是默认行为。 您可以使用--whole-archive
覆盖它,尽管这可能会使生成的模块膨胀。
好吧,我认为在阅读了这个非常有用的博客之后,我对这种情况做出了合理的解释。
它取决于链接顺序。 这是我遗忘的内容,但查看了链接库的方式:
g++ -Wall test_xprintf.cpp -L. -ltest_dbgprintf -I.
将其扩展为两个步骤:
g++ -Wall -g -c -o test_xprintf.o test_xprintf.cpp
g++ -L. test_xprintf.o -ltest_dbgprintf -I.
我认为发生的是链接器:
test_xprintf
导出符号printf()
test_xprintf
,发现未定义的符号printf
printf()
并愉快地将它们链接在一起。 我相信libC最后被链接了,这解释了为什么它没有看到它。
根据解释,我相信这种行为是可以预期的。
您额外的printf
符号(作为C函数可见)使链接器混乱。 很有可能,您还应该看到有关乘法定义符号的链接器警告。
在这种意义上,标准甚至不考虑多重定义的符号(更不用说翻译单元之外的任何内容)。
典型的链接器将绑定第一个匹配的符号(在某种意义上为“第一个”),并对任何匹配的附加符号发出警告。
总结一下:此行为完全取决于链接程序的实现。
在某些编译器中,std库被视为具有弱链接。 您定义的符号始终具有很强的联系,除非另有定义或具有某些属性或类似属性,但默认情况下它们被链接为“强”。
这意味着您在定义符号和标准符号之间出现的任何冲突符号都将解析为您提供的“强”符号。
当我遇到此问题(使用CodeWarrior的ColdFire编译器)时,总是收到链接器警告:
Symbol _sprintf multiply defined in printf.c() and libc.a(printf.o )
Ignoring the definition in libc.a(printf.o )
可能您会看到类似的警告。
为什么有时在std lib中需要这种链接? 好吧,在嵌入式系统中,也许某些功能对于处理器而言无法执行,例如printf函数(不是printf本身,因为它只是一个包装器,我认为它是vsprintf或类似的东西,但我不知道)记得哪一个很长(有时它已经消耗了我几乎整个内存的1/4),所以我不得不提供自己的简化版本。
我不知道这是标准的,还是依赖链接器的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.