Comment analyser le fichier de vidage de mémoire d'un programme avec GDB lorsqu'il a des paramètres de ligne de commande?


156

Mon programme fonctionne comme ceci:

exe -p param1 -i param2 -o param3

Il est écrasé et a généré un fichier de vidage de base, core.pid.

Je souhaite analyser le fichier de vidage de mémoire en

gdb ./exe -p param1 -i param2 -o param3 core.pid

Mais GDB reconnaît les paramètres du fichier EXE comme entrée de GDB.

Comment analyser un fichier de vidage de mémoire dans cette situation?


1
Etes-vous sûr que votre exen'est pas un script shell (pour définir certaines variables, etc.) comme par exemple firefoxsur Linux?
Basile Starynkevitch

Réponses:


182

Vous pouvez utiliser le core avec GDB de plusieurs manières, mais transmettre les paramètres qui doivent être passés à l'exécutable à GDB n'est pas la manière d'utiliser le fichier core. Cela pourrait également être la raison pour laquelle vous avez obtenu cette erreur. Vous pouvez utiliser le fichier core des manières suivantes:

gdb <executable> <core-file>ou gdb <executable> -c <core-file>ou

gdb <executable>
...
(gdb) core <core-file>

Lorsque vous utilisez le fichier core, vous n'avez pas à passer d'arguments. Le scénario de plantage est affiché dans GDB (vérifié avec GDB version 7.1 sur Ubuntu).

Par exemple:

$ ./crash -p param1 -o param2
Segmentation fault (core dumped)
$ gdb ./crash core
GNU gdb (GDB) 7.1-ubuntu
...
Core was generated by `./crash -p param1 -o param2'. <<<<< See this line shows crash scenario
Program terminated with signal 11, Segmentation fault.
#0  __strlen_ia32 () at ../sysdeps/i386/i686/multiarch/../../i586/strlen.S:99
99    ../sysdeps/i386/i686/multiarch/../../i586/strlen.S: No such file or directory.
    in ../sysdeps/i386/i686/multiarch/../../i586/strlen.S
(gdb)

Si vous souhaitez transmettre des paramètres à l'exécutable à déboguer dans GDB, utilisez --args.

Par exemple:

$ gdb --args ./crash -p param1 -o param2
GNU gdb (GDB) 7.1-ubuntu
...
(gdb) r
Starting program: /home/@@@@/crash -p param1 -o param2

Program received signal SIGSEGV, Segmentation fault.
__strlen_ia32 () at ../sysdeps/i386/i686/multiarch/../../i586/strlen.S:99
99    ../sysdeps/i386/i686/multiarch/../../i586/strlen.S: No such file or directory.
    in ../sysdeps/i386/i686/multiarch/../../i586/strlen.S
(gdb)

Les pages de manuel seront utiles pour voir d'autres options GDB.


38

Utilisation simple de GDB, pour déboguer les fichiers coredump:

gdb <executable_path> <coredump_file_path>

Un fichier coredump pour un "processus" est créé en tant que fichier "core.pid".

Une fois que vous êtes entré dans l'invite GDB (lors de l'exécution de la commande ci-dessus), tapez:

...
(gdb) where

Cela vous donnera les informations de la pile, où vous pourrez analyser la cause du crash / de la panne. L'autre commande, aux mêmes fins est:

...
(gdb) bt full

C'est la même chose que ci-dessus. Par convention, il répertorie toutes les informations de la pile (ce qui conduit finalement à l'emplacement du crash).


22

Ignorez simplement les paramètres. GDB n'en a pas besoin:

gdb ./exe core.pid

Mais ça ne marche pas. L'avertissement de sortie gdb: le fichier principal peut ne pas correspondre au fichier exécutable spécifié. Impossible de lire une image de fichier objet valide à partir de la mémoire.
Treper

6
"le fichier core peut ne pas correspondre à l'exécutable spécifié". Avez-vous modifié exe après avoir produit le noyau? L'avez-vous peut-être reconstruit avec différentes options de ligne de commande? Il est très important de donner à GDB exactement le même binaire qui a produit le noyau. Si vous ne le faites pas, vous sortirez les ordures.
Employé russe

2
Assurez-vous également que le binaire passé à gdb n'est pas supprimé. Vous pouvez exécuter 'fichier <nom binaire>' qui montre qu'il est supprimé ou non.
Diwakar Sharma

12

objdump+ gdbexemple exécutable minimal

TL; DR:

Maintenant, pour la configuration complète du test éducatif:

principal c

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int myfunc(int i) {
    *(int*)(NULL) = i; /* line 7 */
    return i - 1;
}

int main(int argc, char **argv) {
    /* Setup some memory. */
    char data_ptr[] = "string in data segment";
    char *mmap_ptr;
    char *text_ptr = "string in text segment";
    (void)argv;
    mmap_ptr = (char *)malloc(sizeof(data_ptr) + 1);
    strcpy(mmap_ptr, data_ptr);
    mmap_ptr[10] = 'm';
    mmap_ptr[11] = 'm';
    mmap_ptr[12] = 'a';
    mmap_ptr[13] = 'p';
    printf("text addr: %p\n", text_ptr);
    printf("data addr: %p\n", data_ptr);
    printf("mmap addr: %p\n", mmap_ptr);

    /* Call a function to prepare a stack trace. */
    return myfunc(argc);
}

Compilez et exécutez pour générer le noyau:

gcc -ggdb3 -std=c99 -Wall -Wextra -pedantic -o main.out main.c
ulimit -c unlimited
rm -f core
./main.out

Production:

text addr: 0x4007d4
data addr: 0x7ffec6739220
mmap addr: 0x1612010
Segmentation fault (core dumped)

GDB nous indique la ligne exacte où l'erreur de segmentation s'est produite, ce que la plupart des utilisateurs veulent lors du débogage:

gdb -q -nh main.out core

puis:

Reading symbols from main.out...done.
[New LWP 27479]
Core was generated by `./main.out'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x0000000000400635 in myfunc (i=1) at main.c:7
7           *(int*)(NULL) = i;
(gdb) bt
#0  0x0000000000400635 in myfunc (i=1) at main.c:7
#1  0x000000000040072b in main (argc=1, argv=0x7ffec6739328) at main.c:28

