Warum benötigt GDB sowohl die ausführbare Datei als auch den Core Dump?

11

Ich debugge mit Core-Dumps und stelle fest, dass gdb Sie benötigt, um sowohl die ausführbare Datei als auch den Core-Dump bereitzustellen. Warum ist das? Wenn der Core-Dump den gesamten vom Prozess verwendeten Speicher enthält, ist die ausführbare Datei dann nicht im Core-Dump enthalten? Vielleicht gibt es keine Garantie dafür, dass die gesamte Exe in den Speicher geladen wird (einzelne ausführbare Dateien sind normalerweise nicht so groß), oder vielleicht enthält der Core-Dump doch nicht den gesamten relevanten Speicher? Ist es für die Symbole (vielleicht werden sie normalerweise nicht in den Speicher geladen)?


quelle
1
Die ausführbare Datei enthält die Symbolinformationen, wie in der GDB-Dokumentation angegeben ...
Thomas Dickey
1
Überraschenderweise erwähnen keine Antworten (außer der, die ich gerade hinzugefügt habe) das DWARF-Format
Basile Starynkevitch

Antworten:

15

Der Core-Dump ist nur der Dump des Speicherbedarfs Ihres Programms. Wenn Sie wissen, wo sich alles befindet, können Sie ihn einfach verwenden.

Sie verwenden die ausführbare Datei, weil sie erklärt, wo sich (in Bezug auf logische Adressen) Dinge im Speicher befinden, dh in der Kerndatei.

Wenn Sie einen Befehl verwenden objdump, werden die Metadaten des ausführbaren Objekts, das Sie untersuchen, ausgegeben. Verwenden Sie als Beispiel ein ausführbares Objekt mit dem Namen a.out.

objdump -h a.outGibt nur die Header-Informationen aus. Es werden Abschnitte mit dem Namen z. .data oder .bss oder .text (es gibt noch viel mehr). Diese informieren den Kernel-Loader darüber, wo im Objekt verschiedene Abschnitte zu finden sind und wo im Prozessadressraum der Abschnitt geladen werden soll und für einige Abschnitte (z. B. .data .text), was geladen werden soll. (Der Abschnitt .bss enthält keine Daten in der Datei, bezieht sich jedoch auf die Speichermenge, die im Prozess für nicht initialisierte Daten reserviert werden muss. Er ist mit Nullen gefüllt.)

Das Layout der ausführbaren Objektdatei entspricht einem Standard-ELF.

objdump -x a.out - wirft alles weg

Wenn das ausführbare Objekt noch seine Symboltabellen enthält (es wurde nicht entfernt - man stripund Sie haben -gdie Debug-Generierung generiert, um die gcc Kompilierung der Wechselstromquelle anzunehmen), können Sie den Kerninhalt anhand von Symbolnamen untersuchen, z. B. wenn Sie eine Variable / einen Puffer hatten Mit dem Namen inputLine in Ihrem Quellcode können Sie diesen Namen verwenden gdb, um den Inhalt anzuzeigen . Das heißt gdb, Sie kennen den Versatz vom Beginn Ihres vom Programm initialisierten Datensegments, in dem inputLine beginnt, und die Länge dieser Variablen.

Lesen Sie weiter Artikel 1 , Artikel 2 und die ELF-Spezifikation (Executable and Linking Format) .


Update nach @mirabilos Kommentar unten.

Aber wenn Sie die Symboltabelle wie in verwenden

$ gdb --batch -s a.out -c core -q -ex "x buf1"

Produziert

 0x601060 <buf1>:    0x72617453

und dann nicht die Symboltabelle verwenden und die Adresse direkt in untersuchen,

$ gdb --batch -c core -q -ex "x 0x601060"

Produziert

0x601060:   0x72617453

Ich habe den Speicher direkt untersucht, ohne die Symboltabelle im 2. Befehl zu verwenden.


Ich sehe auch, dass die Antwort von @ user580082 die Erklärung weiter erweitert und die Abstimmung verbessern wird.

