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

Programmation d'OS Assembleur Discussion :

Triple fault sur implémentation de la GDT


Sujet :

Programmation d'OS Assembleur

  1. #1
    Membre actif Avatar de BioKore
    Homme Profil pro
    Dresseur d'Alpaga
    Inscrit en
    Septembre 2016
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Dresseur d'Alpaga

    Informations forums :
    Inscription : Septembre 2016
    Messages : 300
    Points : 219
    Points
    219
    Par défaut Triple fault sur implémentation de la GDT
    Bonjour à tous,

    Je suis actuellement, pour raisons ludique et pédagogique, de développer un petit kernel sans autre ambition que d'en savoir plus sur la manière dont fonctionnent ces derniers (et développer un peu mes compétences en programmation). Pour "simplifier" la chose, j'ai choisi de booter avec GRUB dans un premier temps ; je me pencherais sur ce sujet plus tard.
    Je tourne sous Linux, compile avec GCC et AS, link avec LD, le tout dans un makefile approprié. Mes essais se font sous virtual-box.

    Mon code est écrit en C et ASM (GNU/AT&T), et je m'inspire des différentes sources d'infos que je trouve ça et là. Je précise aussi que mes cours d'assembleur et architecture sont assez lointains et se faisaient plutôt sous la forme INTEL (MASM/NASM).

    Mon problème est le suivant : impossible de charger les segments code, data ou encore stack (CS,DS,SS) lors-que je souhaite charger la GDT.
    J'en ai donc déduit que cela venait très certainement de la forme de ma gdt, mais je ne trouve pas mon erreur.

    Code de formation des descripteurs de segment et de la GDT Registres (*.h et *.c) :
    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
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
     
    #ifndef __GDT_H
    #define __GDT_H
     
    #include "types.h"
     
    #define GDTSIZE 0xFF
    #define GDTBASE 0x0
     
    //typedef struct segDesc segDesc;
    //typedef struct segPtr segPtr;
     
    //segment descriptor
    struct segDesc {
        uint16_t limit_lb;
        uint16_t base_lb;
        uint8_t base_hb;
        uint8_t type;
        uint8_t granul;         //sinon uint8_t limit:4 et uint8_t flags:4
        uint8_t base_vhb;
     
    } __attribute__((packed));
     
    //Global Register Descriptor Table
    struct segPtr {
        uint16_t limit;     //uint32_t limit();
        uint32_t base;      //uint32_t base();
    }__attribute__((packed));
     
    //déclaration des segment descriptors...
    struct segDesc tSeg[3];
     
    //déclaration des seg registers.
    struct segPtr gdtr;
     
    // initialisation des segments (code, data, stack)
    void initSegmentDesc(uint8_t i, uint32_t base, uint32_t limit, uint8_t type, uint8_t flags_hb);
     
    //init de la gdt globale avec registres
    void init_gdt(void);
     
    extern void gdt_load(void);    //fonction présente dans le loader.s
     
     
     
     
    #endif
    ...
    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
    32
     
    void initSegmentDesc(uint8_t i, uint32_t base, uint32_t limit,
                         uint8_t type, uint8_t flags)
    {
        tSeg[i].limit_lb = (limit & 0xFFFF);
        tSeg[i].base_lb = (base & 0xFFFF);
        tSeg[i].base_hb = (base >> 16) & 0xFF;
        tSeg[i].type = type;
        tSeg[i].granul = (limit >> 16) & 0x0F;          //tSeg[i].limit = (limit >> 16) & 0x0F;
        tSeg[i].granul |= (flags & 0xF0);               //tSeg[i].flags = (flags & 0xF);
        tSeg[i].base_vhb = (base >> 24) & 0xFF;
     
        return;
    }
     
    void init_gdt(void)
    {  
     
        initSegmentDesc(0, 0, 0, 0, 0);                     //null
        initSegmentDesc(1, 0, 0xFFFFFFFF, 0x9A, 0xCF);      //code
        initSegmentDesc(2, 0, 0xFFFFFFFF, 0x92, 0xCF);      //data
        //initSegmentDesc(3, 0x0, 0x0, 0x96, 0x0D);         //stack
     
        gdtr.limit = (sizeof(struct segDesc)*3) - 1;
        gdtr.base = (uint32_t)&tSeg;
     
        //gdtr.limit = GDTSIZE*8;
        //gdtr.base = GDTBASE;
     
        //memcpy((uint8_t*)gdtr.base, (uint8_t*)tSeg, gdtr.limit);
     
        gdt_load();        // <-- bug !!!
    Le sections commentées sont les différents essais que j'ai fait en m'inspirant de ce que j'ai trouvé sur le net.

    loader.s, avec la fonction gdt_load :
    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
    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
     
    .set MAGIC, 0x1badb002	#adresse où doit commencer le kernel (par grub)
    .set FLAGS, (1<<0 | 1<<1)   #init à 0x0 marche aussi...
    .set CHKSUM, -(MAGIC + FLAGS)
     
    .section .multiboot
        .long MAGIC
        .long FLAGS
        .long CHKSUM
     
    .section .text
        .extern kernelStart
        .extern kerr
        .extern gdtr
        .extern idtP
        .global loader
        .global gdt_load
        .global idt_load
     
        loader:
            mov $kernel_stack , %esp
            push %eax
            push %ebx
            movl %cr0, %eax
            or $1, %eax
            movl %eax, %cr0             #passage ne mode protégé... useless.
            call kernelStart
     
        gdt_load:
            cli                         # <-- ne change rien
            lgdtl (gdtr)
            movw $0x10, %ax
            movw %ax, %ds                #data segment
            movw %ax, %es                #extra segment
            movw %ax, %fs                #gp     -   general purpose
            movw %ax, %gs                #gp     -   general purpose
            ljmp $0x08, $next            #colle le cs à 0x08
        next:
            movw $0x18, %ax
            movw %ax, %ss               #colle le ss à 0x18
            movl $0x20000, %esp
            ret
     
        idt_load:
            lidt (idtP)
            ret
     
        _stop:
            cli
            hlt
            jmp _stop
     
     
    .section .bss
        .space 4096     #Mbi
     
        kernel_stack:
    Vous remarquerez que j'ai essayé de forcer le passage en mode protégé (bien qu'inutile car GRUB passe la main sous ce mode). J'ai aussi essayé de limiter les potentiels interupts lors du chargement de la GDT (cli). Celà fait presque une semaine que je refais mon code dans tous les sens donc je ne peut pas vous faire la liste de ce que j'ai testé.

    Voilà mon "kernel" se lance, m'affiche mes deux lignes de présentations et.. TRIPLE_FAULT....

    Quelqu'un voit d'où vient mon erreur ? Je commence à vraiment sécher...

    Merci beaucoup d'avance !

  2. #2
    Responsable Systèmes


    Homme Profil pro
    Gestion de parcs informatique
    Inscrit en
    Août 2011
    Messages
    17 444
    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 444
    Points : 43 088
    Points
    43 088
    Par défaut
    Pour commencer : sais tu ce qu'est un triple fault ? ça te donnera une piste pour chercher.

    Il te faut débuguer pour trouver le prob. Je te conseilles l'utilisation de Bochs.
    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 actif Avatar de BioKore
    Homme Profil pro
    Dresseur d'Alpaga
    Inscrit en
    Septembre 2016
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Dresseur d'Alpaga

    Informations forums :
    Inscription : Septembre 2016
    Messages : 300
    Points : 219
    Points
    219
    Par défaut Merci mais...
    Bonjour, et merci pour cette réponse, mais, cela ne m'aide pas beaucoup plus.

    Le triple Fault intervient en général lors d'un buffer overflow sur l'IDT (ou écriture sur son segment). Ok, mais à mon niveau l'IDT n'est pas encore initialisée donc je ne comprends pas.

    En ce qui concerne l'utilisation de Bochs, j'ai cru voir que les "logs" étaient un peu plus clairs que ce que je retrouve sur VirtualBox effectivement ; je vais essayer d'utiliser cet outil pour voir.

    En ce qui concerne le debugger j'ai bien pensé à GDB mais je ne suis pas sure de voir comment faire avec cet outil pour un "OS". Une idée d'un soft sur lequel je pourrais m'appuyer ?


    Merci !

  4. #4
    Responsable Systèmes


    Homme Profil pro
    Gestion de parcs informatique
    Inscrit en
    Août 2011
    Messages
    17 444
    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 444
    Points : 43 088
    Points
    43 088
    Par défaut
    Le triple Fault intervient en général lors d'un buffer overflow sur l'IDT (ou écriture sur son segment). Ok, mais à mon niveau l'IDT n'est pas encore initialisée donc je ne comprends pas.
    ça vient aussi sur une exception non gérée

    Bochs intègre 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

  5. #5
    Membre actif Avatar de BioKore
    Homme Profil pro
    Dresseur d'Alpaga
    Inscrit en
    Septembre 2016
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Dresseur d'Alpaga

    Informations forums :
    Inscription : Septembre 2016
    Messages : 300
    Points : 219
    Points
    219
    Par défaut Ok
    Merci pour ces infos, je vais voir ce que je peux faire... Aussi-tôt que j'aurais réussi à configurer Bochs.

    Je vous tiens au courant.

  6. #6
    Membre actif Avatar de BioKore
    Homme Profil pro
    Dresseur d'Alpaga
    Inscrit en
    Septembre 2016
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Dresseur d'Alpaga

    Informations forums :
    Inscription : Septembre 2016
    Messages : 300
    Points : 219
    Points
    219
    Par défaut
    Bon, je viens de faire un premier essai avec Bochs ; voici ce que j'obtiens avec un grep "cpu" (sinon je fais du 2Go de log par seconde) :

    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
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
     
    00062680563d[CPU0 ] real mode activated
    00062680570d[CPU0 ] inhibit interrupts mask = 3
    00062680571d[CPU0 ] inhibit interrupts mask = 1
    00062680585d[CPU0 ] interrupt(): vector = 10, TYPE = 4, EXT = 0
    00062680649d[CPU0 ] protected mode activated
    00062680656d[CPU0 ] inhibit interrupts mask = 3
    00062681286d[CPU0 ] inhibit interrupts mask = 3
    00062681290d[CPU0 ] real mode activated
    00062681297d[CPU0 ] inhibit interrupts mask = 3
    00062681298d[CPU0 ] inhibit interrupts mask = 1
    00062681312d[CPU0 ] interrupt(): vector = 10, TYPE = 4, EXT = 0
    00062681374d[CPU0 ] protected mode activated
    00062681381d[CPU0 ] inhibit interrupts mask = 3
    00062827919d[CPU0 ] inhibit interrupts mask = 3
    00062831999e[CPU0 ] check_cs(0x0008): conforming code seg descriptor dpl > cpl, dpl=3, cpl=0
    00062831999d[CPU0 ] exception(0x0d): error_code=0008
    00062831999d[CPU0 ] interrupt(): vector = 0d, TYPE = 3, EXT = 1
    00062831999e[CPU0 ] interrupt(): vector must be within IDT table limits, IDT.limit = 0x0
    00062831999d[CPU0 ] exception(0x0d): error_code=006a
    00062831999d[CPU0 ] exception(0x08): error_code=0000
    00062831999d[CPU0 ] interrupt(): vector = 08, TYPE = 3, EXT = 1
    00062831999e[CPU0 ] interrupt(): vector must be within IDT table limits, IDT.limit = 0x0
    00062831999d[CPU0 ] exception(0x0d): error_code=0042
    00062831999i[CPU0 ] CPU is in protected mode (active)
    00062831999i[CPU0 ] CS.mode = 32 bit
    00062831999i[CPU0 ] SS.mode = 32 bit
    00062831999i[CPU0 ] EFER   = 0x00000000
    00062831999i[CPU0 ] | EAX=00100010  EBX=00010000  ECX=00000000  EDX=00000002
    00062831999i[CPU0 ] | ESP=00102fd4  EBP=00102fe0  ESI=00000000  EDI=00000000
    00062831999i[CPU0 ] | IOPL=0 ID vip vif ac vm RF nt of df if tf sf zf af PF cf
    00062831999i[CPU0 ] | SEG sltr(index|ti|rpl)     base    limit G D
    00062831999i[CPU0 ] |  CS:0010( 0002| 0|  0) 00000000 ffffffff 1 1
    00062831999i[CPU0 ] |  DS:0010( 0002| 0|  0) ffffffff ffffffff 1 1
    00062831999i[CPU0 ] |  SS:0018( 0003| 0|  0) 00000000 ffffffff 1 1
    00062831999i[CPU0 ] |  ES:0010( 0002| 0|  0) ffffffff ffffffff 1 1
    00062831999i[CPU0 ] |  FS:0010( 0002| 0|  0) ffffffff ffffffff 1 1
    00062831999i[CPU0 ] |  GS:0010( 0002| 0|  0) ffffffff ffffffff 1 1
    00062831999i[CPU0 ] | EIP=00100035 (00100035)
    00062831999i[CPU0 ] | CR0=0x60000011 CR2=0x00000000
    00062831999i[CPU0 ] | CR3=0x00000000 CR4=0x00000000
    00062831999d[CTRL ] searching for component 'cpu' in list 'bochs'
    00062831999d[CTRL ] searching for component 'reset_on_triple_fault' in list 'cpu'
    00062831999e[CPU0 ] exception(): 3rd (13) exception with no resolution, shutdown status is 00h, resetting
    00062831999i[CPU0 ] cpu hardware reset
    Je suis loin de tout comprendre malheureusement, mais ce que je vois bien, c'est que j'ai effectivement des erreurs niveau "exceptions", mais que le log me parle aussi de IDT (qui, je le répète, n'est pas lancé à ce stade). Je vois aussi que mon CS est encore à 0x10 au lieux de 0x08.

    Bref, je n'arrive pas du tout à analyser ce log en fin de compte... Quelqu'un y voit plus clair que moi ?

    Merci encore.

  7. #7
    Responsable Systèmes


    Homme Profil pro
    Gestion de parcs informatique
    Inscrit en
    Août 2011
    Messages
    17 444
    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 444
    Points : 43 088
    Points
    43 088
    Par défaut
    Les exceptions telles que segfault, violation de privilèges sont implémentés via l'idt, et vu qu'il n'y a pas d'idt : triple fault.

    C'est probablement ta GDT invalide,. Lances Bochsdbg et désassemble jusqu'au début de ton code (donc tu dois parcourir tout Grub, c'est long et laborieux ...). pour voir ou ça plante. Je ne sais plus si Bochs t'indique si la gdt est valide, mais de mémoire, tu as les différents flags affichés.

    Évites de gérer la GDT en C, fais-le plutôt en assembleur, ce sera plus facile pour toi.
    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

  8. #8
    Membre actif Avatar de BioKore
    Homme Profil pro
    Dresseur d'Alpaga
    Inscrit en
    Septembre 2016
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Dresseur d'Alpaga

    Informations forums :
    Inscription : Septembre 2016
    Messages : 300
    Points : 219
    Points
    219
    Par défaut
    Ok, merci pour ce retour.

    Je peux effectivement voir l'état de la GDT avec bochs (commande "info gdt").

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    Global Descriptor Table (base=0x0000000030200000, limit=35):
    bx_dbg_read_linear: physical memory read error (phy=0x0000000030200000, lin=0x0000000030200000)
    error: GDTR+8*0 points to invalid linear address 0x0000000030200000
    bx_dbg_read_linear: physical memory read error (phy=0x0000000030200008, lin=0x0000000030200008)
    error: GDTR+8*1 points to invalid linear address 0x0000000030200000
    bx_dbg_read_linear: physical memory read error (phy=0x0000000030200010, lin=0x0000000030200010)
    error: GDTR+8*2 points to invalid linear address 0x0000000030200000
    bx_dbg_read_linear: physical memory read error (phy=0x0000000030200018, lin=0x0000000030200018)
    error: GDTR+8*3 points to invalid linear address 0x0000000030200000
    Je veux bien le faire en assembleur, mais si on peut le faire en C, pourquoi en passer par là ?
    Je lui demande pourtant bien de l'initier à 0x0 + FFFFF

    Après, c'est vrai que je ne me suis pas vraiment posé la question en fait, mais niveau performances / portabilité et évolutivité du code, est-ce qu'implémenter la GDT en ASM est équivalent à le faire en C ?
    Parce que si je dois le refaire en C, plus tard, je serais finalement confronté au même problème.

  9. #9
    Responsable Systèmes


    Homme Profil pro
    Gestion de parcs informatique
    Inscrit en
    Août 2011
    Messages
    17 444
    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 444
    Points : 43 088
    Points
    43 088
    Par défaut
    C'est plus une question de compétence/facilité.

    Personnellement, je trouve plus facile de positionner tous les bits des flags GDT en assembleur, mais si tu maitrises la manip en C, tu peux le faire en C aussi. Mais de toute façon tu devras faire de l'assembleur inline pour appeler la commande lgdt et accéder aux registres de commande.

    En tout cas, ton écran montre que ta GDT est invalide.
    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

  10. #10
    Membre actif Avatar de BioKore
    Homme Profil pro
    Dresseur d'Alpaga
    Inscrit en
    Septembre 2016
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Dresseur d'Alpaga

    Informations forums :
    Inscription : Septembre 2016
    Messages : 300
    Points : 219
    Points
    219
    Par défaut
    Ok, je conçoit tout à fait, effectivement, qu'il soit plus aisé de positionner les bits en assembleur. Je vais tout de même essayer.
    Mais même si je le fais en assembleur in fine, j'aimerais tout de même trouver mon erreur dans le code en C....

    Quoi qu'il en soit je ne comprends toujours pas pourquoi ma base est à base=0x0000000030200000 alors que je spécifie bien une base à 0x0.

    N'aurais-je pas oublié de spécifier, à un moment que je suis en 32bits et pas en 64bits (mon proc est en 64 mais je compile tout avec les options 32bits) ? Si c'est le cas, je ne sais pas comment faire du coup...

  11. #11
    Membre actif Avatar de BioKore
    Homme Profil pro
    Dresseur d'Alpaga
    Inscrit en
    Septembre 2016
    Messages
    300
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Canada

    Informations professionnelles :
    Activité : Dresseur d'Alpaga

    Informations forums :
    Inscription : Septembre 2016
    Messages : 300
    Points : 219
    Points
    219
    Par défaut
    Enfin ! J'ai réussi !!!

    J'ai retapé tout mon code, une fois de plus, et cette fois-ci, le DS et CS passent bien ! L'init de SS par contre foire encore, mais je n'ai pas encore regarder d'où pouvais venir l'erreur.

    Impossible de savoir exactement d'où venait mon erreur initiale ; je pense qu'il s'agissait, en fait, de plusieurs erreurs dans mon code C, corrigées naturellement lors de la réécriture...


    Bref, en tout cas, merci pour le coup de main.

    Je poserais juste cette question avant de passer le post en "résolu" et en prévision du débogage de l'init du segment de stack : Comment m'assurer de donner une adresse valide pour mon esp (SS) ? Comment puis-je déterminer une plage "safe" vis-à-vis des autres éléments de mon code actuel et à venir (GDT, IDT etc...) ?

    Merci encore !

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

Discussions similaires

  1. Segmentation fault sur mon serveur
    Par zoullou dans le forum Administration système
    Réponses: 2
    Dernier message: 06/04/2007, 10h17
  2. Segmentation fault sur script PHP
    Par zoullou dans le forum Langage
    Réponses: 1
    Dernier message: 03/04/2007, 09h32
  3. Segmentation fault sur new[] et delete[]
    Par Don ViP dans le forum C++
    Réponses: 4
    Dernier message: 30/04/2006, 00h29
  4. Segmentation Fault sur un fclose
    Par Beush dans le forum C
    Réponses: 9
    Dernier message: 30/11/2005, 19h30
  5. Segmentation fault sur un gethostbyname ?
    Par Mitox dans le forum Réseau
    Réponses: 9
    Dernier message: 25/11/2005, 16h17

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