Mon programme fonctionne comme ceci:
exe -p param1 -i param2 -o param3
Il s'est écrasé et a généré un fichier de vidage principal core.pid
Je veux analyser le fichier core dump par
gdb ./exe -p param1 -i param2 -o param3 core.pid
mais gdb reconnaît les paramètres de exe
comme entrée de gdb.
Comment analyser le fichier core dump dans cette situation?
Vous pouvez utiliser le noyau avec gdb de nombreuses manières, mais le fait de transmettre des paramètres à exécutable à exécutable à gdb n’est pas le moyen d’utiliser le fichier core. Cela pourrait aussi être la raison pour laquelle vous avez eu cette erreur. Vous pouvez utiliser le fichier de base de différentes manières:gdb <executable> <core-file>
ou gdb <executable> -c <core-file>
ou
gdb <executable>
...
(gdb) core <core-file>
Lorsque vous utilisez un fichier core, vous n'avez pas à passer d'arguments. Le scénario de plantage est présenté 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 pour qu'il soit débogué 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 les autres options de gdb.
Utilisation simple de GDB pour déboguer des fichiers coredump:
gdb <executable_path> <coredump_file_path>
Le fichier Coredump pour un "processus" est créé en tant que fichier "core.pid" . Une fois que vous êtes entré dans l'invite gdb, (à l'exécution de la commande ci-dessus), tapez;
...
(gdb) where
Vous obtiendrez ainsi les informations de la pile, où vous pourrez analyser la cause du crash/de la faute . Autre commande, pour les mêmes raisons;
...
(gdb) bt full
C'est comme ci-dessus. Par convention, il répertorie l’ensemble des informations de la pile (qui conduit finalement à l’emplacement du crash).
Sautez juste les params, gdb n'en a pas besoin:
gdb ./exe core.pid
De Tutoriel du débogueur gdb de RMS :
Prompt > myprogram
Segmentation fault (core dumped)
Prompt > gdb myprogram
...
(gdb) core core.pid
...
Assurez-vous que votre fichier est vraiment une image core
- vérifiez-le en utilisant file
.
Une approche légèrement différente vous permettra de sauter complètement la GDB. Si tout ce que vous voulez, c'est une trace, l'utilitaire spécifique à Linux 'catchsegv' va capturer SIGSEGV et afficher une trace.
Peu importe que l'exécutable ait des arguments ou non, Pour exécuter GDB sur tout binaire avec un fichier core généré, la syntaxe est la suivante.
Syntax:
gdb <binary name> <generated core file>
Eg:
gdb l3_entity 6290-corefile
laissez-moi 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 noyau, qu’il s’agisse d’un accès NULL ou de 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 pensez que quelque chose ne va pas, sélectionnez-la.
(gdb) frame 8
Maintenant pour voir plus de détails à ce sujet:
(gdb) list +
Pour étudier plus en détail le problème, vous pouvez imprimer les valeurs de la variable suspectée ici à ce stade.
(gdb) print thread_name
Vous pouvez analyser le fichier de vidage principal à l’aide de la commande "gdb".
gdb - The GNU Debugger
syntax:
# gdb executable-file core-file
ex: # gdb out.txt core.xxx
Merci.
objdump
+ gdb
exemple minimal exécutable
TL; DR:
objdump -s core
pour vider la mémoire
GDB pour trouver la ligne défaillante, mentionnée précédemment dans: Comment analyser le fichier de vidage principal d'un programme avec gdb?
Passons maintenant à la configuration complète du test pédagogique:
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);
}
Compiler et exécuter 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
Sortie:
text addr: 0x4007d4
data addr: 0x7ffec6739220
mmap addr: 0x1612010
Segmentation fault (core dumped)
GDB nous indique la ligne exacte où le segfault s'est produit, ce que veulent la plupart des utilisateurs 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 7 du buggy.
Analyse Binutils
Premier:
file core
nous dit que le fichier core
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 habituels de binutils.
Des informations supplémentaires sur le format sont disponibles à l'adresse suivante:
man 5 core
Ensuite:
readelf -Wa core
donne quelques indications sur la structure du fichier. La mémoire semble être contenue dans les en-têtes de programme habituels:
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 quelques métadonnées plus présentes dans une zone de notes. Notamment, je suppose que le PC doit être là (confirmation de TODO):
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 de notre exécution.
Testé sous Ubuntu 16.04 AMD64, GCC 6.4.0, binutils 2.26.1.
Tapez simplement commande
$ gdb <Binary> <codeDump>
ou
$ gdb <binary>
$ gdb) core <coreDump>
Pas besoin de fournir d'argument en ligne de commande. Le vidage de code généré en raison d'un exercice précédent.