X Tian
quelle
6
Noch nie von "Basic Stack Section" gehört. .bss ist (historisch) "Block durch Symbol gestartet" und praktisch "einheitliche Daten", während .data "initialisierte Daten" sind und Text (nicht .code) zum Speichern von Maschinencode verwendet wird. In einer Binärdatei gibt es keinen Stapelabschnitt, da Stapel zur Laufzeit erstellt werden.
Jlliagre
"Wenn Sie wissen, wo sich alles befand, können Sie das einfach verwenden" ist auch nicht wahr, da nicht alles im Programm unbedingt im Footprint enthalten ist.
Mirabilos
1
@jlliagre Sie haben Recht, ich habe fälschlicherweise .text .code genannt (weil ich beim Verfassen der Antwort über eine Erklärung nachgedacht habe) - aktualisiert. Ich habe fälschlicherweise falsch an bss mit Namen gedacht und meine Antwort aktualisiert, aber * Block Started by Symbol vermieden, da ich nicht glaube, dass es wirklich zur Gleichung beiträgt, und erklärt habe, dass es als nicht initialisierte Daten verwendet wird, was unsere waren gemeinsames Verständnis. Vielen Dank - Ich habe mich über Ihren Kommentar zur Korrektur dieses Beitrags gefreut.
X Tian
4

Die Kerndatei ist eine Momentaufnahme des Stapelabbilds, der Speicherzuordnungen und der Register zum Zeitpunkt der Prozessbeendigung. Der Inhalt kann wie in der Kernmanpage angegeben bearbeitet werden . Standardmäßig werden private Zuordnungen, freigegebene Zuordnungen und ELF-Headerinformationen in die Kerndatei kopiert.

Wenn Sie zu Ihrer Frage kommen , ist der Grund, warum gdb eine ausführbare Datei benötigt, der, dass die Ausführung nicht simuliert wird, indem die binären Anweisungen wie valgrind gelesen und interpretiert werden. Stattdessen wird sie zum übergeordneten Element des Prozesses, um das Verhalten des Prozesses während der Ausführung zu steuern Zeit. Es verwendet die Kerndatei, um die Speicherzuordnungen und den Prozessorstatus während des Absturzes zu bestimmen.

Unter Linux können übergeordnete Prozesse zusätzliche Informationen über ihre untergeordneten Prozesse erhalten, insbesondere die Möglichkeit, sie zu verfolgen, sodass der Debugger auf Informationen auf niedriger Ebene des Prozesses zugreifen kann, z. B. auf seinen Speicher lesen / schreiben, sich registrieren, Signalzuordnungen ändern, seine Ausführung stoppen usw.

Sie werden die Anforderung einer ausführbaren Datei verstehen, obwohl die Kerndatei mehr vorhanden ist, sobald Sie die Funktionsweise eines Debuggers gelesen haben.

Enzym
quelle
1

(zusätzlich zu anderen guten Antworten)

Auf modernen Linux-Systemen (und vielen Unix-ähnlichen Systemen) liegen die Debugging-Informationen (einschließlich Metadaten zu Symboltypen , Quellcode-Speicherort, Variablentyp usw. usw.) im DWARF- Format vor und befinden sich in der ausführbaren ELF- Datei ( oder gemeinsam genutzte ELF-Bibliotheken), wenn es mit einer -gOption kompiliert wird . Ich empfehle das Kompilieren von Programmen zum Debuggen -g3 -O0und möglicherweise -fno-inlinebei Verwendung eines aktuellen GCC . Mit GCC können Sie jedoch sogar sowohl Optimierungs- als auch Debugging-Informationen kompilieren, z. B. mit -O2 -g1, obwohl die Debug-Informationen in diesem Fall möglicherweise etwas "unscharf" sind (dies kann leicht dazu beitragen, einige ungezogene Heisenbugs zu erkennen ).

Es ist durchaus sinnvoll, zu vermeiden, dass diese Informationen in Kerndateien abgelegt werden, da Sie möglicherweise viele verschiedene Kerndateien (stellen Sie sich eine weit verbreitete Software vor, bei der viele Benutzer Fehlerberichte erstellen, die meisten davon mit einem coreSpeicherauszug) für dieselbe ausführbare Datei erstellen . Außerdem werden Core (5) -Dateien vom Kernel ausgegeben, was sich nicht um das Vorhandensein von DWARF-Abschnitten in ausführbaren Dateien von elf (5) kümmern sollte (da diese Abschnitte nicht dem virtuellen Adressraum des fehlerhaften Prozesses zugeordnet sind, der den Core auf ein Signal ausgegeben hat ( 7) ). Es besteht sogar die Möglichkeit, dass die Debug-Informationen in separaten Dateien (außerhalb der ausführbaren Datei) abgelegt werden.

Übrigens kann GDB schmerzhaft zum Debuggen von Core-Dumps für ausführbare Dateien ohne Debug-Informationen verwendet werden. Aber dann debuggen Sie praktisch auf der Ebene des Maschinencodes (nicht auf der symbolischen Ebene, die von Programmiersprachen und ihren Compilern bereitgestellt wird).

Basile Starynkevitch
quelle