内联静态数据导致段类型冲突
Inline static data causes a section type conflict
我想将一些用户定义的数据放入自定义部分,以供应用程序和离线分析器同时读取。假设以下示例:
const int* get_data()
{
__attribute__((section(".custom")))
static const int data = 123;
return & data;
}
inline const int* inline_get_data()
{
__attribute__((section(".custom")))
static const int inline_data = 123;
return & inline_data;
}
int main()
{
(void) get_data();
(void) inline_get_data();
return 0;
}
data
和inline_data
的值将出现在.custom
部分。当 __attributes__
被 corresponding pragmas.
替换时,Clang 编译此示例并产生正确的结果,就像 MSVC 所做的那样
不幸的是,GCC 5.2 给出了以下错误:
error: inline_data causes a section type conflict with data
问题归结为两个变量有不同的联系(data
在flagged部分和a
部分,inline_data
部分是用 aG
标记)。如果第二个函数没有被标记为内联而是一个模板(GCC 5.2 编译它),GCC 4.9 也会以同样的方式失败。
如果在生成的程序集中临时更改并手动修复了一个节名称,GCC 5.2 也可以正常编译。
这个问题有什么已知的解决方法吗?我无法控制函数签名,*data
变量是由我提供的宏生成的,它们可以出现在任何地方。
为了大家好,我会重申你已经知道的和@Rumbaruk
已经引用:gcc的文档明确限制section
属性的应用为global变量。所以
gcc 行为的理想解决方法是让 gcc 在不受支持的 gcc-specific 语言应用程序上不呕吐或发出损坏的代码
延期。我们没有权利期望成功或期望成功一再重复table.
这里有关于 gcc 如何以及为什么产生 section-type 冲突的长篇解释
编译错误和 clang 没有。如果不耐烦,请滚动到 Fixes,但不要
期待银弹。
出于演示目的,我将使用比
您已发布,即:
source.cpp
const int* get_data()
{
__attribute__((section(".custom")))
static const int data = 123;
return & data;
}
inline const int* inline_get_data()
{
__attribute__((section(".custom")))
static const int inline_data = 123;
return & inline_data;
}
const int* other_get_data()
{
return inline_get_data();
}
header.h
#ifndef HEADER_H
#define HEADER_H
extern const int* get_data();
extern const int* other_get_data();
#endif
main.cpp
#include "header.h"
#include <iostream>
int main()
{
std::cout << (*get_data() + *other_get_data()) << std::endl;
return 0;
}
就目前而言,此程序重现了 section-type 冲突错误
用 gcc 5.2 编译:
$ g++-5 -Wall -pedantic -c source.cpp
source.cpp:12:22: error: inline_data causes a section type conflict with data
static const int inline_data = 123;
^
Clang (3.6/3.7) 无投诉:
$ clang++ -Wall -pedantic -I. -o prog main.cpp source.cpp
$ ./prog
246
gcc 阻塞的根源在于 inline_get_data()
是
内联函数,带有外部链接,属于链接部分
到与 non-inline 函数相同的翻译单元中的静态数据,
get_data()
,将相同的链接部分归于它自己的静态数据。
编译器采用不同的规则为get_data()
生成链接
和 inline_get_data()
分别。 get_data()
是简单的情况,inline_get_data()
是棘手的情况。
为了看到区别,让我们通过在 get_data()
中将 "custom"
替换为 "custom.a"
并将 "custom"
替换为 "custom.b"
来暂时消除 gcc 部分冲突inline_get_data()
.
现在我们可以用 gcc 编译 source.cpp
并检查相关符号 table 条目:
$ objdump -C -t source.o | grep get_data
0000000000000000 l O .custom.a 0000000000000004 get_data()::data
0000000000000000 l d .text._Z15inline_get_datav 0000000000000000 .text._Z15inline_get_datav
0000000000000000 g F .text 000000000000000b get_data()
0000000000000000 u O .custom.b 0000000000000004 inline_get_data()::inline_data
0000000000000000 w F .text._Z15inline_get_datav 000000000000000b inline_get_data()
000000000000000b g F .text 000000000000000b other_get_data()
get_data()
当然已经做了全局符号(g
)和get_data()::data
做了
本地符号 (l
)。但是 inline_get_data()
已经变成了 weak,既不是全局的也不是本地的
符号 (w
) 和 inline_get_data()::inline_data
,虽然它在句法上是 block-scope 静态的,
已成为 唯一的全局符号 (u
)。这是标准 ELF 的 GNU 扩展
符号绑定要求运行时链接器确保符号在整个过程中是唯一的
运行时链接。
在 inline_get_data()
的这些不同链接规定中,gcc 正在处理它认为合适的情况
事实上,该函数 与外部链接 内联。函数的事实
is inline 表示必须定义在每个翻译单元中
它被使用,并且它具有 external linkage 的事实意味着所有这些定义
必须地址相同 inline_data()::get_data
。因此 block-scope 静态变量必须,
出于链接目的,成为 public 符号。
出于同样的动机,gcc 对属性部分的处理方式不同 custom.a
get_data()
的设置和 inline_get_data()
中的属性部分 custom.b
。
指定 inline_get_data()::inline_data
一个唯一的全局符号后,它想要
确保该符号的多个定义不会被链接引入
从不同的翻译单元多次复制 custom.b
部分。为此,它
将 GROUP
链接器属性应用于 custom.b
:这(跳过详细信息)启用它
生成一个 .section
指令,将 custom.b
分配给一个命名的 section-group 和
指示链接器只保留该节组的一个副本。观察:
$ readelf -t source.o
...
...
[ 7] .custom.a
PROGBITS PROGBITS 0000000000000000 0000000000000068 0
0000000000000004 0000000000000000 0 4
[0000000000000002]: ALLOC
[ 8] .custom.b
PROGBITS PROGBITS 0000000000000000 000000000000006c 0
0000000000000004 0000000000000000 0 4
[0000000000000202]: ALLOC, GROUP
^^^^^
...
...
而这是section-type冲突错误的触发器,当custom.a
和custom.b
是一样的。 Gcc 无法制作既有又没有 GROUP
的部分
属性。
现在如果 get_data()
和 inline_get_data
在不同的翻译单元中定义
编译器无法注意到冲突。那有什么关系呢?会出现什么问题
那种情况?
在那种情况下没有问题,因为在那种情况下 是 没有 section-type 冲突。
gcc 在 source.o
中生成的一个部分 custom
是 在 source.o
中的一个部分。它必须
有或没有 GROUP
属性,但无论哪种方式都不会与
other_source.o
中的同名部分 custom
具有相反的状态。这些
是链接器的不同输入部分。它将对输入 custom
部分进行重复数据删除
已 GROUP
ed,每个 group-name 仅保留其中一个。它不会那样做
输入 custom
部分不是 GROUPed
,最后它会合并
它留下的所有输入 custom
部分进入二进制文件中的一个输出 custom
部分,
现在 non-applicable GROUP
属性被丢弃。该输出 custom
部分将
包含 get_data()::data
作为局部符号和 inline_get_data()::inline_data
作为唯一的全局符号。
冲突仅在于编译器遇到关于 source.o(custom)
节是否存在的矛盾规则
应 GROUP
ed 与否。
为什么clang不符合同样的矛盾呢?这是因为 clang 需要
一个更简单但有点具有外部链接的 内联函数问题的鲁棒性较低的方法
包含静态数据.
坚持 custom.a
和 custom.b
部分的区别,现在让我们用 clang 编译 source.cpp
并检查相关符号和部分特征:
$ objdump -C -t source.o | grep get_data
0000000000000000 l O .custom.a 0000000000000004 get_data()::data
0000000000000000 l d .text._Z15inline_get_datav 0000000000000000 .text._Z15inline_get_datav
0000000000000010 g F .text 000000000000000b other_get_data()
0000000000000000 w F .text._Z15inline_get_datav 0000000000000010 inline_get_data()
0000000000000000 g F .text 0000000000000010 get_data()
0000000000000000 w O .custom.b 0000000000000004 inline_get_data()::inline_data
这里与 gcc 的输出有一处不同。正如我们所料,clang 没有用
GNU-specific 符号绑定 唯一全局符号 (u
) 用于 inline_get_data()::inline_data
。
它使它成为一个弱符号,就像 inline_get_data()
本身一样。
对于部分特征,我们有:
$ readelf -t source.o
...
...
[ 8] .custom.a
PROGBITS PROGBITS 0000000000000000 0000000000000080 0
0000000000000004 0000000000000000 0 4
[0000000000000002]: ALLOC
[ 9] .custom.b
PROGBITS PROGBITS 0000000000000000 0000000000000084 0
0000000000000004 0000000000000000 0 4
[0000000000000002]: ALLOC
...
...
没有区别,所以不冲突。这就是为什么我们可以替换部分名称 custom.a
和 custom.b
与 custom
,每个原始文件,并成功编译。
Clang 依赖于 inline_get_data()::inline_data
的弱绑定
回答每个实现只处理一个这样的符号的要求
进入链接的 inline_get_data()
。这将它从 section-type 中保存下来
冲突,但放弃了 gcc 更复杂方法的链接 armour-plating。
你能告诉 gcc 放弃这种健壮性并采用 clang-like 编译方式吗
inline_get_data()
?您可以 一点 ,但还不够。你可以给 gcc 选项
-fno-gnu-unique
指示编译器忘记 GNU-specfic unique global
符号绑定。如果你这样做,那么它将 inline_get_data()::inline_data
弱符号,如 clang;但这不会推动它 - 也许它应该 - 放弃 section-grouping
链接符号的属性部分,你仍然会得到 section-type
冲突。我找不到抑制这个的选项 nitty-gritty code-generation
您公认的臭问题代码的行为。
修复
我们已经了解了 gcc section-type 冲突是如何以及为什么由
存在于两个函数定义的同一翻译单元中,一个 与外部内联
linkage,另一个不是inline,每个都属于同一个linkage section
到它的静态数据。
我可以建议两种补救措施,其中一种简单且安全但仅适用于一种变体
的问题,另一个适用总是,但激烈和绝望。
简单安全的
冲突的函数定义可以通过两种方式进入相同的
翻译单位:-
- 它们都在同一个源 (
.cpp
) 文件中定义。
- non-inline 函数在源文件中定义,其中包含 header
其中定义了内联函数。
如果你有类型 1 的情况,那么这只是代码编写者的一个错误
源文件以使用外部链接在其中编写内联函数。在这个
如果内联函数是 local 到它的翻译单元,应该是 static
。如果是
使 static
然后 gcc 的外部链接作用消失并且 section-type
与他们发生冲突。你说过你无法控制你的代码
属性部分内容是 macro-injected,但其作者应该可以接受
事实上,在源文件中编写内联外部函数而不是
aheader是个错误,愿意改正。
绝命毒师
第 2 类情况的可能性更大。对于这些,据我所知,你的唯一希望
是将程序集 hack 注入到你的 gcc 构建中,以便 gcc 的 .section
指令
关于内联外部函数定义中的属性部分是
在生成 object 代码之前以编程方式编辑为 clang-like。
显然,这样的解决方案仅适用于您知道的某些 gcc 版本集
生成目标 "right pattern of wrong .section
directives"
你的纠正技巧,以及使用它的构建系统应该 sanity-check
提前运行gcc版本。
必要的准备工作是修改生成 custom
部分的宏
属性,而不是统一生成节名 .custom
而是生成
序列 .custom.1
, custom.2
,...,custom.N
在连续调用中
翻译单位。使用内置预处理器 __COUNTER__
来执行此操作,例如
#define CAT2(x,y) x##y
#define CONCAT(x,y) CAT2(x,y)
#define QUOT(x) #x
#define QUOTE(x) QUOT(x)
#define SET_SECT() __attribute__((section(QUOTE(CONCAT(.custom.,__COUNTER__)))))
这样做的目的只是让 gcc 像这样预处理代码:
const int* get_data()
{
SET_SECT()
static const int data = 123;
return & data;
}
inline const int* inline_get_data()
{
SET_SECT()
static const int inline_data = 123;
return & inline_data;
}
进入如下代码:
const int* get_data()
{
__attribute__((section(".custom.0")))
static const int data = 123;
return & data;
}
inline const int* inline_get_data()
{
__attribute__((section(".custom.1")))
static const int inline_data = 123;
return & inline_data;
}
不会引起 section-type 冲突。
有了这个并应用到 source.cpp
,您可以 assemble 使用 gcc 的文件:
g++ -S source.cpp
并在输出 source.s
中观察到没有问题的部分 custom.0
获取 .section
指令:
.section .custom.0,"a",@progbits
而有问题的部分 custom.1
得到:
.section .custom.1,"aG",@progbits,_ZZ15inline_get_datavE11inline_data,comdat
其中 _ZZ15inline_get_datavE11inline_data
是 section-group 名称,comdat
告诉链接器删除这个 section-group.
用 clang 重复此操作并观察相应的指令是:
.section .custom.0,"a",@progbits
.section .custom.1,"a",@progbits
除了部分名称外没有区别。
所以驴bly hack 你需要的是一个可以变成以下任何一个的东西:
.section .custom.0,"a",@progbits
.section .custom.1,"aG",@progbits,_ZZ15inline_get_datavE11inline_data,comdat
进入:
.section .custom,"a",@progbits
这可以用sed
替换来表示:
s|^\t\.section\t\.custom\.[0-9]\{1,\},"a\(G\)*",@progbits.*$|\t\.section\t\.custom,"a",@progbits|g
对于演示程序,假设对宏设备进行必要的改动,
可以在 makefile 中制定激进的解决方案,如下所示:
CXX ?= g++
SRCS = main.cpp source.cpp
ASMS = $(SRCS:.cpp=.s)
OBJS = $(SRCS:.cpp=.o)
CPPFLAGS = -I.
CXXFLAGS = -fno-gnu-unique
%.o: %.cpp
%.s: %.cpp
%.s: %.cpp
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -S -o $@ $<
%.o: %.s
%.o: %.s
sed -i 's|^\t\.section\t\.custom\.[0-9]\{1,\},"a\(G\)*",@progbits.*$$|\t\.section\t\.custom,"a",@progbits|g' $<
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<
.PHONY: all clean
.INTERMEDIATE: $(ASMS)
all: prog
prog: $(OBJS)
$(CXX) -o $@ $^
clean:
rm -f prog $(OBJS) $(ASMS)
从中可以用 gcc 构建一个 ./prog
来满足打印的期望 246
在标准输出上。
注意makefile的三个细节:-
- 我们需要编写像
%.o: %.cpp
这样的空模式规则来删除make的内置
这些规则的食谱。
- 在
sed
命令中我们需要*$$
作为eol-marker来转义make-expansion的
$
.
-fno-gnu-unique
在编译器标志中传递给 fill-out clang 模仿。
除了 stop-gap 之外,这不是我想要暴露给开放 user-base 的解决方案。我不会
异议,如果 take-away 来自所有的是:难道没有更好的方法来解决潜在的问题吗?
弄乱自定义部分是一个有点肮脏的话题,因为 gcc 根据 init 值决定数据或 bss,并且不希望您在那个级别弄乱它。
我对 用户数据 的建议是像通常使用数据一样使用它 - 将它放在数据文件中。如果非要用library的话,至少可以让它用自己的library,这样数据就可以在正常的地方了。
一个很小的用户库,可以用-fno-zero-initialized-in-bss
构建,将所有用户数据放在数据部分以便于解析。但是不要在你的二进制文件上这样做。
gcc 文档(例如 5.3)说:
Use the section attribute with global variables and not local variables [...]
因此,您需要从函数中提取这些变量:
__attribute__((section(".custom"))) static const int data = 123;
__attribute__((section(".custom"))) static const int inline_data = 123;
const int* get_data()
{
return &data;
}
inline const int* inline_get_data()
{
return &inline_data;
}
int main()
{
(void)get_data();
(void)inline_get_data();
}
这与 gcc-5.2 和 clang-3.5.1 编译得很好
我终于找到了满意的解决方案。它实际上只是已知技术的组合。运行时使用一个普通的静态变量,其地址通过内联汇编放入自定义段。在不同的平台(clang、MSVC)上,__attribute__
或 #pragma
可以使用相同的结果,没有 ASM。这个解决方案可以很容易地包装成一个通用的、平台不可知的宏。
const int* get_data()
{
static const int data = 123;
__asm__(
".pushsection .custom, \"?\", @progbits" "\n"
".quad %c0" "\n"
".popsection" "\n"
: : "i"(&data)
);
return & data;
}
inline const int* inline_get_data()
{
static const int inline_data = 123;
__asm__(
".pushsection .custom, \"?\", @progbits" "\n"
".quad %c0" "\n"
".popsection" "\n"
: : "i"(&inline_data)
);
return & inline_data;
}
int main()
{
(void) get_data();
(void) inline_get_data();
return 0;
}
我想将一些用户定义的数据放入自定义部分,以供应用程序和离线分析器同时读取。假设以下示例:
const int* get_data()
{
__attribute__((section(".custom")))
static const int data = 123;
return & data;
}
inline const int* inline_get_data()
{
__attribute__((section(".custom")))
static const int inline_data = 123;
return & inline_data;
}
int main()
{
(void) get_data();
(void) inline_get_data();
return 0;
}
data
和inline_data
的值将出现在.custom
部分。当 __attributes__
被 corresponding pragmas.
不幸的是,GCC 5.2 给出了以下错误:
error: inline_data causes a section type conflict with data
问题归结为两个变量有不同的联系(data
在flagged部分和a
部分,inline_data
部分是用 aG
标记)。如果第二个函数没有被标记为内联而是一个模板(GCC 5.2 编译它),GCC 4.9 也会以同样的方式失败。
如果在生成的程序集中临时更改并手动修复了一个节名称,GCC 5.2 也可以正常编译。
这个问题有什么已知的解决方法吗?我无法控制函数签名,*data
变量是由我提供的宏生成的,它们可以出现在任何地方。
为了大家好,我会重申你已经知道的和@Rumbaruk
已经引用:gcc的文档明确限制section
属性的应用为global变量。所以
gcc 行为的理想解决方法是让 gcc 在不受支持的 gcc-specific 语言应用程序上不呕吐或发出损坏的代码
延期。我们没有权利期望成功或期望成功一再重复table.
这里有关于 gcc 如何以及为什么产生 section-type 冲突的长篇解释 编译错误和 clang 没有。如果不耐烦,请滚动到 Fixes,但不要 期待银弹。
出于演示目的,我将使用比 您已发布,即:
source.cpp
const int* get_data()
{
__attribute__((section(".custom")))
static const int data = 123;
return & data;
}
inline const int* inline_get_data()
{
__attribute__((section(".custom")))
static const int inline_data = 123;
return & inline_data;
}
const int* other_get_data()
{
return inline_get_data();
}
header.h
#ifndef HEADER_H
#define HEADER_H
extern const int* get_data();
extern const int* other_get_data();
#endif
main.cpp
#include "header.h"
#include <iostream>
int main()
{
std::cout << (*get_data() + *other_get_data()) << std::endl;
return 0;
}
就目前而言,此程序重现了 section-type 冲突错误 用 gcc 5.2 编译:
$ g++-5 -Wall -pedantic -c source.cpp
source.cpp:12:22: error: inline_data causes a section type conflict with data
static const int inline_data = 123;
^
Clang (3.6/3.7) 无投诉:
$ clang++ -Wall -pedantic -I. -o prog main.cpp source.cpp
$ ./prog
246
gcc 阻塞的根源在于 inline_get_data()
是
内联函数,带有外部链接,属于链接部分
到与 non-inline 函数相同的翻译单元中的静态数据,
get_data()
,将相同的链接部分归于它自己的静态数据。
编译器采用不同的规则为get_data()
生成链接
和 inline_get_data()
分别。 get_data()
是简单的情况,inline_get_data()
是棘手的情况。
为了看到区别,让我们通过在 get_data()
中将 "custom"
替换为 "custom.a"
并将 "custom"
替换为 "custom.b"
来暂时消除 gcc 部分冲突inline_get_data()
.
现在我们可以用 gcc 编译 source.cpp
并检查相关符号 table 条目:
$ objdump -C -t source.o | grep get_data
0000000000000000 l O .custom.a 0000000000000004 get_data()::data
0000000000000000 l d .text._Z15inline_get_datav 0000000000000000 .text._Z15inline_get_datav
0000000000000000 g F .text 000000000000000b get_data()
0000000000000000 u O .custom.b 0000000000000004 inline_get_data()::inline_data
0000000000000000 w F .text._Z15inline_get_datav 000000000000000b inline_get_data()
000000000000000b g F .text 000000000000000b other_get_data()
get_data()
当然已经做了全局符号(g
)和get_data()::data
做了
本地符号 (l
)。但是 inline_get_data()
已经变成了 weak,既不是全局的也不是本地的
符号 (w
) 和 inline_get_data()::inline_data
,虽然它在句法上是 block-scope 静态的,
已成为 唯一的全局符号 (u
)。这是标准 ELF 的 GNU 扩展
符号绑定要求运行时链接器确保符号在整个过程中是唯一的
运行时链接。
在 inline_get_data()
的这些不同链接规定中,gcc 正在处理它认为合适的情况
事实上,该函数 与外部链接 内联。函数的事实
is inline 表示必须定义在每个翻译单元中
它被使用,并且它具有 external linkage 的事实意味着所有这些定义
必须地址相同 inline_data()::get_data
。因此 block-scope 静态变量必须,
出于链接目的,成为 public 符号。
出于同样的动机,gcc 对属性部分的处理方式不同 custom.a
get_data()
的设置和 inline_get_data()
中的属性部分 custom.b
。
指定 inline_get_data()::inline_data
一个唯一的全局符号后,它想要
确保该符号的多个定义不会被链接引入
从不同的翻译单元多次复制 custom.b
部分。为此,它
将 GROUP
链接器属性应用于 custom.b
:这(跳过详细信息)启用它
生成一个 .section
指令,将 custom.b
分配给一个命名的 section-group 和
指示链接器只保留该节组的一个副本。观察:
$ readelf -t source.o
...
...
[ 7] .custom.a
PROGBITS PROGBITS 0000000000000000 0000000000000068 0
0000000000000004 0000000000000000 0 4
[0000000000000002]: ALLOC
[ 8] .custom.b
PROGBITS PROGBITS 0000000000000000 000000000000006c 0
0000000000000004 0000000000000000 0 4
[0000000000000202]: ALLOC, GROUP
^^^^^
...
...
而这是section-type冲突错误的触发器,当custom.a
和custom.b
是一样的。 Gcc 无法制作既有又没有 GROUP
的部分
属性。
现在如果 get_data()
和 inline_get_data
在不同的翻译单元中定义
编译器无法注意到冲突。那有什么关系呢?会出现什么问题
那种情况?
在那种情况下没有问题,因为在那种情况下 是 没有 section-type 冲突。
gcc 在 source.o
中生成的一个部分 custom
是 在 source.o
中的一个部分。它必须
有或没有 GROUP
属性,但无论哪种方式都不会与
other_source.o
中的同名部分 custom
具有相反的状态。这些
是链接器的不同输入部分。它将对输入 custom
部分进行重复数据删除
已 GROUP
ed,每个 group-name 仅保留其中一个。它不会那样做
输入 custom
部分不是 GROUPed
,最后它会合并
它留下的所有输入 custom
部分进入二进制文件中的一个输出 custom
部分,
现在 non-applicable GROUP
属性被丢弃。该输出 custom
部分将
包含 get_data()::data
作为局部符号和 inline_get_data()::inline_data
作为唯一的全局符号。
冲突仅在于编译器遇到关于 source.o(custom)
节是否存在的矛盾规则
应 GROUP
ed 与否。
为什么clang不符合同样的矛盾呢?这是因为 clang 需要 一个更简单但有点具有外部链接的 内联函数问题的鲁棒性较低的方法 包含静态数据.
坚持 custom.a
和 custom.b
部分的区别,现在让我们用 clang 编译 source.cpp
并检查相关符号和部分特征:
$ objdump -C -t source.o | grep get_data
0000000000000000 l O .custom.a 0000000000000004 get_data()::data
0000000000000000 l d .text._Z15inline_get_datav 0000000000000000 .text._Z15inline_get_datav
0000000000000010 g F .text 000000000000000b other_get_data()
0000000000000000 w F .text._Z15inline_get_datav 0000000000000010 inline_get_data()
0000000000000000 g F .text 0000000000000010 get_data()
0000000000000000 w O .custom.b 0000000000000004 inline_get_data()::inline_data
这里与 gcc 的输出有一处不同。正如我们所料,clang 没有用
GNU-specific 符号绑定 唯一全局符号 (u
) 用于 inline_get_data()::inline_data
。
它使它成为一个弱符号,就像 inline_get_data()
本身一样。
对于部分特征,我们有:
$ readelf -t source.o
...
...
[ 8] .custom.a
PROGBITS PROGBITS 0000000000000000 0000000000000080 0
0000000000000004 0000000000000000 0 4
[0000000000000002]: ALLOC
[ 9] .custom.b
PROGBITS PROGBITS 0000000000000000 0000000000000084 0
0000000000000004 0000000000000000 0 4
[0000000000000002]: ALLOC
...
...
没有区别,所以不冲突。这就是为什么我们可以替换部分名称 custom.a
和 custom.b
与 custom
,每个原始文件,并成功编译。
Clang 依赖于 inline_get_data()::inline_data
的弱绑定
回答每个实现只处理一个这样的符号的要求
进入链接的 inline_get_data()
。这将它从 section-type 中保存下来
冲突,但放弃了 gcc 更复杂方法的链接 armour-plating。
你能告诉 gcc 放弃这种健壮性并采用 clang-like 编译方式吗
inline_get_data()
?您可以 一点 ,但还不够。你可以给 gcc 选项
-fno-gnu-unique
指示编译器忘记 GNU-specfic unique global
符号绑定。如果你这样做,那么它将 inline_get_data()::inline_data
弱符号,如 clang;但这不会推动它 - 也许它应该 - 放弃 section-grouping
链接符号的属性部分,你仍然会得到 section-type
冲突。我找不到抑制这个的选项 nitty-gritty code-generation
您公认的臭问题代码的行为。
修复
我们已经了解了 gcc section-type 冲突是如何以及为什么由 存在于两个函数定义的同一翻译单元中,一个 与外部内联 linkage,另一个不是inline,每个都属于同一个linkage section 到它的静态数据。
我可以建议两种补救措施,其中一种简单且安全但仅适用于一种变体 的问题,另一个适用总是,但激烈和绝望。
简单安全的
冲突的函数定义可以通过两种方式进入相同的 翻译单位:-
- 它们都在同一个源 (
.cpp
) 文件中定义。 - non-inline 函数在源文件中定义,其中包含 header 其中定义了内联函数。
如果你有类型 1 的情况,那么这只是代码编写者的一个错误
源文件以使用外部链接在其中编写内联函数。在这个
如果内联函数是 local 到它的翻译单元,应该是 static
。如果是
使 static
然后 gcc 的外部链接作用消失并且 section-type
与他们发生冲突。你说过你无法控制你的代码
属性部分内容是 macro-injected,但其作者应该可以接受
事实上,在源文件中编写内联外部函数而不是
aheader是个错误,愿意改正。
绝命毒师
第 2 类情况的可能性更大。对于这些,据我所知,你的唯一希望
是将程序集 hack 注入到你的 gcc 构建中,以便 gcc 的 .section
指令
关于内联外部函数定义中的属性部分是
在生成 object 代码之前以编程方式编辑为 clang-like。
显然,这样的解决方案仅适用于您知道的某些 gcc 版本集
生成目标 "right pattern of wrong .section
directives"
你的纠正技巧,以及使用它的构建系统应该 sanity-check
提前运行gcc版本。
必要的准备工作是修改生成 custom
部分的宏
属性,而不是统一生成节名 .custom
而是生成
序列 .custom.1
, custom.2
,...,custom.N
在连续调用中
翻译单位。使用内置预处理器 __COUNTER__
来执行此操作,例如
#define CAT2(x,y) x##y
#define CONCAT(x,y) CAT2(x,y)
#define QUOT(x) #x
#define QUOTE(x) QUOT(x)
#define SET_SECT() __attribute__((section(QUOTE(CONCAT(.custom.,__COUNTER__)))))
这样做的目的只是让 gcc 像这样预处理代码:
const int* get_data()
{
SET_SECT()
static const int data = 123;
return & data;
}
inline const int* inline_get_data()
{
SET_SECT()
static const int inline_data = 123;
return & inline_data;
}
进入如下代码:
const int* get_data()
{
__attribute__((section(".custom.0")))
static const int data = 123;
return & data;
}
inline const int* inline_get_data()
{
__attribute__((section(".custom.1")))
static const int inline_data = 123;
return & inline_data;
}
不会引起 section-type 冲突。
有了这个并应用到 source.cpp
,您可以 assemble 使用 gcc 的文件:
g++ -S source.cpp
并在输出 source.s
中观察到没有问题的部分 custom.0
获取 .section
指令:
.section .custom.0,"a",@progbits
而有问题的部分 custom.1
得到:
.section .custom.1,"aG",@progbits,_ZZ15inline_get_datavE11inline_data,comdat
其中 _ZZ15inline_get_datavE11inline_data
是 section-group 名称,comdat
告诉链接器删除这个 section-group.
用 clang 重复此操作并观察相应的指令是:
.section .custom.0,"a",@progbits
.section .custom.1,"a",@progbits
除了部分名称外没有区别。
所以驴bly hack 你需要的是一个可以变成以下任何一个的东西:
.section .custom.0,"a",@progbits
.section .custom.1,"aG",@progbits,_ZZ15inline_get_datavE11inline_data,comdat
进入:
.section .custom,"a",@progbits
这可以用sed
替换来表示:
s|^\t\.section\t\.custom\.[0-9]\{1,\},"a\(G\)*",@progbits.*$|\t\.section\t\.custom,"a",@progbits|g
对于演示程序,假设对宏设备进行必要的改动, 可以在 makefile 中制定激进的解决方案,如下所示:
CXX ?= g++
SRCS = main.cpp source.cpp
ASMS = $(SRCS:.cpp=.s)
OBJS = $(SRCS:.cpp=.o)
CPPFLAGS = -I.
CXXFLAGS = -fno-gnu-unique
%.o: %.cpp
%.s: %.cpp
%.s: %.cpp
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -S -o $@ $<
%.o: %.s
%.o: %.s
sed -i 's|^\t\.section\t\.custom\.[0-9]\{1,\},"a\(G\)*",@progbits.*$$|\t\.section\t\.custom,"a",@progbits|g' $<
$(CXX) $(CPPFLAGS) $(CXXFLAGS) -c -o $@ $<
.PHONY: all clean
.INTERMEDIATE: $(ASMS)
all: prog
prog: $(OBJS)
$(CXX) -o $@ $^
clean:
rm -f prog $(OBJS) $(ASMS)
从中可以用 gcc 构建一个 ./prog
来满足打印的期望 246
在标准输出上。
注意makefile的三个细节:-
- 我们需要编写像
%.o: %.cpp
这样的空模式规则来删除make的内置 这些规则的食谱。 - 在
sed
命令中我们需要*$$
作为eol-marker来转义make-expansion的$
. -fno-gnu-unique
在编译器标志中传递给 fill-out clang 模仿。
除了 stop-gap 之外,这不是我想要暴露给开放 user-base 的解决方案。我不会 异议,如果 take-away 来自所有的是:难道没有更好的方法来解决潜在的问题吗?
弄乱自定义部分是一个有点肮脏的话题,因为 gcc 根据 init 值决定数据或 bss,并且不希望您在那个级别弄乱它。
我对 用户数据 的建议是像通常使用数据一样使用它 - 将它放在数据文件中。如果非要用library的话,至少可以让它用自己的library,这样数据就可以在正常的地方了。
一个很小的用户库,可以用-fno-zero-initialized-in-bss
构建,将所有用户数据放在数据部分以便于解析。但是不要在你的二进制文件上这样做。
gcc 文档(例如 5.3)说:
Use the section attribute with global variables and not local variables [...]
因此,您需要从函数中提取这些变量:
__attribute__((section(".custom"))) static const int data = 123;
__attribute__((section(".custom"))) static const int inline_data = 123;
const int* get_data()
{
return &data;
}
inline const int* inline_get_data()
{
return &inline_data;
}
int main()
{
(void)get_data();
(void)inline_get_data();
}
这与 gcc-5.2 和 clang-3.5.1 编译得很好
我终于找到了满意的解决方案。它实际上只是已知技术的组合。运行时使用一个普通的静态变量,其地址通过内联汇编放入自定义段。在不同的平台(clang、MSVC)上,__attribute__
或 #pragma
可以使用相同的结果,没有 ASM。这个解决方案可以很容易地包装成一个通用的、平台不可知的宏。
const int* get_data()
{
static const int data = 123;
__asm__(
".pushsection .custom, \"?\", @progbits" "\n"
".quad %c0" "\n"
".popsection" "\n"
: : "i"(&data)
);
return & data;
}
inline const int* inline_get_data()
{
static const int inline_data = 123;
__asm__(
".pushsection .custom, \"?\", @progbits" "\n"
".quad %c0" "\n"
".popsection" "\n"
: : "i"(&inline_data)
);
return & inline_data;
}
int main()
{
(void) get_data();
(void) inline_get_data();
return 0;
}