我想将一些用户定义的数据放入自定义部分,以便同时由应用程序和离线分析器读取。假设以下示例:
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
变量是由我提供的宏生成的,它们可以出现在任何地方。
答案 0 :(得分:13)
为了一般的利益,我会重申你已经知道的和@Rumbaruk的内容
已经引用:gcc的文档明确限制section
属性应用于全局变量。所以
gcc行为的理想解决方法是让gcc不受barf限制或在不受支持的gcc特定语言应用程序上发出破坏的代码
延期。我们没有权利期望成功或期望成功始终如一。
这里有一个很长的解释,说明gcc如何以及为什么会产生部分类型冲突 编译错误和clang没有。如果不耐烦,请滚动到修正,但不要 期待一颗银弹。
出于演示目的,我将使用比实际更加逼真的程序 你发布了,即:
<强> 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;
}
目前,该程序在重现时会重现段类型冲突错误 用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()
带有外部链接的内联函数,用于归属链接部分
与非内联函数在同一翻译单元中的静态数据,
get_data()
,它将相同的链接部分归属于其自己的静态数据。
编译器采用不同的规则来生成get_data()
的链接
和inline_get_data()
分别。 get_data()
是一个简单的案例,inline_get_data()
很棘手。
要了解其中的差异,请暂时取消gcc部分冲突,将"custom"
替换为"custom.a"
中的get_data()
并将"custom"
替换为"custom.b"
} inline_get_data()
。
现在我们可以使用gcc编译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
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
,虽然它在语法上是块范围静态的,
已经成为唯一的全局符号(u
)。这是标准ELF的GNU扩展
符号绑定,要求运行时链接程序确保符号在整个符号中是唯一的
运行时链接。
在inline_get_data()
的这些不同的链接规定中,gcc正在应对它认为合适的情况
事实上该函数与外部链接内联。这个功能的事实
是内联意味着它必须在每个翻译单元中定义
使用它,以及它具有外部链接的事实意味着所有这些定义
必须解决相同的inline_data()::get_data
。因此块范围的静态变量必须,
为了联系目的,成为公共符号。
根据同样的动机,gcc与归属部分custom.a
的处理方式不同
在get_data()
中设置custom.b
和归因部分inline_get_data()
。
指定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
^^^^^
...
...
当custom.a
和custom.b
时,这是部分类型冲突错误的触发器
是一回事。 Gcc无法创建一个既有又有GROUP
的部分
属性。
现在,如果get_data()
和inline_get_data
在不同的翻译单元中定义了
编译器无法注意到冲突。那有什么关系呢?怎么会出错?
那个案子?
在这种情况下没有出错,因为在这种情况下, 没有部分类型冲突。
由custom
中的gcc生成的source.o
部分是 source.o
中的部分。它必须
要么具有或不具有GROUP
属性,要么两种方式都没有冲突
custom
中具有相同状态的other_source.o
部分。这些
是链接器的不同输入节。它将对输入custom
部分进行重复数据删除
GROUP
编辑,每个组名只保留其中一个。它不会那样做
输入custom
部分不是GROUPed
,最后它将合并
所有输入的custom
部分都留在二进制文件的一个输出custom
部分中,
暂停现在不适用的GROUP
属性。输出custom
部分将会
包含get_data()::data
作为本地符号,inline_get_data()::inline_data
作为唯一的全局符号。
冲突只包括编译器遇到关于source.o(custom)
部分的矛盾规则
应该是GROUP
。
为什么然后没有因为同样的矛盾而犯规?这是因为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无济于事
u
的GNU特定符号绑定唯一全局符号(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()
的弱约束
回答每个实现仅解决一个这样的符号的要求
进入联系的inline_get_data()
。这样可以将其从节类型中保存下来
冲突,但放弃了gcc更复杂方法的联动装甲。
你可以告诉 gcc放弃那种健壮性并采取类似铿锵的方式编译
-fno-gnu-unique
?你可以一点,但还不够。你可以给gcc选项
inline_get_data()::inline_data
指示编译器忘记GNU-specfic 唯一全局
符号绑定。如果你这样做,那么它将.cpp
一个微弱的象征,像铿锵;但这不会推动它 - 也许它应该 - 放弃部分分组
符号的属性部分的链接,您仍然会得到部分类型
冲突。我找不到任何选项来抑制这种相当细致的代码生成
你承认有臭味的问题代码的行为。
<强>修复强>
我们已经看到了gcc部分类型冲突是如何以及为什么由 存在于两个函数的定义的相同翻译单元中,一个与外部内联 链接,另一个不是内联的,每个都属于同一个链接部分 其静态数据。
我可以建议两种补救措施,其中一种简单而安全,但仅适用于一种变体 问题,另一个适用永远,但激烈和绝望。
简单安全的
冲突的函数定义可以通过两种方式进入相同的方法 翻译单位: -
static
)文件中定义。如果你有类型1的情况,那么对于任何编码的人来说,这只是一个蠢事
源文件使用外部链接对其中的内联函数进行编码。在这
如果内联函数是 local 到它的翻译单元,应该是static
。如果是
制作.section
然后gcc的外部连接消耗消失,而节类型消失
与他们发生冲突。你说你无法控制你的代码
属性部分是宏注入的,但它的作者应该是接受的
在源文件中编写内联外部函数而不是
标题是一个大错,并愿意纠正它。
极度绝望的人
2型病例的可能性更大。对于这些,据我所知,你的希望
是在你的gcc构建中注入一个程序集hack,以便gcc的.section
指令
关于内联外部函数定义中的属性部分是
在生成目标代码之前,以编程方式编辑为类似于clang。
显然,这样的解决方案仅适用于您知道的某些gcc版本
生成错误custom
指令的正确模式&#34;在哪个目标
你的纠正黑客,以及使用它的构建系统应该进行健全性检查
提前运行gcc版本。
必要的初步是修改生成.custom
部分的宏
属性,以便它不是统一生成节名.custom.1
而是生成
序列custom.2
,custom.N
,...,__COUNTER__
在a中的连续调用
翻译单位。使用内置#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__)))))
的预处理器来执行此操作,例如
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;
}
这一点只是让gcc预处理代码如:
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;
}
代码如下:
source.cpp
不会引发部分类型冲突。
有了这个并应用于g++ -S source.cpp
,您可以使用gcc:
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
其中comdat
是部门组名称和.section .custom.0,"a",@progbits
.section .custom.1,"a",@progbits
告诉链接器对此节组进行重复数据删除。
用clang重复此操作,并观察相应的指令是:
.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
替换表示:
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)
对于演示程序,假设对宏设备进行必要的更改, 可以在如下的makefile中制定出激烈的解决方案:
./prog
可以使用gcc构建246
,以满足打印期望%.o: %.cpp
在stdout上。
请注意makefile的三个细节: -
sed
这样的空模式规则来删除内置的make
这些规则的食谱。 *$$
命令中,我们需要$
作为eol标记来逃避扩展
-fno-gnu-unique
。这不是一个我想要暴露给开放用户群的解决方案,除非是止损。我没赢 如果从所有这些中获取的话是异议的话:是不是有更好的方法可以解决潜在的问题?
答案 1 :(得分:4)
与自定义部分混淆是一个肮脏的主题,因为gcc根据init值决定数据或bss,并且不希望你在那个级别搞乱它。
我建议用户数据是在正常使用数据时使用它 - 将其放入数据文件中。如果你坚持使用库,你至少可以让它使用自己的库,然后数据可以在正常的地方。
可以使用-fno-zero-initialized-in-bss
构建一个小型用户lib,以便在数据部分中包含所有用户数据,以便于解析。但是不要在你的二进制文件上这样做。
答案 2 :(得分: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;
}
答案 3 :(得分:0)
gcc文档(例如5.3)说:
将section属性与全局变量一起使用,而不是局部变量[...]
因此,您需要从函数中提取这些变量:
$jsonRequestURL = 'http://uat.xendpay.com/partner/api/1.0/{partner_key}/quote?countryFrom=PL&countryTo=GB¤cyFrom=PLN¤cyTo=GBP&deliveryType=BANK_TRANSFER&paymentMethod=banktransferlocal&amount=1000.00&amountCurrency=PLN&indent=true';
json_decode ( file_get_contents ( $jsonRequestURL ) );
这与gcc-5.2和clang-3.5.1
编译得很好