Question

Notre équipe utilise actuellement du code porté d'une ancienne architecture vers un nouveau produit basé sur la plateforme ARM Cortex M3 en utilisant une version personnalisée de GCC 4.5.1.Nous lisons les données d'une liaison de communication et essayons de convertir le tableau d'octets bruts en une structure pour analyser proprement les données.Après avoir lancé le pointeur vers une structure et déréférencé, nous recevons un avertissement :"le déréférencement d'un pointeur de type enfreint les règles strictes d'alias".

Après quelques recherches, j'ai réalisé que puisque le tableau de caractères n'a pas de règles d'alignement et que la structure doit être alignée sur les mots, la conversion des pointeurs provoque un comportement indéfini (une mauvaise chose).Je me demande s'il existe une meilleure façon de faire ce que nous essayons.

Je sais que nous pouvons explicitement aligner le tableau de caractères en utilisant " de GCCattribut ((aligné (4)))".Je pense que cela rendra notre code "plus sûr", mais les avertissements encombreront toujours nos builds, et je ne veux pas désactiver les avertissements au cas où cette situation se reproduirait.Ce que nous voulons, c'est un moyen de faire ce que nous essayons en toute sécurité, qui nous informera toujours si nous tentons de faire quelque chose de dangereux ailleurs plus tard.Puisqu’il s’agit d’un système intégré, l’utilisation de la RAM et de la mémoire flash est importante dans une certaine mesure.

La portabilité (compilateur et architecture) n'est pas un gros problème, cela ne concerne qu'un seul produit.Toutefois, si une solution portable existe, elle serait préférable.

Voici un exemple (très simplifié) de ce que nous faisons actuellement :

#define MESSAGE_TYPE_A 0
#define MESSAGE_TYPE_B 1

typedef struct MessageA __attribute__((__packed__))
{
    unsigned char  messageType;
    unsigned short data1;
    unsigned int   data2;
}

typedef struct MessageB __attribute__((__packed__))
{
    unsigned char  messageType;
    unsigned char  data3;
    unsigned char  data4;
}


// This gets filled by the comm system, assume from a UART interrupt or similar
unsigned char data[100];


// Assume this gets called once we receive a full message
void ProcessMessage()
{
    MessageA* messageA;
    unsigned char messageType = data[0];

    if (messageType == MESSAGE_TYPE_A)
    {
        // Cast data to struct and attempt to read
        messageA = (MessageA*)data; // Not safe since data may not be word aligned
                                    // This may cause undefined behavior

        if (messageA->data1 == 4) // warning would be here, when we use the data at the pointer
        {
            // Perform some action...
        }
    }
    // ...
    // process different types of messages
}
Était-ce utile?

La solution

Le cortex m3 peut gérer des accès non alignés très bien.Je l'ai fait dans des systèmes de traitement de paquets similaires avec le M3.Vous n'avez rien à faire, vous pouvez simplement utiliser le drapeau -fno-strict-aliasing pour vous débarrasser de l'avertissement.

Autres conseils

Comme cela a déjà été souligné, les pointeurs de casting sont une pratique louche.

Solution: Utilisez un syndicat

struct message {
  unsigned char messageType;
  union {
    struct {
      int data1;
      short data2;
    } A;
    struct {
      char data1[5];
      int data2;
    } B;
  } data;
};

void func (...) {
  struct message msg;
  getMessage (&msg);

  switch (msg.messageType) {
    case TYPEA:
      doStuff (msg.data.A.data1);
      break;
    case TYPEB:
      doOtherStuff (msg.data.B.data1);
      break;
  }
}

par cela signifie que le compilateur sait que vous accédez aux mêmes données via différents moyens, et les avertissements et les mauvaises choses disparaîtront.

de Cour, vous devez vous assurer que l'alignement et l'emballage de la structure correspondent au format de votre message.Méfiez-vous des problèmes d'endian et tels si la machine de l'autre extrémité du lien ne correspond pas.

Jeu de mots à travers une distribution de types différents de char * ou un pointeur vers une variante signée/non signée de char n'est pas strictement conforme car il viole les règles d'alias C (et parfois les règles d'alignement si aucune attention n'est apportée).

Cependant, gcc permet le jeu de mots à travers les types d'union.Page de manuel de gcc le documente explicitement :

La pratique consistant à lire un membre du syndicat différent de celui à qui on a écrit le plus récemment (appelée « type-punning ») est courante.Même avec -fstrict-aliasing, le type-punning est autorisé, à condition que la mémoire soit accessible via le type d'union.

Pour désactiver les optimisations liées aux règles d'alias avec gcc (et ainsi permettre au programme de briser les règles d'alias C), le programme peut être compilé avec : -fno-strict-aliasing.Notez qu'avec cette option activée, le programme n'est plus strictement conforme, mais vous avez dit que la portabilité n'était pas un souci.Pour information, le noyau Linux est compilé avec cette option.

GCC a un -fno-strict-aliasing indicateur qui désactivera les optimisations basées sur l'aliasing strict et sécurisera votre code.

Si vous cherchez vraiment un moyen de « réparer », vous devez repenser le fonctionnement de votre code.Vous ne pouvez pas simplement superposer la structure comme vous le souhaitez, vous devez donc faire quelque chose comme ceci :

MessageA messageA;
messageA.messageType = data[0];
// Watch out - endianness and `sizeof(short)` dependent!
messageA.data1 = (data[1] << 8) + data[2];
// Watch out - endianness and `sizeof(int)` dependent!
messageA.data2 = (data[3] << 24) + (data[4] << 16)
               + (data[5] <<  8) + data[6];

Cette méthode vous permettra d'éviter de compresser votre structure, ce qui pourrait également améliorer ses caractéristiques de performances ailleurs dans votre code.Alternativement:

MessageA messageA;
memcpy(&messageA, data, sizeof messageA);

Je le ferai avec vos structures emballées.Vous effectueriez les opérations inverses pour retraduire les structures dans un tampon plat si nécessaire.

Arrêtez d'utiliser des structures compactes et memcpy les champs individuels en variables de taille et de type corrects.Il s'agit d'un moyen sûr, portable et propre de réaliser ce que vous essayez d'accomplir.Si vous avez de la chance, gcc optimisera le petit fichier de taille fixe memcpy en quelques instructions simples de chargement et de stockage.

Pour les accès non alignés, regardez les macros Linux get_unaligned / put_unaligned.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top