【发布时间】:2021-11-18 10:36:01
【问题描述】:
最近在 Linux 上开始 C 编码并遇到“多重定义”错误(来源 A.c 和 B.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() 的所有调用
问题
- 重命名符号 Hello() 是唯一正确的解决方案吗?
- 使用 --allow-multiple-definition 选项是不好的做法吗?
- 如果可能,链接器是否应该对预期的代码更加敏感 通过限制函数可见性来实现功能,即,了解 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 链接并解析。