为啥链接器链接了错误的函数?

Posted

技术标签:

【中文标题】为啥链接器链接了错误的函数?【英文标题】:Why wrong function was linked by the linker?为什么链接器链接了错误的函数? 【发布时间】:2020-05-31 14:11:50 【问题描述】:

我对以下情况感到很困惑:

我有一个使用静态库 A 来获取可执行文件的 Linux 项目(用 c 编写)。在这个静态库 A 中有一个从 Linux 包含调用 sem_init 的 c 代码。 .c 文件中包含对 semaphore.h 的包含,该文件使用 GCC 编译,没有警告。我使用 ld 链接器。

在我无意中将一个新模块(编译到静态库 B)添加到项目之前,一切都很好。新代码具有同名 (sem_init) 但签名不同的函数。 A 中的代码不包括 B 中定义新 sem_init() 的 .h 文件。

现在,在将库 B 添加到编译后,我看到不是 semaphore.h 中的 sem_init,而是从库 B 中调用了新的 sem_init。

我知道调用同名函数是一个糟糕的想法,但在大型项目中,它可能会无意中发生。

问题:为什么会这样???为什么链接器选择了新的 sem_init???为什么我没有收到多定义链接错误?

【问题讨论】:

与 C++ 不同,我不相信链接器知道 C 函数签名,所以一个 sem_init() 和另一个一样好,如果它是从库中提取的,我想它只需要它找到的第一个。 请提供minimal reproducible example,或至少提供您的编译和链接命令。链接器仅扫描库中未解析的符号。首先扫描哪个库,获胜。 在 C++ 中由于名称修改而不可能,这是否正确?对不起,不能提供一个最小的例子,我在我的问题中简化了一个例子,实际上,该项目包含 10 个库,它是一个专有代码 在大型项目中不会发生这种情况,因为大型项目有命名约定。 【参考方案1】:

当链接器链接静态库时,它只会在那里查找未定义的符号,如果找到定义,则将其链接并解析符号。 sem_init 恰好来自 B,它在 -lc(也实现 POSIX sem_init 的 GNU C 标准库)被链接之前链接,因此链接器从 B 获取 sem_init 然后永远不会再次查找sem_init,这就是为什么您没有收到多个符号定义错误的原因。

B 库可能不打算使用不同的参数列表来实现 POSIX sem_init。它可能需要将其sem_init 标记为static,以便其他翻译单元看不到它。和/或使用完全不同的名称。

【讨论】:

非常感谢。这对我来说是新的。我几乎可以肯定,具有相同名称但不同签名的函数在 C 中不能相互互换。在 C++ 中,由于名称修饰,这是不可能的,对吗? @user3518295 你说得对,C++ 名称修改将参数类型编码为修改后的函数名称。 再次感谢您!为什么在这种情况下(使用 C 代码)没有发生多定义链接错误?多定义链接错误应该防止链接器不知道要选择哪个符号定义来解析未解析符号的情况,不是吗? @user3518295 我在第一段中回答说,这就是链接库的工作方式。仅当您将具有重复符号定义的多个 .o 文件链接到可执行文件或共享库时,才会出现多定义链接器错误。

以上是关于为啥链接器链接了错误的函数?的主要内容,如果未能解决你的问题,请参考以下文章

std::string 生成链接器错误—— const char* 不会。为啥?

为啥我的 C++ 代码中出现链接器命令失败错误? [复制]

为啥链接器看不到我的函数(定义宏来替换系统日志)?

为啥 Xcode 将我的所有代码编译两次,导致任何全局变量的链接器错误?

构造函数重载类获取链接器错误使用DLL?

为啥链接器无法识别我的链接器脚本中定义的入口点