Wie analysiere ich die Core-Dump-Datei eines Programms mit GDB, wenn es Befehlszeilenparameter enthält?

156

Mein Programm funktioniert folgendermaßen:

exe -p param1 -i param2 -o param3

Es stürzte ab und erzeugte eine Core-Dump-Datei. core.pid .

Ich möchte die Core-Dump-Datei von analysieren

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

GDB erkennt jedoch die Parameter der EXE-Datei als Eingabe von GDB.

Wie analysiere ich in dieser Situation eine Core-Dump-Datei?

Treper
quelle
1
Sind Sie sicher, dass exees sich nicht um ein Shell-Skript handelt (um einige Variablen usw. festzulegen), wie z. B. firefoxunter Linux?
Basile Starynkevitch

Antworten:

182

Sie können den Kern mit GDB auf viele Arten verwenden, aber die Übergabe von Parametern, die an die ausführbare Datei an GDB übergeben werden sollen, ist nicht die Möglichkeit, die Kerndatei zu verwenden. Dies könnte auch der Grund sein, warum Sie diesen Fehler erhalten haben. Sie können die Kerndatei folgendermaßen verwenden:

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

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

Bei Verwendung der Kerndatei müssen Sie keine Argumente übergeben. Das Absturzszenario wird in GDB angezeigt (überprüft mit GDB Version 7.1 unter Ubuntu).

Beispielsweise:

$ ./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)

Wenn Sie Parameter an die ausführbare Datei übergeben möchten, die in GDB debuggt werden soll, verwenden Sie --args .

Beispielsweise:

$ 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)

Manpages sind hilfreich, um andere GDB-Optionen anzuzeigen.

other.anon.coward
quelle
38

Einfache Verwendung von GDB zum Debuggen von Coredump-Dateien:

gdb <executable_path> <coredump_file_path>

Eine Coredump-Datei für einen "Prozess" wird als "core.pid" -Datei erstellt.

Geben Sie Folgendes ein, nachdem Sie die GDB-Eingabeaufforderung aufgerufen haben (bei Ausführung des obigen Befehls):

...
(gdb) where

Dadurch erhalten Sie die Informationen des Stapels, in denen Sie die Ursache des Absturzes / Fehlers analysieren können. Ein anderer Befehl für die gleichen Zwecke lautet:

...
(gdb) bt full

Dies ist das gleiche wie oben. Konventionell werden die gesamten Stapelinformationen aufgelistet (was letztendlich zum Absturzort führt).

Parasrish
quelle
22

Überspringen Sie einfach die Parameter. GDB braucht sie nicht:

gdb ./exe core.pid
Nialscorva
quelle
Das funktioniert aber nicht. Die GDB-Ausgabewarnung: Die Kerndatei stimmt möglicherweise nicht mit der angegebenen ausführbaren Datei überein. Fehler beim Lesen eines gültigen Objektdateibilds aus dem Speicher.
Treper
6
"Die Kerndatei stimmt möglicherweise nicht mit der angegebenen ausführbaren Datei überein". Haben Sie exe geändert, nachdem es den Kern erzeugt hat? Haben Sie es vielleicht mit verschiedenen Befehlszeilenoptionen neu erstellt? Es ist sehr wichtig, GDB genau die gleiche Binärdatei zu geben , die den Kern erzeugt hat. Wenn Sie dies nicht tun, werden Sie Müll rausbringen.
Angestellt Russisch
2
Stellen Sie außerdem sicher, dass die an gdb übergebene Binärdatei nicht entfernt wird. Sie können 'Datei <Binärname>' ausführen, um anzuzeigen, dass sie entfernt wurde oder nicht.
Diwakar Sharma
12

objdump + gdb minimales lauffähiges Beispiel

TL; DR:

Nun zum vollständigen Testaufbau:

Haupt 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);
}

Kompilieren und ausführen, um den Kern zu generieren:

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

Ausgabe:

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

GDB verweist uns auf die genaue Zeile, in der der Segmentierungsfehler aufgetreten ist. Dies möchten die meisten Benutzer beim Debuggen:

gdb -q -nh main.out core

dann:

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

Das zeigt uns direkt auf die Buggy-Linie 7.

CLI-Argumente werden in der Kerndatei gespeichert und müssen nicht erneut übergeben werden

Um die spezifischen CLI-Argumentfragen zu beantworten, sehen wir, dass, wenn wir die cli-Argumente ändern, z. B.:

rm -f core
./main.out 1 2

dann spiegelt sich dies in der vorherigen Bactrace wider, ohne dass Änderungen an unseren Befehlen vorgenommen wurden:

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

Also beachte wie jetzt argc=3. Daher muss dies bedeuten, dass die Kerndatei diese Informationen speichert. Ich vermute, es speichert es nur als Argumente vonmain , genauso wie es die Argumente anderer Funktionen speichert.

