C/UNIX A1 5 qua - DÈbogage

1. DÈbogage post-mortem avec gdb(1) ou dbx(1)

Soit le programme litigieux suivant:
$ cat segfault.c
int     fun()
{
  int   *p;

  p = 0;
  *p = 42;
}

int     main()
{
  fun();
}
Si nous l'ÈxÈcutons, le systËme nous envoie un SIGSEGV:
$ ./segfault
Segmentation fault
Notes:
  • C'est le shell qui rÈcupËre le numÈro de signal qui nous a tuÈ et l'affiche.
  • Le systËme n'a pas gÈnÈrÈ de fichier core car nous avons fixÈ la limite coredumpsize ý zÈro.

    Pour dÈboguer correctement, il faut dire au systËme que nous avons besoin des fichiers core:

    En tcsh(1):

    $ unlimit coredumpsize
    
    En bash(1):
    $ ulimit -c unlimited
    
    Les fichiers core contiennent l'image mÈmoire du processus au moment ou l'erreur s'est produite.

    Il nous faut aussi compiler les fichiers sources avec l'option -g.

    $ cc -g -c segfault.c
    $ cc -o segfault segfault.o
    $ ./segfault
    Segmentation fault (core dumped)
    $ gdb ./segfault segfault.core 
    GNU gdb 4.17
    Copyright 1998 Free Software Foundation, Inc.
    GDB is free software, covered by the GNU General Public License, and
    you are
    welcome to change it and/or distribute copies of it under certain
    conditions.
    Type "show copying" to see the conditions.
    There is absolutely no warranty for GDB.  Type "show warranty" for
    details.
    This GDB was configured as Ô386--netbsd"...
    Core was generated by `segfault'.
    Program terminated with signal 11, Segmentation fault.
    Reading symbols from /usr/libexec/ld.elf_so...done.
    Reading symbols from /usr/lib/libc.so.12...done.
    #0  0x80487d4 in fun () at segfault.c:6
    6         *p = 42;
    (gdb) where
    #0  0x80487d4 in fun () at segfault.c:6
    #1  0x80487e4 in main () at segfault.c:11
    #2  0x8048585 in ___start ()
    (gdb) print p
    $1 = (int *) 0x0
    (gdb) 
    
    Nous lanÁons gdb(1) avec en paramËtre le fichier ÈxÈcutable suivi de l'image mÈmoire. Le programme nous propose une invite o˜ nous pouvons taper des commandes:

    CommandeDescription
    whereMontre la pile des fonctions ý l'instant ou le core a ÈtÈ gÈnÈrÈ.
    print varAffiche la variable passÈe en paramËtre.
    quitQuitte le programme.
    Tab 5qua.1.1. Quelques commandes de gdb(1) et dbx(1)

    2. DÈbogage interactif avec gdb(1) ou dbx(1)

    Il nous est possible de dÈboguer le programme depuis le dÈmarrage:
    $ gdb ./segfault
    GNU gdb 4.17
    Copyright 1998 Free Software Foundation, Inc.
    GDB is free software, covered by the GNU General Public License, and
    you are
    welcome to change it and/or distribute copies of it under certain
    conditions.
    Type "show copying" to see the conditions.
    There is absolutely no warranty for GDB.  Type "show warranty" for
    details.
    This GDB was configured as Ô386--netbsd"...
    (gdb) break fun
    Breakpoint 1 at 0x80487ca: file segfault.c, line 5.
    (gdb) run
    Starting program: /space/wd0e/root/work/cunix_a1/cours8-12/./segfault 
    
    Breakpoint 1, fun () at segfault.c:5
    5         p = 0;
    (gdb) next
    6         *p = 42;
    (gdb) next
    
    Program received signal SIGSEGV, Segmentation fault.
    0x80487d4 in fun () at segfault.c:6
    6         *p = 42;
    (gdb) quit
    The program is running.  Exit anyway? (y or n) y
    
    Commande gdb(1)Commande dbx(1)Description
    break funstop funPositionne un point d'arrÍt ý la fonction fun.
    runidemDÈmarre le programme.
    nextidemŠxÈcute la fonction ou l'instruction suivante.
    stepidemRentre dans la fonction suivante suivante.
    Tab 5qua.2.1. Autres commandes de gdb(1) et dbx(1)

    Il existe des centaines d'autres commandes aux dÈbuggers qui permettent de faire des arrÍts conditionnels, etc.

    3. Explication des problËmes d'alignement

    Soit le programme suivant:
    $ cat align.c 
    int     main()
    {
      long  *p;
    
      p = xmalloc(2 * sizeof (*p));
      p = (char *)p + 1;
      *p = 42;
    }
    
    Sur alpha:
    $ ./align
    Unaligned access pid=5251  va=0x140000901 pc=0x120001230
    ra=0x12000115c inst=0xb0200001
    
    De tels problËmes d'alignement peuvent causer des bus error sur certaines architectures (par exemple mips). Le PC apparement ne tient pas compte de tels problËmes (voir Sebc pour l'explication).

    Dans tous les cas, on ne doit pas utiliser une adresse non divisible par la taille des registres du processeur (32 bits soit 4 pour R3000 et x86, 64 bits soit 8 pour alpha) pour faire une opÈration LOAD ou STORE d'un entier supÈrieur ý 1 octet (mov ax, [adress] ou mov eax, [address] et vis-versa sur x86, LHx, SHx, LWx, SWx dans la famille R[36]000).


    Fig 5qua.3.1. Alignement

    Note: La configuration de droite est impossible.

    4. Explication des problËmes de segmentation fault

    Soit le programme suivant qui utilise une mauvaise adresse:
    $ cat badaddr.c
    int     main()
    {
      long  *p, *n;
    
      p = xmalloc(sizeof (*p));
      n = p + 1;
      *n = 42;
    }
    $ ./badaddr
    $
    
    Nous remarquons qu'il ne gÈnËre pas obligatoirement un segmentation fault.

    Quand nous linkons avec la libefence (_resources/libefence.tgz), le segmentation fault est mis en Èvidence:

    $ ./badaddr
    
      Electric Fence 2.0.5 Copyright (C) 1987-1995 Bruce Perens.
    Segmentation fault (core dumped)
    
    Explication: La mÈmoire est allouÈe par page de 4096, 8192 ou plus selon les architectures (voir a1_cunix9.html). Lorsque nous utilisons une adresse non thÈoriquement allouÈe, il se peut qu'elle soit contenue dans la page allouÈe. Dans l'exemple prÈcÈdent, l'adresse n a de fortes chances d'Ítre dans la page allouÈe pour p.

    La librairie libefence alloue les buffers ý la fin des pages mÈmoire quand elle le peut ce qui a pour effet de gÈnÈrer des segmentation fault lorsque nous Ècrivons aprËs les buffers.


    Fig 5qua.4.1. Mauvaises adresses et pages mÈmoire

    Note: La libefence ne rÈsout pas tous les problËmes mais peut aider ý mieux identifier l'origine des erreurs mÈmoire.


    Copyright © EPITA mailto:vianney@epita.fr.