Let me make this concrete again. With this test program:
struct test {
int a __attribute__((packed));
int b __attribute__((packed));
};
char c = 1;
struct test t = { .a=2, .b=3 };
what can I assume about the alignment of t.a? As I now understand it,
t.a and t.b both have alignment of 1, so t has alignment of 1, so t can
be packed immediately after c with no gap. It is then unsafe (on some
hardware) to take &t.a and pass it to a function that wants an int*.
What does gcc actually do? Well on x86, it does indeed pack t
immediately after c:
.file "test2.c"
globl c
.data
.type c, @object
.size c, 1
c:
.byte 1
globl t
.type t, @object
.size t, 8
t:
.long 2
.long 3
.ident "GCC: (GNU) 4.1.2 20060920 (prerelease) (Debian 4.1.1-14)"
.section .note.GNU-stack,"",@progbits
On the other hand, on ARM it leaves a gap:
.file "test2.c"
.global c
.data
.type c, %object
.size c, 1
c:
.byte 1
.global t
.align 2 <<<------
.type t, %object
.size t, 8
t:
.word 2
.word 3
.ident "GCC: (GNU) 4.1.2 20061028 (prerelease) (Debian 4.1.1-19)"
But, if I declare struct test like this:
struct test {
int a;
int b;
} __attribute__((packed));
then the ARM compiler generates the same layout as the x86 compiler,
without the gap.
The practical consequence of this is that making the "harmless" change
of moving the attribute from the members to the struct as a whole
causes code that previously worked to not work. Would you say that
such code was "always wrong", or is there an issue here?
Thanks for all the advice.
Phil.