【问题标题】:Multiple definition errors during gcc linking in LinuxLinux中gcc链接期间的多个定义错误
【发布时间】:2021-11-18 10:36:01
【问题描述】:

最近在 Linux 上开始 C 编码并遇到“多重定义”错误(来源 A.cB.c 如下)

gcc -Wall A.o B.o -o C

/usr/bin/ld: B.o: 在函数“Hello”中:
B.c:(.text+0x0): 多个 “你好”的定义; A.o:A.c:(.text+0x0): 这里先定义

通过 S.O 搜索,建议使用此链接器命令行选项

--allow-multiple-definition (-z muldefs)
通常当一个符号被定义多次时,链接器会报告一个致命的 错误。
这些选项允许多个定义和第一个 将使用定义。

运行此命令绕过了错误消息

gcc -Wall -Wl,--allow-multiple-definition A.o B.o -o C

似乎恢复了平静,然而,它输出了

./C

你好 A
你好 A
世界 B

我期待下面的输出,并“假设”链接器会顺利解决任何符号冲突,但至少保留现有代码功能

你好 A
你好 B
世界 B

但是,生成的 C ELF 二进制文件通过检查来自 objdump -S -M intel C

  • 删除了 B.c 中重复的 Hello()
  • 重新指向 A.c 中对 Hello() 的所有调用

问题

  1. 重命名符号 Hello() 是唯一正确的解决方案吗?
  2. 使用 --allow-multiple-definition 选项是不好的做法吗?
  3. 如果可能,链接器是否应该对预期的代码更加敏感 通过限制函数可见性来实现功能,即,了解 Hello() 是 Bo 中的重复符号并从 World() 调用它应该限于 Bo 而不是查看 敖?



gcc -Wall -c A.c

#include <stdio.h>

void Hello(void)
{
    printf("Hello A\n");
}

int main()
{
    Hello();
    World();

    return 0;
}

gcc -Wall -c B.c

#include <stdio.h>

void Hello(void)
{
    printf("Hello B\n");
}

void World(void)
{
    Hello();
    printf("World B\n");
}

已编辑

根据 cmets/answers,添加 static 关键字效果很好

static void Hello(void)
{
    printf("Hello B\n");
}

现在不需要使用那个命令行选项

gcc -Wall A.o B.o -o C

你好 A
你好 B
世界 B

这个问题的真正推动力是我们构建了一个 UASM 程序集对象文件,并且给出了关于 C 静态关键字的提示,至少我现在可以研究 UASM 中有什么可以使这些函数成为对象的私有。

更新

对于 UASM,可以通过添加将功能范围限制在目标文件中

PROC PRIVATE FRAME

谢谢!

【问题讨论】:

  • 要正确恢复和平,请将static 添加到Hello 的定义中。
  • 我想知道 --allow-multiple-definition 选项的添加是如何通过代码审查的...
  • 如果您有多个具有外部链接的函数定义,则程序的行为是未定义的。它在 C 标准的第 6.9 节中(措辞为“应该有一个单一的定义”)。
  • 我不明白这个问题的目的。您编写了一个明显不正确的 C 程序,然后您尝试使用各种晦涩难懂的编译器特定技巧来构建它。但为什么?你知道你的程序不正确吗?如果是这样,通过构建它来完成什么?
  • 最近一位同事将一个大对象(比如 B.o)与另一个对象(比如 A.o)链接在一起,但遇到了多个定义错误。我建议使用 --allow-multiple-definition 来允许项目构建。后来重新思考,我想知道那个选项到底做了什么,这是个坏建议吗?我构建了这个人为的问题来挖掘内部结构。该选项有不好的结果。建议的修复是创建一个“静态”函数,其范围仅限于其目标文件。这很好用。 UASM 程序集对象也可以通过 PROC PRIVATE FRAME 链接并解析。

标签: c linux gcc


【解决方案1】:
  1. 最好更改名称,但您也可以将它们设为静态(以限制对它们所在文件的访问)或稍微更改签名。

  2. 是的。难以置信的糟糕!在现实世界中,您期望 Hello() 做某事,但现在您让编译器决定使用哪个版本的 Hello() - 可能是正确的。可能是错的。甚至有这个选项(恕我直言)都是疯狂的。

  3. 您可以通过将它们设为静态来做到这一点。

这有点学术。为什么您首先认为拥有两个名为 Hello() 的全局范围函数是一个好主意?

【讨论】:

  • 感谢您的想法!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-06-04
  • 2016-12-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-05
相关资源
最近更新 更多