IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C Discussion :

problème : adresse d'une variable globale modifiée


Sujet :

C

  1. #1
    Membre chevronné
    Profil pro
    Inscrit en
    Septembre 2009
    Messages
    1 860
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2009
    Messages : 1 860
    Par défaut problème : adresse d'une variable globale modifiée
    Bonjour,

    J'ai un problème sur un µControlleur 32bits (PIC32MX) avec le code suivant. L'adresse de la variable gblVar est modifiée lors d'appel de fonctions :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    extern STRUCT_GLB gblVar; // défini dans un autre fichier .c
    int foo1(int a, int b, int c, int d);
    int foo2(int a, int b, int c, int d, MY_STRUCT * e);
     
    void init(void){
    	int a, b, c, d;
     
    	//...
     
    	printf("%p\n", &gblVar);
    	foo1(a, b, c, d);
    	printf("%p\n", &gblVar); // erreur : l'adresse a été modifiée (les deux octets de poids fort de l'adresse ont été mis à 0)
     
    	gblVar.x = 10; // le CPU génère une exception car l'adresse de la variable n'est plus valide
     
    	//...
    }
    Dans un autre fichier .c, j'ai :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
     
    int foo1(int a, int b, int c, int d);
    int foo2(int a, int b, int c, int d, MY_STRUCT * e);
     
    int foo1(int a, int b, int c, int d){
     
    	MY_STRUCT e;
     
    	e.a = 10;
    	// ...
    	e.z = 12;
     
    	return foo2(a, b, c, d, &e);
    }
     
    int foo2(int a, int b, int c, int d, MY_STRUCT * e){
     
    	// ...
     
    	return x;
    }

    Dans la fonction foo1(), si je remplace :
    Par :
    => je n'ai plus le problème.

    De quoi pourrait venir le problème ? Bug du compilateur/linkeur ?

    Merci d'avance

  2. #2
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 496
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 496
    Billets dans le blog
    1
    Par défaut
    Comment est définie la variable globale ? En quoi intervient-elle dans les fonctions foo1 et foo2 ?

  3. #3
    Membre chevronné
    Profil pro
    Inscrit en
    Septembre 2009
    Messages
    1 860
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2009
    Messages : 1 860
    Par défaut
    Dans un fichier .c, elle est déclarée comme ceci :
    un fichier .h lui est associé pour pouvoir l'utiliser dans d'autre fichiers .c :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    extern APP_STRUCT gblVar;
    La variable gblVar n'est pas utilisé dans les fonctions fooX(), elle est juste utilisé dans la fonction init() ... j'ai l'impression qu'il y a une altération de la pile mais je ne vois rien qui pourrait causer ça dans les fonctions foo1 et foo2.
    => je n'ai aucun warning/erreur lors de la compilation


    Si je modifie foo1() comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    int foo1(int a, int b, int c, int d){
     
    	MY_STRUCT e;
     
    	e.a = 10;
    	// ...
    	e.z = 12;
     
    	/* *** modif ***** */
    	int val;
    	val = foo2(a, b, c, d, &e);
     
    	printf("%p\n", &gblVar);
     
    	return val;
    }
    Le printf m'affiche l'adresse non erronée

  4. #4
    Membre Expert
    Avatar de skeud
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Juin 2011
    Messages
    1 091
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 35
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2011
    Messages : 1 091
    Billets dans le blog
    1
    Par défaut
    Tu as passé un coup de valgrind pour vérifier que tu n'as pas des erreurs cachées ailleur? En général, les soucis de pile se résolve facilement avec valgrind.

    J'ai un doute quand au return, ta variable e ne serait pas détruite avant l'appel de la fonction foo2?
    Genre si tu essaye de récupérer le retour de foo2 puis de le retourner, il se passe quoi?

  5. #5
    Membre chevronné
    Profil pro
    Inscrit en
    Septembre 2009
    Messages
    1 860
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2009
    Messages : 1 860
    Par défaut
    Hello, j'ai identifié l'erreur.

    Voici un exemple simplifié du problème.

    Je lance la fonction foo1 depuis ma fonction principale :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
     void foo1(){    
         MY_STRUCT aaa;    
         printf("sizeof(aaa) = %u\n", sizeof(aaa)); // ça affiche "sizeof(aaa) = 48"   // pourquoi la valeur ne vaut pas 64 ????????????????  
        // => Quand je lis le code assembleur (disassembly listing) de foo1(), la taille de aaa vaut 48 octets dans la stack software.
        //     Le backup des registres internes du CPU S0-S3 est placé dans la stack juste après aaa, ce qui fait que lors du memcpy du foo2(), ces valeurs sont écrasées => c'est ce qui génère le bug je pense, car le context est érroné lorsuqe je reviens dans ma fonction principale
     
         foo2(&aaa);    
     }    
     
     void foo2(MY_STRUCT * p){    
         memcpy(p, &glbVar2, sizeof(*p));    
     }
    => je n'ai aucune erreur de compilation


    J'ai controllé le code assembleur (disassembly listing) de foo2()
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
     
    // Disassembly listing of foo2() function    
     734:                 void foo2(MY_STRUCT * p){    
     9D055188  27BDFFE8   ADDIU SP, SP, -24    
     9D055190  AFBF0014   SW RA, 20(SP)    
     735:                     memcpy(p, &glbVar, sizeof(*p));    
     9D055184  3C05A000   LUI A1, -24576    
     9D05518C  24A53E40   ADDIU A1, A1, 15936    
     9D055194  0F41A184   JAL 0x9D068610    
     9D055198  24060040   ADDIU A2, ZERO, 64 ///////////////////// value is good  ////////////////////    
     736:                 }    
     9D05519C  8FBF0014   LW RA, 20(SP)    
     9D0551A0  03E00008   JR RA    
     9D0551A4  27BD0018   ADDIU SP, SP, 24
    => il semble être bon

    Définition de ma structure :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    #define TAB_ELMT_COUNT 2    
     typedef struct {  
         unsigned int x; // 4 bytes  
         unsigned int y; // 4 bytes  
     } INFO_STRUCT1;  
     typedef struct {  
         unsigned int a; // 4 bytes  
         unsigned int b; // 4 bytes  
     } INFO_STRUCT2;  
     
     typedef enum {  
         MY_ENUM_A = 0,  
         MY_ENUM_B = 0x8223,  
         MY_ENUM_C = 0x8823  
     } MY_ENUM; // 4 bytes  
     typedef struct {  
         MY_ENUM a; // 4 bytes  
         UINT16 b; // 2 bytes  
         UINT8 c; // 1 byte  
         // 1 bytes (padding)  
     
         BOOL d; // 4 bytes  
     } TAB_ELMT_STRUCT; // 12 bytes  
     
     typedef struct {  
         INFO_STRUCT1 info1; // 8 bytes  
         INFO_STRUCT2 info2; // 8 bytes  
     
         TAB_ELMT_STRUCT tab1[TAB_ELMT_COUNT]; // 12x2 = 24 bytes   
         TAB_ELMT_STRUCT tab2[TAB_ELMT_COUNT]; // 12x2 = 24 bytes   
     } MY_STRUCT; // 64 bytes
    => je n'arrive pas à voir pourquoi il y a un écart de 16 octets sur la taille de la structure suivant la fonction qui l'utilise.

    Une idée ?

    Je vois plusieurs causes possibles :
    - compilateur qui bug
    - déclaration de ma structure fausse (problème d'alignement) : pourtant elle me semble bonne (de plus je ne vois pas d'où pourrait venir la différence de 16 octets)...
    - options du compilateur fausses (pourtant, il me semble avoir les mêmes paramètres de configuration entre tous les fichiers .c)

  6. #6
    Expert éminent
    Avatar de Médinoc
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Septembre 2005
    Messages
    27 398
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 41
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Septembre 2005
    Messages : 27 398
    Par défaut
    Es-tu sûr des tailles de BOOL, int, MY_ENUM et TAB_ELEMENT_STRUCT sur ton microcontrôleur?

    À ta place, j'afficherais un sizeof de chaque, pour vérifier.
    SVP, pas de questions techniques par MP. Surtout si je ne vous ai jamais parlé avant.

    "Aw, come on, who would be so stupid as to insert a cast to make an error go away without actually fixing the error?"
    Apparently everyone.
    -- Raymond Chen.
    Traduction obligatoire: "Oh, voyons, qui serait assez stupide pour mettre un cast pour faire disparaitre un message d'erreur sans vraiment corriger l'erreur?" - Apparemment, tout le monde. -- Raymond Chen.

  7. #7
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 496
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 496
    Billets dans le blog
    1
    Par défaut
    As-tu vérifié les tailles de tous tes types avec des sizeof ? On a parfois des surprises.

    La norme dans mon souvenir ne garantit pas qu'un type énuméré soit codé sur 32 bits.

    Le padding, c'est parfois surprenant également.

    Moi, j'essaye de compter et j'arrive à ça par exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    #define TAB_ELMT_COUNT 2    
     typedef struct {  
         unsigned int x; // 4 bytes  
         unsigned int y; // 4 bytes  
     } INFO_STRUCT1;  
     
     typedef struct {  
         unsigned int a; // 4 bytes  
         unsigned int b; // 4 bytes  
     } INFO_STRUCT2;  
     
     typedef enum {  
         MY_ENUM_A = 0,  
         MY_ENUM_B = 0x8223,  
         MY_ENUM_C = 0x8823  
     } MY_ENUM; // 1 bytes  
     
     typedef struct {  
         MY_ENUM a; // 1 bytes  
         UINT16 b; // 2 bytes  
         UINT8 c; // 1 byte  
         BOOL d; // 4 bytes  
     } TAB_ELMT_STRUCT; // 8 bytes  
     
     typedef struct {  
         INFO_STRUCT1 info1; // 8 bytes  
         INFO_STRUCT2 info2; // 8 bytes  
     
         TAB_ELMT_STRUCT tab1[TAB_ELMT_COUNT]; // 8x2 = 16 bytes   
         TAB_ELMT_STRUCT tab2[TAB_ELMT_COUNT]; // 8x2 = 16 bytes   
     } MY_STRUCT; // 48 bytes
    CQFD ?

  8. #8
    Membre chevronné
    Profil pro
    Inscrit en
    Septembre 2009
    Messages
    1 860
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2009
    Messages : 1 860
    Par défaut
    Merci, c'est bon j'ai trouvé : vous n'auriez pas pu trouver car j'ai trop simplifié mon code, il aurait fallut rajouter ça :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    typedef struct {  
    #ifdef TOTO
         INFO_STRUCT1 info1; // 8 bytes  
         INFO_STRUCT2 info2; // 8 bytes  
    #endif
    
         TAB_ELMT_STRUCT tab1[TAB_ELMT_COUNT]; // 8x2 = 16 bytes   
         TAB_ELMT_STRUCT tab2[TAB_ELMT_COUNT]; // 8x2 = 16 bytes   
     } MY_STRUCT; // 48 bytes
    Le problème est que j'utilise une libray qui inclus les fichiers .h n'importe comment, ce qui fait que TOTO n''était pas défini dans tout les fichier .c
    => j'ai vu le probleme en faisant un sizeof sur .info1 : erreur de compilation car membre inconnu dans certains fichiers .c

    Merci pour votre aide (voici comment perdre 3 jours sur un truc a la c...)

  9. #9
    Membre chevronné
    Profil pro
    Inscrit en
    Septembre 2009
    Messages
    1 860
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2009
    Messages : 1 860
    Par défaut
    Citation Envoyé par Bktero Voir le message
    La norme dans mon souvenir ne garantit pas qu'un type énuméré soit codé sur 32 bits.
    La norme dit que le type énuméré est de type int => la norme dit qu'un int fait au minimum 16bits : cependant, j'ai remarqué qu'en général (mais ce n'est pas marqué dans la norme), ça correspond à la taille de stockage des données par défaut du CPU (ex: 32bits) => ce qui me semble logique car les données de la taille du CPU sont traité plus rapidement que les autre (ex : sur un CPU 32bits, les données 16bits sont traitées plus lentement que des données 32bits car il faut faire des masquage).

    Pour l'alignement, j'ai remarqué que les données étaient alignées sur une adresse qui est divisible par leur taille.
    ex pour des variables de 1 octet => elles sont positionnées aux adresses 0,1,2,3,4,...
    ex pour des variables de 4 octets => elles sont positionnées aux adresses 0,4,8,12,16,...
    => ce n'est pas décrit dans la norme, mais je n'ai jamais eu de surprise en utilisant cette méthode pour déterminer où se situaient les octets de padding dans une structure (a condition de ne pas utilisé des directives spéciales visant a supprimer le padding sur les structures).

  10. #10
    Modérateur

    Avatar de Bktero
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Juin 2009
    Messages
    4 496
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 38
    Localisation : France, Loire Atlantique (Pays de la Loire)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels

    Informations forums :
    Inscription : Juin 2009
    Messages : 4 496
    Billets dans le blog
    1
    Par défaut
    Par contre, je me pose vraiment une question : quel rapport avec la variable globale ?

  11. #11
    Membre chevronné
    Profil pro
    Inscrit en
    Septembre 2009
    Messages
    1 860
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Septembre 2009
    Messages : 1 860
    Par défaut
    L'explication est un peu complexe : il faut comprendre comment fonctionne le CPU.
    Le CPU a différents registres internes pour faire ses traitements. Parmis ces registres, il y a les registres s0-s7.
    => Définition de s0-s7 : Saved temporary - caller must preserve contents
    => En gros, si une fonction veut modifier la valeur de ces registres, elle doit auparavant sauvegarder ces valeurs (dans la pile) afin de pouvoir restaurer les valeurs initiales avant de rendre la main à la fonction appelante.


    Dans mon premier message, j'ai trop simplifié mon exemple : foo1() et foo2() ne sont pas dans le même fichier .c.
    foo1() ne voit pas le #define TOTO alors que foo2() le voit.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    typedef struct {  
    #ifdef TOTO
         INFO_STRUCT1 info1; // 8 bytes  
         INFO_STRUCT2 info2; // 8 bytes  
    #endif
     
         TAB_ELMT_STRUCT tab1[TAB_ELMT_COUNT]; // 8x2 = 16 bytes   
         TAB_ELMT_STRUCT tab2[TAB_ELMT_COUNT]; // 8x2 = 16 bytes   
     } MY_STRUCT; // 48 ou 64 bytes suivant si TOTO est actif
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    void init(void){
    	int a, b, c, d;
     
    	//...
     
    	printf("%p\n", &gblVar);
    	foo1(a, b, c, d);
    	printf("%p\n", &gblVar); // erreur : l'adresse a été modifiée
     
    	gblVar.x = 10; // le CPU génère une exception car l'adresse de la variable n'est plus valide
     
    	//...
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
     
    int foo1(int a, int b, int c, int d){
     
    	MY_STRUCT e;
     
    	e.a = 10;
    	// ...
    	e.z = 12;
     
    	return foo2(a, b, c, d, &e);
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    // contenu de foo2()
    int foo2(int a, int b, int c, int d, MY_STRUCT * e){
     
    	// ...
    	memcpy(e, &tata, sizeof(*e));
    	// ...
    	return x;
    }
    Déroulement du programe
    # Execution de init() :
    - l'adressse de gblVar est stockée dans le registre interne s0 du CPU pendant tout le traitement de la fonction (j'ai vu ça en regardant le code assembleur de la fonction).
    - la fonction appel la fonction foo1()

    # Execution de foo1() :
    - la fonction enregistre les valeurs de s0-s7 dans la pile
    - ensuite elle utilise les registres s0-s7 pour faire différents traitements
    - la fonction appele la fonction foo2() en passant en paramètre l'adresse de la variable e. Cette variable temporaire est donc stockée dans la pile juste avant l'emplacement mémoire où sont stockés s0-s7 (j'ai vérifié le code assembleur). A cause du #define TOTO manquant pour le fichier .c, la taille de la structure de la variable "e" est donc réduite par rapport à la taille de la variable vu par foo2().

    # Execution de foo2() :
    - la fonction exécute le memcpy. Vu que dans le fichier .c #define TOTO est bien déclaré, le memcpy écrase les valeurs de s0-s7 qui sont dans la pile
    - on sort de la fonction

    # Execution de foo1() :
    - la fonction restaure les valeurs erronées de s0-s7 qui sont dans la pile
    - on sort de la fonction

    # Execution de init() :
    - init() execute le printf en utilisant la valeur de s0 comme désignant l'adresse de gblVar=> erreur système car adresse contenue dans s0 invalide


    ... en espérant que mon explication a été clair.

    PS : je pensais que le fait de déclarer la variable e en static résolvait le bug mais ce n'est pas totalement vrai car ça doit causer un bug ailleurs je pense.

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. problème avec une variable globale(SDL)
    Par rayman77 dans le forum C
    Réponses: 1
    Dernier message: 20/02/2009, 13h20
  2. modifier une variable globale
    Par bobo696 dans le forum Débuter
    Réponses: 2
    Dernier message: 22/01/2009, 11h42
  3. Problème pour modifier une variable globale
    Par supertom dans le forum EDI, CMS, Outils, Scripts et API
    Réponses: 6
    Dernier message: 07/06/2007, 18h00
  4. Problème d'adresse dans une variable
    Par mick77 dans le forum Macros et VBA Excel
    Réponses: 3
    Dernier message: 07/06/2007, 12h41
  5. Modifier une variable globale
    Par bahet dans le forum XSL/XSLT/XPATH
    Réponses: 2
    Dernier message: 19/04/2006, 18h04

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo