链接器如何确定链接到哪个函数?

时间:2014-11-11 02:54:08

标签: c++ c linker

下面的代码是我看到的问题的简化版本;基本上,外部函数testprint()最终调用printf()中定义的test_xprintf.cpp而不是标准printf()

(是的,代码看起来很奇怪,但它意味着代表问题,因此它本身并不一定有意义。)

为什么链接器链接到printf()中定义的test_xprintf?这是预期的行为还是依赖于工具?

//
// 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);
}

4 个答案:

答案 0 :(得分:2)

这是GNU链接器的已知行为。解析符号时,它只会检测.o之间的多个定义;如果在.o中没有找到定义,它只会求助于图书馆;然后它将在第一场比赛后停止搜索。

这是默认行为。您可以使用--whole-archive覆盖它,但这可能会使您生成的模块膨胀。

答案 1 :(得分:1)

好的,我认为在阅读了这篇非常有用的blog之后,我找到了一个合理的解释。

这取决于链接顺序。这是我掩饰的东西,但看着我链接库的方式:

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.

我认为发生的事情是链接器:

  • 首先在printf()
  • 中导出了符号test_xprintf
  • 当遇到lib test_xprintf时,它找到了未定义的符号printf
  • 查找当前导出的符号列表,找到printf()并愉快地将它们链接在一起。

最后我认为libC是联系在一起的,这解释了为什么它没有看到它。

根据解释,我相信行为是预期的。

答案 2 :(得分:0)

您的额外printf符号(作为C函数可见)会使链接器混乱。有可能,您还应该看到有关多重定义符号的链接器警告。

该标准甚至不考虑这种意义上的多重定义符号(更不用说翻译单元之外的任何内容)。

典型的链接器将绑定第一个匹配的符号(在某种意义上&#34;第一个&#34;)并为任何匹配的附加内容发出警告。

总结一下:这种行为完全取决于链接器的实现。

答案 3 :(得分:0)

在某些编译器中,std库被视为具有弱连接。 您定义的符号始终具有强大的链接,除非使用某些属性或类似定义,但默认情况下它们是链接的“强”#34;。

这意味着您的definde符号与标准符号之间的任何冲突符号将被解析为您提供的符号,这些符号是强大的&#34;的。

当我遇到这个问题时(使用CodeWarrior&#39; 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或类似的,但我不是&# 39;记住哪一个,这很长(有时几乎占了整个记忆的1/4),所以我必须提供自己的简化版本。

我不知道这是标准的,还是仅依赖于链接器。