Reputation: 15746
This is a follow-up question to the accepted answer to this other SO article. I think this stands alone in its own right, which is why I posted it.
I'm trying to "collect" structs defined in different modules into an ELF section. I'm doing this by way of GCC compiler __attributes__
. I'm not sure what's preventing this from working.
There are a number of related questions on SO and I've tried some of their ideas with the idea that something small in my code is the issue. For example, this one.
Update (I simplified the code some more)
#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;
}
What I am seeing:
$ ./a.out
element name: mary
1
2
element name:
element name:
element name:
Segmentation fault (core dumped)
What I expected to see:
$ ./a.out
element name: mary
1
2
element name: joe
2
element name: bob
2
Upvotes: 1
Views: 2083
Reputation: 39386
The underlying problem is that the C compiler and the linker do not agree on the alignment of the structures.
For the contents of a section, say foo
, to be treated as a single array by the C compiler, both the linker and the C compiler must agree on the size and alignment of each structure. The issue is that the linker typically uses much larger alignment than the C compiler, so successive symbols placed in the section have higher alignment than the C compiler expects.
The solution is to ensure both the C compiler and the linker agree on the alignment of the symbols placed in the section.
For example, if you have e.g.
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 }
};
the symbol placed by the linker is foo
, and it will be interpreted as the C compiler defined it. However, if we have
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
};
then foo1
and foo2
are placed by the linker, using whatever alignment it chooses; and to treat the entire foo
section as an array, our C definition of the structures must have a size or alignment that matches the linker alignment.
The solution is not to pack the structures, but to pad or align them to the alignment the linker actually uses; or to tell the linker to use the same alignment for the foo section as the C compiler uses for the structures.
There are many ways this can be achieved. Some suggest using a linker script, but I disagree: I prefer to align (using __attribute__((__aligned__(size)))
) or pad (using e.g. a trailing unsigned char padding[bytes];
) the structure instead, because it makes the code more portable across architectures and compilers (and most importantly, compiler versions) in my experience. Others probably disagree, but I can only comment on my experience, and what I have found to work best.
Because the linker alignment for the section may change, we'll certainly want it to be easily defined at compile time. The simplest option is to define a macro, say SECTION_ALIGNMENT
, that one can override at compile time (using e.g. -DSECTION_ALIGNMENT=32
gcc option). In the header file, if it is not defined, it should default to known values (8 for 32-bit arches, 16 for 64-bit arches in Linux, I believe):
#ifndef SECTION_ALIGNMENT
#if defined(__LP64__)
#define SECTION_ALIGNMENT 16
#else
#define SECTION_ALIGNMENT 8
#endif
#endif
and the C compiler is told that each such structure has that alignment,
struct foo {
/* ... Fields ... */
} __attribute__((__section__("foo"), __aligned__(SECTION_ALIGNMENT)));
so that both the C compiler and the linker agree on the size and alignment of each such structure placed in the foo
section.
Note that my related answer has a working example RPN calculator, using this exact mechanism to "register" operators supported by the calculator. If there are any objections to the content of this answer, I would greatly appreciate if one would test that real-world example first.
Upvotes: 1
Reputation: 141483
Padding. We all love padding so much.
Consider the following:
int main(void)
{
printf("%" PRIdPTR "\n",
(uintptr_t)&INFO_1 - (uintptr_t)&INFO_0);
printf("%" PRIdPTR "\n",
(uintptr_t)&__start_info[1] - (uintptr_t)&__start_info[0]);
return 0;
}
Do you think that &INFO_1 == &__start_info[1]
? Maybe. Maybe not. The output on my ArchLinux 4.16.8 gcc8.1 is:
288
272
Whoa. &__start_info[1] - &__start_info[0]
is equal to sizeof(struct mystruct) = 272
. Variables INFO_1 and INFO_0 reside in section named info
. But there is padding between them. Exactly 288 - 272 = 16
bytes of padding were added between the ending of INFO_1 and the beginning of INFO_2 variable.
Why? Because we can. I mean, the C compiler can. The C compiler may put any number of padding between any variables. Probably the 16 bytes of padding are because of some memory granulation or optimization.
Adding __attribute__((__aligned__(1)))
seems to fix the problem, at least on my PC. I don't think aligned(1)
was designed to remove padding between variables.
Maybe a better way (and still not reliable) is to store only pointers to structs in section (added VLA initialization, and ups, i reformatted a little):
#define DEFINE_INFO(...) \
__attribute__((__used__,__section__("info") /* maybe aligned(1) too? */ )) \
static const struct mystruct * const \
INFO_NAME(__COUNTER__) = &(const struct mystruct){ __VA_ARGS__ }
This works (I use that on at least 3 platforms with gcc), but this is still not reliable. Notice, that in this example only the pointers to variables will be stored in info section, the variables will be stored in other section. This is not how C was designed to be used.
We all know, that the only "good" way is to declare an array in this section, as the C compiler cannot put padding bytes between array members:
__attribute__((__used__,__section__("info")))
static const struct mystruct INFO[] = {
{
.name = "mary",
.on_init = _print_number,
.on_do_something = _print_number
},{
.name = "joe",
.on_do_something = _print_number
},{
.name = "bob",
.on_do_something = _print_number
}
};
, but that removes the fun of declaring a variable in one file, and then iterating over it in another... ; )
Upvotes: 0