Dies ist sinnvoll, wenn Sie bedenken, dass der Core-Dump den gesamten Speicher- und Registerstatus des Programms speichern muss und daher alle Informationen enthält, die zum Bestimmen des Werts von Funktionsargumenten auf dem aktuellen Stapel erforderlich sind.

Weniger offensichtlich ist, wie die Umgebungsvariablen überprüft werden: So erhalten Sie Umgebungsvariablen aus einem Core-Dump Umgebungsvariablen sind auch im Speicher vorhanden, sodass der objdump diese Informationen enthält, aber ich bin nicht sicher, wie ich sie alle auf einmal bequem auflisten kann , eins nach dem anderen wie folgt hat an meinen Tests gearbeitet:

p __environ[0]

Binutils-Analyse

Durch die Verwendung von binutils-Tools wie readelfund objdumpkönnen wir die in dercore Datei wie den Speicherstatus erstellen.

Das meiste / alles muss auch über GDB sichtbar sein, aber diese binutils-Tools bieten einen umfangreicheren Ansatz, der für bestimmte Anwendungsfälle praktisch ist, während GDB für eine interaktivere Erkundung bequemer ist.

Zuerst:

file core

sagt uns, dass die coreDatei tatsächlich eine ELF- Datei ist:

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

Deshalb können wir es mit den üblichen binutils-Werkzeugen direkter untersuchen.

Ein kurzer Blick auf den ELF-Standard zeigt, dass es tatsächlich einen ELF-Typ gibt, der diesem gewidmet ist:

Elf32_Ehd.e_type == ET_CORE

Weitere Formatinformationen finden Sie unter:

man 5 core

Dann:

readelf -Wa core

gibt einige Hinweise zur Dateistruktur. Der Speicher scheint in regulären Programm-Headern enthalten zu sein:

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

In einem Notizbereich sind weitere Metadaten vorhanden, insbesondere prstatusder 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 kann leicht den gesamten Speicher entleeren mit:

objdump -s core

was beinhaltet:

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..........

Das stimmt genau mit dem Standardwert in unserem Lauf überein.

Dies wurde unter Ubuntu 16.04 amd64, GCC 6.4.0 und binutils 2.26.1 getestet.

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
quelle
10

Aus dem GDB-Debugger-Tutorial von RMS :

prompt > myprogram
Segmentation fault (core dumped)
prompt > gdb myprogram
...
(gdb) core core.pid
...

Stellen Sie sicher, dass Ihre Datei wirklich ein coreBild ist - überprüfen Sie sie mit file.

Brian Cain
quelle
9

Mit einem etwas anderen Ansatz können Sie GDB vollständig überspringen. Wenn Sie nur eine Rückverfolgung wünschen, fängt das Linux-spezifische Dienstprogramm 'catchsegv' SIGSEGV ab und zeigt eine Rückverfolgung an.

Rob Latham
quelle
3

Es spielt keine Rolle, ob die ausführbare Datei Argumente enthält oder nicht. Um GDB auf einer beliebigen Binärdatei mit einer generierten Kerndatei auszuführen, finden Sie unten die Syntax.

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

Lassen Sie mich das folgende Beispiel zum besseren Verständnis nehmen.

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)

Anhand der obigen Ausgabe können Sie etwas über den Kern erraten, ob es sich um einen NULL-Zugriff, SIGABORT usw. handelt.

Diese Zahlen Nr. 0 bis Nr. 10 sind die Stapelrahmen von GDB. Diese Stapelrahmen gehören nicht zu Ihrer Binärdatei. Wenn Sie in den obigen 0 - 10 Frames den Verdacht haben, dass etwas nicht stimmt, wählen Sie diesen Frame aus

(gdb) frame 8

Nun, um mehr Details darüber zu sehen:

(gdb) list +

Um das Problem weiter zu untersuchen, können Sie hier zu diesem Zeitpunkt die vermuteten Variablenwerte drucken.

(gdb) print thread_name
Venkatakrishna Kalepalli
quelle
0

Geben Sie einfach den Befehl ein:

$ gdb <Binary> <codeDump>

Oder

$ gdb <binary>

$ gdb) core <coreDump>

Es ist nicht erforderlich, ein Befehlszeilenargument anzugeben. Der Code-Dump, der aufgrund einer früheren Übung generiert wurde.

KB Arora
quelle
-1

Sie können die Core-Dump-Datei mit dem Befehl "gdb" analysieren.

 gdb - The GNU Debugger

 syntax:

 # gdb executable-file core-file

 example: # gdb out.txt core.xxx 
Ranjithkumar T.
quelle
1
out.txt ist eine ausführbare Datei? Das scheint eine irreführende Dateierweiterung zu sein.
Alan