qui nous dirige directement vers la ligne buggy 7.

Les arguments CLI sont stockés dans le fichier core et n'ont pas besoin d'être passés à nouveau

Pour répondre aux questions d'argument CLI spécifiques, nous voyons que si nous changeons les arguments cli par exemple avec:

rm -f core
./main.out 1 2

alors cela se reflète dans le bactrace précédent sans aucun changement dans nos commandes:

Reading symbols from main.out...done.
[New LWP 21838]
Core was generated by `./main.out 1 2'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0  0x0000564583cf2759 in myfunc (i=3) at main.c:7
7           *(int*)(NULL) = i; /* line 7 */
(gdb) bt
#0  0x0000564583cf2759 in myfunc (i=3) at main.c:7
#1  0x0000564583cf2858 in main (argc=3, argv=0x7ffcca4effa8) at main.c:2

Alors notez comment maintenant argc=3. Par conséquent, cela doit signifier que le fichier principal stocke ces informations. Je suppose que ça le stocke juste comme les arguments demain , tout comme il stocke les arguments de toute autre fonction.

Cela a du sens si vous considérez que le vidage de mémoire doit stocker la totalité de la mémoire et de l'état des registres du programme, et qu'il a donc toutes les informations nécessaires pour déterminer la valeur des arguments de fonction sur la pile actuelle.

Il est moins évident d'inspecter les variables d'environnement: Comment obtenir une variable d'environnement à partir d'un vidage de mémoire Les variables d'environnement sont également présentes en mémoire, de sorte que l'objdump contient ces informations, mais je ne sais pas comment les répertorier toutes en une seule fois. , un par un comme suit a cependant fonctionné sur mes tests:

p __environ[0]

Analyse Binutils

En utilisant des outils binutils comme readelfet objdump, nous pouvons vider en bloc les informations contenues dans lecore fichier, telles que l'état de la mémoire.

La plupart / tout cela doit également être visible via GDB, mais ces outils binutils offrent une approche plus globale qui est pratique pour certains cas d'utilisation, tandis que GDB est plus pratique pour une exploration plus interactive.

Première:

file core

nous dit que le corefichier est en fait un fichier ELF :

core: ELF 64-bit LSB core file x86-64, version 1 (SYSV), SVR4-style, from './main.out'

c'est pourquoi nous pouvons l'inspecter plus directement avec les outils binutils habituels.

Un rapide coup d'œil à la norme ELF montre qu'il existe en fait un type ELF qui lui est dédié:

Elf32_Ehd.e_type == ET_CORE

Vous trouverez de plus amples informations sur le format à l'adresse:

man 5 core

Ensuite:

readelf -Wa core

donne quelques indications sur la structure des fichiers. La mémoire semble être contenue dans les en-têtes de programmes ordinaires:

Program Headers:
  Type           Offset   VirtAddr           PhysAddr           FileSiz  MemSiz   Flg Align
  NOTE           0x000468 0x0000000000000000 0x0000000000000000 0x000b9c 0x000000     0
  LOAD           0x002000 0x0000000000400000 0x0000000000000000 0x001000 0x001000 R E 0x1000
  LOAD           0x003000 0x0000000000600000 0x0000000000000000 0x001000 0x001000 R   0x1000
  LOAD           0x004000 0x0000000000601000 0x0000000000000000 0x001000 0x001000 RW  0x1000

et il y a plus de métadonnées présentes dans une zone de notes, contient notamment prstatusle PC :

Displaying notes found at file offset 0x00000468 with length 0x00000b9c:
  Owner                 Data size       Description
  CORE                 0x00000150       NT_PRSTATUS (prstatus structure)
  CORE                 0x00000088       NT_PRPSINFO (prpsinfo structure)
  CORE                 0x00000080       NT_SIGINFO (siginfo_t data)
  CORE                 0x00000130       NT_AUXV (auxiliary vector)
  CORE                 0x00000246       NT_FILE (mapped files)
    Page size: 4096
                 Start                 End         Page Offset
    0x0000000000400000  0x0000000000401000  0x0000000000000000
        /home/ciro/test/main.out
    0x0000000000600000  0x0000000000601000  0x0000000000000000
        /home/ciro/test/main.out
    0x0000000000601000  0x0000000000602000  0x0000000000000001
        /home/ciro/test/main.out
    0x00007f8d939ee000  0x00007f8d93bae000  0x0000000000000000
        /lib/x86_64-linux-gnu/libc-2.23.so
    0x00007f8d93bae000  0x00007f8d93dae000  0x00000000000001c0
        /lib/x86_64-linux-gnu/libc-2.23.so
    0x00007f8d93dae000  0x00007f8d93db2000  0x00000000000001c0
        /lib/x86_64-linux-gnu/libc-2.23.so
    0x00007f8d93db2000  0x00007f8d93db4000  0x00000000000001c4
        /lib/x86_64-linux-gnu/libc-2.23.so
    0x00007f8d93db8000  0x00007f8d93dde000  0x0000000000000000
        /lib/x86_64-linux-gnu/ld-2.23.so
    0x00007f8d93fdd000  0x00007f8d93fde000  0x0000000000000025
        /lib/x86_64-linux-gnu/ld-2.23.so
    0x00007f8d93fde000  0x00007f8d93fdf000  0x0000000000000026
        /lib/x86_64-linux-gnu/ld-2.23.so
  CORE                 0x00000200       NT_FPREGSET (floating point registers)
  LINUX                0x00000340       NT_X86_XSTATE (x86 XSAVE extended state)

objdump peut facilement vider toute la mémoire avec:

objdump -s core

qui contient:

Contents of section load1:

 4007d0 01000200 73747269 6e672069 6e207465  ....string in te
 4007e0 78742073 65676d65 6e740074 65787420  xt segment.text 

Contents of section load15:

 7ffec6739220 73747269 6e672069 6e206461 74612073  string in data s
 7ffec6739230 65676d65 6e740000 00a8677b 9c6778cd  egment....g{.gx.

Contents of section load4:

 1612010 73747269 6e672069 6e206d6d 61702073  string in mmap s
 1612020 65676d65 6e740000 11040000 00000000  egment..........

qui correspond exactement à la valeur stdout dans notre exécution.

Cela a été testé sur Ubuntu 16.04 amd64, GCC 6.4.0 et binutils 2.26.1.



9

Une approche légèrement différente vous permettra de sauter complètement GDB. Si tout ce que vous voulez est une trace arrière, l'utilitaire spécifique à Linux 'catchsegv' capturera SIGSEGV et affichera une trace arrière.


3

Peu importe si l'exécutable a des arguments ou non. Pour exécuter GDB sur n'importe quel binaire avec un fichier core généré, la syntaxe est ci-dessous.

Syntax:
gdb <binary name> <generated core file>
Eg:
gdb l3_entity 6290-corefile

Permettez-moi de prendre l'exemple ci-dessous pour plus de compréhension.

bash-4.1$ **gdb l3_entity 6290-corefile**

**Core was generated** by `/dir1/dir2/dir3/l3_entity **Program terminated with signal SIGABRT, Aborted.**
#0
#1
#2
#3
#4
#5
#6
#7
#8
#9
#10
(gdb)

À partir de la sortie ci-dessus, vous pouvez deviner quelque chose à propos du core, s'il s'agit d'un accès NULL, SIGABORT, etc.

Ces nombres # 0 à # 10 sont les cadres de pile de GDB. Ces cadres de pile ne sont pas de votre binaire. Dans les 0 à 10 images ci-dessus, si vous suspectez quelque chose de mal, sélectionnez cette image

(gdb) frame 8

Maintenant, pour voir plus de détails à ce sujet:

(gdb) list +

Pour étudier le problème plus en détail, vous pouvez imprimer les valeurs de variable suspectées ici à ce stade.

(gdb) print thread_name

0

Tapez simplement la commande:

$ gdb <Binary> <codeDump>

Ou

$ gdb <binary>

$ gdb) core <coreDump>

Il n'est pas nécessaire de fournir un argument de ligne de commande. Le vidage de code généré à la suite d'un exercice précédent.


-1

Vous pouvez analyser le fichier de vidage de mémoire en utilisant la commande "gdb".

 gdb - The GNU Debugger

 syntax:

 # gdb executable-file core-file

 example: # gdb out.txt core.xxx 

1
out.txt est un fichier exécutable? Cela semble être une extension de fichier trompeuse.
Alan
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.