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

How does the linker determine which function to link to?

本文关键字:链接 函数 何确定      更新时间:2023-10-16

下面的代码是我看到的问题的简化版本;基本上外部函数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 %dn", 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()
  • 当它遇到lib 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(, 所以我不得不提供我自己的简化版本。

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