GCC 错误“<variable> 导致节类型冲突”

GCC error "<variable> causes a section type conflict"

为什么在尝试将两个 (RAM) 变量(仅初始化值不同)放入同一部分时会出现编译器错误?

问题

C 源代码:

int __attribute__((section(".in_my_data"))) _foo = 1;
int __attribute__((section(".in_my_data"))) _bar = 0;

(相关的)GCC 编译器输出:

mcve/main.c:75:45: error: _bar causes a section type conflict

链接器脚本在 SECTIONS 定义中包含以下行,但(致命)错误来自编译器,而不是链接器。

.my_data : { *(.in_my_data) } > data

更多信息

更改 C 源代码以允许编译器使用两个部分允许编译通过,但如果两个输入部分映射到同一输出部分,链接器随后会生成错误。

C 源代码:

int __attribute__((section(".in_my_data_nonzero"))) _foo = 1;
int __attribute__((section(".in_my_data_zero"))) _bar = 0;

链接描述文件:

.my_data : { *(.in_my_data*) } > data

(相关的)链接器输出:

Link Error: attributes for input section '.in_my_data_nonzero' conflict
with output section '.my_data'

交换 C 源代码中行的顺序只会改变错误部分(C 源代码中第二个出现的部分)。

问题

对于初始化为零的变量,GCC 编译器需要什么属性,而对于初始化为非零的变量则不需要,反之亦然?

编译器是否试图将初始化为零的变量放在 .bss 部分,而不是初始化数据的 .data 部分?或者是否有另一个初始化为零的数据部分?

相关问题

类似的问题出现涵盖内存类型(ROM 与 RAM)之间的冲突问题:

... 或将初始化的 const 数据放入 NOLOAD 输出部分:

... 或成因仍是个谜,可能与以下有关:

据我所知,上面的

None 似乎有一个我可以应用于此问题的答案。

警告:此答案可能仅适用于 Microchip XC16 编译器。

研究

编译器将属性分配给 C 变量,以便将它们分配给特定的部分,如下所示(有关 XC16 的特定信息,请参见下面的 注 1)。

  • int a = 1; - 分配给 .data.
  • int b = 0; - 分配给 .bss.
  • int c; - 分配给 .bss.

第一个和最后一个有意义:.data 用于初始化数据,.bss 用于未初始化数据。然而,.bss 数据也被 ANSI C 启动设置为零(参见 注 2)。

回答

编译器似乎包含 初始化的变量,但其值等于 .bss 部分中的所有位 0 以及所有那些根本没有初始化的。

根据wikipedia

An implementation may also assign statically-allocated variables and constants initialized with a value consisting solely of zero-valued bits to the BSS section.

解决方法

GCC 有一个选项 -fno-zero-initialized-in-bss,可用于强制所有初始化为零的变量进入 .data 部分,如 this answer 中所述。这可以应用于每个源文件,但不能应用于单个变量。

如意算盘

如果有一个 __attribute__((**doload**)) 可以用来强制编译器在 .data 而不是 .bss 中放置一个零初始化变量,那就太好了。

备注

注1:XC16编译器可能使用.ndata.nbss来表示地址0x8000以下的near数据。

注意 2:用 __attribute__((noload)) 标记变量将导致变量被排除在 .bss 部分之外。 XC16 生成一个特定的输出部分,每个变量都有一个(GUID?)唯一名称。

默认情况下,GCC 将 classes 对象放在不同的部分,可执行代码到 .text,初始化数据到 .data,静态数据到 .bss,还有一些更模糊的。

如果您试图强制两个不同 class 的对象属于同一部分,GCC 将引发此部分错误。

解决方法是将它们放在不同的段中,最后,告诉链接器最终将这些对象合并在同一个段中,例如:

.boot : {                 // target ELF section will contain....
    *(.boot)              // executable code
    *(.boot_rodata)       // constant data
    *(.boot_bss)          // static data
} > BOOT                  // to finally be place in this area