Mar*_*cus 1 c gcc compiler-errors x86-64 riscv
我正在尝试splash2x.raytrace在两种架构上编译一个程序(转换后的 PARSEC 基准套件包) - amd64 和 riscv64;但是,当我尝试在两者上本地编译它时,我得到了无法解释的不同行为。
具体来说,虽然在 amd64 上它编译,但在 riscv64 上它失败了很多multiple definition of....
该程序有大约十几个.c文件和一个标题,它们都包含在其中(“ rt.h”)。
样本变量是:
# File: rt.h
INT prims_in_leafs;
Run Code Online (Sandbox Code Playgroud)
导致错误:
/usr/bin/ld: cr.o:/home/riscv/parsec-benchmark/ext/splash2x/apps/raytrace/obj/riscv64-linux.gcc/rt.h:750: multiple definition of `prims_in_leafs'; bbox.o:/home/riscv/parsec-benchmark/ext/splash2x/apps/raytrace/obj/riscv64-linux.gcc/rt.h:750: first defined here
/usr/bin/ld: env.o:/home/riscv/parsec-benchmark/ext/splash2x/apps/raytrace/obj/riscv64-linux.gcc/rt.h:750: multiple definition of `prims_in_leafs'; bbox.o:/home/riscv/parsec-benchmark/ext/splash2x/apps/raytrace/obj/riscv64-linux.gcc/rt.h:750: first defined here
/usr/bin/ld: fbuf.o:/home/riscv/parsec-benchmark/ext/splash2x/apps/raytrace/obj/riscv64-linux.gcc/rt.h:750: multiple definition of `prims_in_leafs'; bbox.o:/home/riscv/parsec-benchmark/ext/splash2x/apps/raytrace/obj/riscv64-linux.gcc/rt.h:750: first defined here
/usr/bin/ld: geo.o:/home/riscv/parsec-benchmark/ext/splash2x/apps/raytrace/obj/riscv64-linux.gcc/rt.h:750: multiple definition of `prims_in_leafs'; bbox.o:/home/riscv/parsec-benchmark/ext/splash2x/apps/raytrace/obj/riscv64-linux.gcc/rt.h:750: first defined here
Run Code Online (Sandbox Code Playgroud)
用于编译的命令非常简单:
gcc -c -static-libgcc -pthread *.c
gcc *.o -pthread -o raytrace -L/usr/lib64 -L/usr/lib -lm
Run Code Online (Sandbox Code Playgroud)
现在,我发现头文件没有头文件保护,所以我想在某种程度上,在 amd64 上这个文件只包含一次,而在 riscv64 上多次。
另一方面,include "rt.h"is 在每个文件的顶部,在 any 之外#ifdef,所以我无法解释为什么它在 amd64 上成功,即使有标题保护,它在 riscv64 上仍然失败。
系统的区别在于:
我想我可以手动更改所有变量等,但这需要大量工作,而且仍然无法解释差异。
发生了什么?我怎样才能使程序编译?
示例文件在这里:
rt.h: https://pastebin.com/QKjvSe02main.c: https://pastebin.com/bT7meaMNcr.c: https://pastebin.com/Trck6imWgeo.c: https://pastebin.com/JY67u2Xe该错误消息并未说明该标头已多次包含在内。它表示相同的符号存在于多个目标文件中,而头文件只是该符号的来源。
如果一个 H 文件定义了一个符号,那么实际上每个包含它的 C 文件都会定义那个符号,因为只有编译后的文件才能定义一个符号,而 H 文件没有被编译,当这些 C 文件被编译时,它们会被包含到 C 文件中(实际上当它们被预处理时,但通常在它们被编译时发生)。如果多个 C 文件定义了同一个符号,被编译并链接在一起(注意这是一个ld错误,所以它是一个链接器错误),你最终会得到多个同名的符号,这是 C 标准不允许的.
如果 H 文件只是想通知 C 文件有关在别处(例如在另一个 C 文件中)定义的符号的存在和类型,则它必须仅通过添加前缀来声明符号extern。
// Declares that a symbol of type int named "a" exists somewhere
extern int a;
// Declares and defines a symbol of type int named "a" in the
// current compilation context
int a;
Run Code Online (Sandbox Code Playgroud)
所以行为 riscv64 是预期的,x86_64 上的行为不是。至少在严格遵循 C 标准时。正确的做法是只extern在 H 文件中有声明,并在编译的一个且仅一个C 文件中为它们匹配定义。这也是我编写 C 代码的方式,这也是我见过的大多数 C 代码的编写方式。
但显然,C 标准似乎存在一个共同的扩展。它是如此常见,以至于 C 标准本身在注释中提到了它。来自 C11 标准:
J.5 通用扩展
J.5.11 对象标识符可能有多个外部定义,无论是否显式使用关键字 extern;如果定义不一致,或者初始化了多个,则行为未定义(6.9.2)。
海湾合作委员会就是其中之一。当然,链接器也必须支持这一点,而 GNU 工具链确实支持这个扩展,它被称为Common Model。
在 GCC 10 之前,除非作为参数传递,否则使用通用模型-fno-common。从 GCC 10 开始,除非您-fcommon作为参数传递,否则不再使用它。
使长话短说:
它与体系结构(riscv64 vs x86_64)无关,这是因为您曾经使用过GCC 9.3和GCC 10.2,并且您编译的代码依赖于支持通用模型。
阅读有关此主题的有趣页面(这也是我找到答案信息的地方):
https://wiki.gentoo.org/wiki/Gcc_10_porting_notes/fno_common
| 归档时间: |
|
| 查看次数: |
151 次 |
| 最近记录: |