(Oui, je vais répondre à ma propre question.)
Oui, __attribute__((packed))
est potentiellement dangereux sur certains systèmes. Le symptôme sans doute de ne pas s'afficher sur un système x86, qui rend le problème plus insidieux; tester sur les systèmes x86 ne révèlerai pas le problème. (Sur x86, mal alignées les accès sont traitées dans le matériel; si vous déréférencer un int*
pointeur qui pointe vers une drôle de adresse, ce sera un peu plus lent que si il a été correctement aligné, mais vous aurez à obtenir le résultat correct.)
Sur certains systèmes, tels que SPARC, essayant d'accéder à un mal alignées int
objet provoque une erreur de bus, le plantage du programme.
Il y a eu aussi des systèmes où le mauvais alignement de l'accès tranquillement ignore les bits de l'adresse, à l'origine pour accéder à la mauvaise partie de la mémoire.
Considérons le programme suivant:
#include <stdio.h>
#include <stddef.h>
int main(void)
{
struct foo {
char c;
int x;
} __attribute__((packed));
struct foo arr[2] = { { 'a', 10 }, {'b', 20 } };
int *p0 = &arr[0].x;
int *p1 = &arr[1].x;
printf("sizeof(struct foo) = %d\n", (int)sizeof(struct foo));
printf("offsetof(struct foo, c) = %d\n", (int)offsetof(struct foo, c));
printf("offsetof(struct foo, x) = %d\n", (int)offsetof(struct foo, x));
printf("arr[0].x = %d\n", arr[0].x);
printf("arr[1].x = %d\n", arr[1].x);
printf("p0 = %p\n", (void*)p0);
printf("p1 = %p\n", (void*)p1);
printf("*p0 = %d\n", *p0);
printf("*p1 = %d\n", *p1);
return 0;
}
Sur x86 Ubuntu avec gcc 4.5.2, il produit la sortie suivante:
sizeof(struct foo) = 5
offsetof(struct foo, c) = 0
offsetof(struct foo, x) = 1
arr[0].x = 10
arr[1].x = 20
p0 = 0xbffc104f
p1 = 0xbffc1054
*p0 = 10
*p1 = 20
Sur SPARC Solaris 9 avec gcc 4.5.1, il produit est le suivant:
sizeof(struct foo) = 5
offsetof(struct foo, c) = 0
offsetof(struct foo, x) = 1
arr[0].x = 10
arr[1].x = 20
p0 = ffbff317
p1 = ffbff31c
Bus error
Dans les deux cas, le programme est compilé sans options supplémentaires, juste gcc packed.c -o packed
.
(Un programme qui utilise un seul struct plutôt que le tableau n'est pas fiable présenter le problème, car le compilateur ne peut allouer de la structure sur un impair de l'adresse de l' x
membre est correctement aligné. Avec un tableau de deux struct foo
objets, l'un ou l'autre aura un mal alignées x
membre.)
En se référant à la membre de l' x
d'un struct foo
par nom, le compilateur sait qu' x
est potentiellement mal aligné, et va générer un code supplémentaire pour accéder correctement.
Une fois l'adresse de l' arr[0].x
ou arr[1].x
a été stockée dans un pointeur d'objet, ni le compilateur ni le programme en cours d'exécution sait qu'il pointe vers un désabusé int
objet. Juste suppose qu'il est correctement aligné, résultant (sur certains systèmes) dans une erreur de bus ou de tout autre manquement similaire.
La fixation de ce ccag serait, je crois, être peu pratique. Une solution générale, il faudrait, pour chaque tentative de déréférencer un pointeur vers n'importe quel type de non triviale de l'alignement des exigences (a) prouver au moment de la compilation que le pointeur ne pointe pas vers un désabusé membre d'un pique-struct, ou (b) générer plus volumineux et plus lent code peut gérer soit aligné ou mal alignées objets.
J'ai envoyé un gcc rapport de bug. Comme je l'ai dit, je ne crois pas que c'est pratique pour régler le problème, mais le document doit mentionner qu'il (elle ne dispose actuellement pas).