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

x86 32-bits / 64-bits Assembleur Discussion :

[GCC] Plantage du CPU en cas de certains overflows


Sujet :

x86 32-bits / 64-bits Assembleur

  1. #1
    Membre du Club
    Profil pro
    Inscrit en
    Avril 2012
    Messages
    72
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2012
    Messages : 72
    Points : 48
    Points
    48
    Par défaut [GCC] Plantage du CPU en cas de certains overflows
    Bonjour

    Ma configuration est : Windows 10 + Code::Blocks + TDM64 (gcc 64 bits).

    D’un part, on peut dire qu’un programme qui plante est un programme mal conçu, peu importe la nature du plantage. Mais d’autre part, il est rare qu’un programme soit correct du premier coup et lors de la mise au point les messages du compilo sont bien utiles. Ma question se situe dans ce cadre.

    Le problème est le suivant (il peut être constaté avec le petit programme donné à la fin) :

    1. en cas de division par zéro, le programme plante (normal !) mais le CPU ne plante pas : il stoppe le programme et envoi le message d’erreur
      Process returned -1073741676 (0xC0000094)
      ce qui donne en clair : EXCEPTION_INT_DIVIDE_BY_ZERO
    2. en cas d’overflow le programme peut éventuellement poursuivre (et donnera alors des résultats faux) mais, en cas de plantage, je m’attendais à un comportement identique : un truc du genre
      Process returned -1*?????? (0xC00*????)
      qui en clair correspondrait à EXCEPTION_INT_OVERFLOW
      Mais là, non seulement le programme plante mais le CPU aussi : même au bout de 60 secondes, pas d’interruption, pas de message d’erreur, il semble perdu dans une boucle infinie …



    Mes questions : ne serait-il pas préférable de faire apparaître un message d’erreur dans les deux cas ? Que le CPU plante (je ne parle pas du programme), trouvez-vous cela « normal » ou même « banal » ? Sinon, est-ce dû à ma configuration, à un défaut de conception du CPU ou du compilo ? Bref j’aimerais connaître vos avis (éclairés) sur le sujet.

    Merci d’avance

    --------- Programme -------------------

    Pour tester div_test() et div_bug_test(), décommenter la ligne concernée.

    PS : dans le cas de la division par zéro, il semble que le compilo court-circuite la partie try/catch. Si c’est dû au fait que cette partie est mal conçue, merci de m’indiquer la bonne manière.

    Code C++ : 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
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
     
    #include <iostream>
     
    using namespace std;
     
    int divise(int x, int n);
    int divise_bug(int x, int n);
    void div_test(int x, int y);
    void div_bug_test(int x, int y);
     
    ////////////////////////
     
    int main()
    {
       int x = 1503 ;
       div_test(x,5);
    // div_test(x,0);     // division par zéro
     
    // div_bug_test(x,5); // overflow (mais pas de division par zéro)
     
     
        return 0;
    }
     
    /////////////////////////
     
    int divise(int x, int n)
    {
       int z;
       __asm__ __volatile__(
          " mov $0, %%rdx ;"
          " div %[n] ;"
          :    "=a" (z)
          : [x] "a" (x),
            [n] "r" (n)
          : "cc", "%rdx"
       );
       return(z);
    }
     
    int divise_bug(int x, int n)
    {
       int z;
       __asm__ __volatile__(
    //    " mov $0, %%rdx ;"      // rdx contient une valeur inconnue
          " div %[n] ;"           // qui, en général, va causer un overflow
          :    "=a" (z)
          : [x] "a" (x),
            [n] "r" (n)
          : "cc", "%rdx"
       );
       return(z);
    }
     
    void div_test(int x, int y)
    {
       int z;
       cout<<"\n x = "<<x<<"; y = "<<y;
       try
       {
            z = divise(x,y);
            cout << "; x/y = " << z << endl;
       }
       catch (std::exception const & exception)
       {
            cout << "Erreur : " << exception.what() << endl;
       }
    }
     
    void div_bug_test(int x, int y)
    {
       int z;
       cout<<"\n x = "<<x<<"; y = "<<y;
       try
       {
            z = divise_bug(x,y);
            cout << "; x/y = " << z << endl;
       }
       catch (std::exception const & exception)
       {
            cout << "Erreur : " << exception.what() << endl;
       }
    }

  2. #2
    Responsable Systèmes


    Homme Profil pro
    Gestion de parcs informatique
    Inscrit en
    Août 2011
    Messages
    17 453
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Gestion de parcs informatique
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Août 2011
    Messages : 17 453
    Points : 43 106
    Points
    43 106
    Par défaut
    Une division par 0 lève une exception (au sens CPU, pas au sens C++), qui est une interruption spéciale. Le code déclenché par le noyau va killer le processus ayant déclenché le prob.

    Un overflow, c'est un flag au niveau du CPU au même titre que la retenue. Il est à ta charge de les gérer. En assembleur par exemple, tu as des sauts conditionnels qui vont dépendre des flags carry ou overflow, ou zero.

    La division par 0, c'est bloquant car impossible, pas un overflow ou une retenue.

    Donc, il faut tester si il y a overflow quel que soit le langage. Avec certains langages tu peux avoir le travail maché par rapport au C.

    Dans ton code le C++ ne sait pas ce que fait le code assembleur, il récupère juste le résultat dans ton int. Les exceptions C++ n'ont rien à voir.

    Tu as un exemple ici pour la gestion de l'overflow en C++ : https://stackoverflow.com/questions/...exception-in-c

    pour les détails au niveau C++, mes collègues en faisant t'expliqueront mieux que moi.
    Ma page sur developpez.com : http://chrtophe.developpez.com/ (avec mes articles)
    Mon article sur le P2V, mon article sur le cloud
    Consultez nos FAQ : Windows, Linux, Virtualisation

  3. #3
    Membre du Club
    Profil pro
    Inscrit en
    Avril 2012
    Messages
    72
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2012
    Messages : 72
    Points : 48
    Points
    48
    Par défaut
    Bonjour chrtophe

    Merci de m’avoir suivi ici.

    Lors d’une erreur fatale, comme une division par zéro mais pas seulement, il est inutile de continuer le programme. Donc interruption et message d’erreur. Sinon un ou plusieurs flags pour signaler la situation et on passe à l’instruction suivante : si le programme est bien conçu, le programmeur a prévu un test sur ces flag pour gérer les éventuels problèmes. J’espère ne pas avoir trahi ta pensée.

    Mais ce n’est pas du tout ce qui se passe avec la fonction div_bug_test (sinon ce fil n’existerait pas !). Pas de message d’erreur pour un overflow : ok. Mais le CPU ne passe pas à l’instruction suivante non plus : il reste perdu dans sa division et boucle sans fin. De mon point de vue, c’est un dysfonctionnement.

    Bien sûr, le programmeur peut, comme dans la fonction div_test, prévoir la situation en amont et ici il doit le faire. Mais je rappelle que je me place dans la situation de mise au point d’un programme ; et si tous les problèmes devait être prévus en amont alors tous les programmes marcheraient du premier coup !

  4. #4
    Responsable Systèmes


    Homme Profil pro
    Gestion de parcs informatique
    Inscrit en
    Août 2011
    Messages
    17 453
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Gestion de parcs informatique
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Août 2011
    Messages : 17 453
    Points : 43 106
    Points
    43 106
    Par défaut
    Ton code assembleur ne teste pas la flag overflow du CPU, enfin j'ai l'impression je maitrise pas la syntaxe inline. Reste à voir ce qu'il se passe avec un débogueur, notamment si la partie assembleur retourne correctement à la partie C pour commencer.
    Ma page sur developpez.com : http://chrtophe.developpez.com/ (avec mes articles)
    Mon article sur le P2V, mon article sur le cloud
    Consultez nos FAQ : Windows, Linux, Virtualisation

  5. #5
    Membre du Club
    Profil pro
    Inscrit en
    Avril 2012
    Messages
    72
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2012
    Messages : 72
    Points : 48
    Points
    48
    Par défaut
    Effectivement, le programme ne teste aucun flag. Mais aucun test ne serait possible puisque le CPU reste enfermé dans la division : il ne peut donc exécuter aucune instruction ultérieure.

    J’ai utilisé le block try/catch « au cas où », mais on peut utiliser directement la fonction divise_bug
    Code C++ : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    int main()
    {
       int x = 1503, z ;
       div_test(x,5);
     
    // div_test(x,0);     // division par zéro
     
    // div_bug_test(x,5); // overflow (mais pas de division par zéro)
       z = divise_bug(x,5);
     
     
        return 0;
    }

    Voici ce que donne ma fonction divise_bug désassemblée (j’ai éliminé les lignes de liaison entre C++ et asm)
    Code asm : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
     
            mov    0x10(%rbp),%eax          //  eax = x
    	mov    0x18(%rbp),%ecx		//  ecx = n
    	div    %ecx		        //  eax = x / n
    	mov    %eax,-0x4(%rbp) 		//  z = eax

    Comme tu peux le voir, rien de bien extraordinaire : sauf que la dernière ligne (mov ..) n’est jamais exécutée...

  6. #6
    Responsable Systèmes


    Homme Profil pro
    Gestion de parcs informatique
    Inscrit en
    Août 2011
    Messages
    17 453
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Gestion de parcs informatique
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Août 2011
    Messages : 17 453
    Points : 43 106
    Points
    43 106
    Par défaut
    déjà c'est pas bon, il faudrait mettre edx à 0 avant la division. Ca se trouve ton prob. est là.
    Ma page sur developpez.com : http://chrtophe.developpez.com/ (avec mes articles)
    Mon article sur le P2V, mon article sur le cloud
    Consultez nos FAQ : Windows, Linux, Virtualisation

  7. #7
    Membre du Club
    Profil pro
    Inscrit en
    Avril 2012
    Messages
    72
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2012
    Messages : 72
    Points : 48
    Points
    48
    Par défaut
    Bien sur qu'il faut mettre rdx à zéro avant la division : c'est ce que je fais dans la fonction divise et c'est bien pour cela que ma fonction s'appelle divise_bug ! Regarde : dans divise_bug, la mise à zéro de rdx est commenté et il est indiqué dans le commentaire que ça va boguer.

    Ma question n'est pas "Comment faire une belle fonction divise qui marche".
    Ma question est :"Est-il normal que le CPU plante en plein milieu d'une instruction sans être capable d'en sortir"

    Ma fonction divise_bug est seulement un exemple contrôlé dans lequel on connaît l'origine du problème (vu qu'on l'a créé !).

  8. #8
    Responsable Systèmes


    Homme Profil pro
    Gestion de parcs informatique
    Inscrit en
    Août 2011
    Messages
    17 453
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Paris (Île de France)

    Informations professionnelles :
    Activité : Gestion de parcs informatique
    Secteur : High Tech - Matériel informatique

    Informations forums :
    Inscription : Août 2011
    Messages : 17 453
    Points : 43 106
    Points
    43 106
    Par défaut
    Tu as essayé avec un débogueur ?
    Ma page sur developpez.com : http://chrtophe.developpez.com/ (avec mes articles)
    Mon article sur le P2V, mon article sur le cloud
    Consultez nos FAQ : Windows, Linux, Virtualisation

  9. #9
    Membre du Club
    Profil pro
    Inscrit en
    Avril 2012
    Messages
    72
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Avril 2012
    Messages : 72
    Points : 48
    Points
    48
    Par défaut
    D’habitude, on pose une question du genre « J’ai un programme qui marche pas et je vois pas pourquoi »

    Ici ce n’est pas le cas : à partir d’un programme qui marche (la fonction divise), j’ai volontairement introduit une erreur (j’ai commenté la ligne de code qui met rdx à zéro avant la division). Donc ce nouveau programme (la fonction divise_bug) ne marche pas et je sais très bien pourquoi.

    Pourquoi faire un truc pareil ? Parce que c’est un exemple où non seulement le programme plante (c’est normal, on a tout fait pour !) mais où
    le CPU plante en plein milieu d'une instruction sans être capable d'en sortir
    Et ça, pour moi, ce n’est pas normal. Mais ce n’est que mon point de vue et j’aimerais savoir si c’est aussi le votre ou si tout le monde s’en fout.

Discussions similaires

  1. Passer la validation Javascript dans certains cas
    Par tkino dans le forum Struts 1
    Réponses: 2
    Dernier message: 31/07/2006, 15h37
  2. Que mettre dans un champ DATETIME pour certains cas?
    Par Jim_Nastiq dans le forum Requêtes
    Réponses: 5
    Dernier message: 04/07/2006, 10h22
  3. [JOINTURES] interêt dans certain cas
    Par nicotine002 dans le forum Langage SQL
    Réponses: 4
    Dernier message: 13/06/2006, 17h22
  4. fonctions désactivées sauf certains cas
    Par ricoba19 dans le forum Langage
    Réponses: 3
    Dernier message: 08/06/2006, 16h51
  5. [POO] Modèle objet: this inutilisable dans certains cas?
    Par vlord dans le forum Général JavaScript
    Réponses: 13
    Dernier message: 13/08/2005, 10h41

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