c - 如何使用 gcc 编译器 __attributes__ 在 ELF 部分中收集结构?
问题描述
这是对其他 SO 文章的已接受答案的后续问题。我认为这本身就是独立的,这就是我发布它的原因。
我正在尝试将不同模块中定义的结构“收集”到 ELF 部分中。我通过 GCC 编译器来做到这一点__attributes__
。我不确定是什么阻止了它的工作。
关于 SO 有许多相关问题,我已经尝试了他们的一些想法,认为我的代码中的一些小问题就是问题所在。比如这个。
更新(我进一步简化了代码)
#include <stdio.h>
#define INFO_NAME(counter) INFO_CAT(INFO_, counter)
#define INFO_CAT(a, b) INFO_DUMMY() a ## b
#define INFO_DUMMY()
#define DEFINE_INFO(data...) \
const static struct mystruct INFO_NAME(__COUNTER__) \
__attribute((__section__("info"))) \
__attribute((__used__)) = { data } \
struct mystruct
{
char name[255];
int (*on_init) (int num1);
int (*on_do_something) (int num1);
};
extern struct mystruct __start_info[];
extern struct mystruct __stop_info[];
static int _print_number(int x)
{
printf("%d\n", x);
}
DEFINE_INFO(
.name = "mary",
.on_init = _print_number,
.on_do_something = _print_number
);
DEFINE_INFO(
.name = "joe",
.on_do_something = _print_number
);
DEFINE_INFO(
.name = "bob",
.on_do_something = _print_number
);
int main(void)
{
struct mystruct *iter = &__start_info;
for ( ; iter < &__stop_info; ++iter)
{
printf("element name: %s\n", iter->name);
if (iter->on_init != NULL)
{
iter->on_init(1);
}
if (iter->on_do_something != NULL)
{
iter->on_do_something(2);
}
}
return 0;
}
我所看到的:
$ ./a.out
element name: mary
1
2
element name:
element name:
element name:
Segmentation fault (core dumped)
我期望看到的:
$ ./a.out
element name: mary
1
2
element name: joe
2
element name: bob
2
解决方案
根本问题是 C 编译器和链接器在结构的对齐方式上不一致。
对于foo
要被 C 编译器视为单个数组的节的内容,链接器和 C 编译器必须就每个结构的大小和对齐方式达成一致。问题是链接器通常使用比 C 编译器大得多的对齐方式,因此放置在节中的连续符号具有比 C 编译器预期的更高的对齐方式。
解决方案是确保 C 编译器和链接器都同意放置在节中的符号的对齐方式。
例如,如果您有
static struct {
int i;
double d;
char c;
float f;
} foo[] __attribute__((__used__, __section__("foo"))) = {
{ 1, 1.0, '1', 1.0f },
{ 2, 2.0, '2', 2.0f }
};
链接器放置的符号是foo
,它将被解释为 C 编译器定义它。但是,如果我们有
static struct {
int i;
double d;
char c;
float f;
} foo1 __attribute__((__used__, __section__("foo"))) = {
1, 1.0, '1', 1.0f
};
static struct {
int i;
double d;
char c;
float f;
} foo2 __attribute__((__used__, __section__("foo"))) = {
2, 2.0, '2', 2.0f
};
然后foo1
并由foo2
链接器放置,使用它选择的任何对齐方式;并且要将整个foo
部分视为一个数组,我们对结构的 C 定义必须具有与链接器对齐匹配的大小或对齐方式。
解决方案不是打包结构,而是将它们填充或对齐到链接器实际使用的对齐方式;或者告诉链接器对 foo 部分使用与 C 编译器对结构使用相同的对齐方式。
有很多方法可以实现这一点。有些人建议使用链接器脚本,但我不同意:我更喜欢对齐(使用__attribute__((__aligned__(size)))
)或填充(使用例如尾随unsigned char padding[bytes];
)结构,因为它使代码在架构和编译器(最重要的是编译器版本)之间更具可移植性我的经验。其他人可能不同意,但我只能评论我的经验,以及我发现最有效的方法。
因为节的链接器对齐可能会改变,我们当然希望它在编译时容易定义。最简单的选择是定义一个宏,比如说SECTION_ALIGNMENT
,一个可以在编译时覆盖的宏(例如使用-DSECTION_ALIGNMENT=32
gcc 选项)。在头文件中,如果没有定义,它应该默认为已知值(我相信 8 代表 32 位拱门,16 代表 Linux 中的 64 位拱门):
#ifndef SECTION_ALIGNMENT
#if defined(__LP64__)
#define SECTION_ALIGNMENT 16
#else
#define SECTION_ALIGNMENT 8
#endif
#endif
并且 C 编译器被告知每个这样的结构都有这种对齐方式,
struct foo {
/* ... Fields ... */
} __attribute__((__section__("foo"), __aligned__(SECTION_ALIGNMENT)));
以便 C 编译器和链接器都同意放置在foo
节中的每个此类结构的大小和对齐方式。
请注意,我的相关答案有一个工作示例 RPN 计算器,使用这种确切的机制来“注册”计算器支持的运算符。如果对此答案的内容有任何异议,我将不胜感激,如果有人能先测试这个真实世界的例子。
推荐阅读
- ios - 从关闭初始化时,有时不会调用 UIButton 选择器
- vue.js - Vue:动态类中的数组长度条件
- pytorch - 如何理解mbart中的decoder_start_token_id和forced_bos_token_id?
- javascript - 使用 JQuery 修改和删除 CSS 类属性
- ssl - Hashicorp 的 Vault CLI 不在令牌助手中存储身份验证令牌
- kotlin - 无法从适配器类向另一个活动发送数据
- javascript - 如何在 VScode 中使用文档对象模型 (DOM)?
- struct - srcpy和struct问题,动态数组新增struct book记录
- python-3.x - 是否可以控制函数如何将 np.array 向量化为参数?
- javascript - 当使用 babel.config.js 以及在 webpack babel 加载器中指定选项时,它会合并两者还是忽略 babel.config.js?