Erster Kontakt mit dem GDB

H. Högl <Hubert.Hoegl@hs-augsburg.de>, 2007, 2015
Letzte Änderung: 2015-10-29

Inhalt

Dieses Tutorial verwendet das "maximum" Beispiel aus dem Buch von Bartlett. Im Git Repositorium findet man es im Verzeichnis sysprog/pgu/prog-3-2. Die Quelltextdatei heisst maximum.s.

1   Einfache Befehle

Zunächst muss man das Programm mit make kompilieren.

Den GNU Debugger gdb startet man danach einfach durch das Kommando gdb. Danach sieht man die GDB Eingabeaufforderung (gdb), an der man Kommandos eingeben kann. Zu allen Befehlen kann man sich Hilfetexte ausgeben lassen mit dem in GDB eingebauten Kommando help, z.B. help list.

(gdb) file maximum

Damit wird das zu debuggende Programm bekannt gegeben. Alternativ kann man auch gleich gdb maximum auf der Kommandozeile aufrufen.

(gdb) list _start

Programmquelltext ausgeben zur Orientierung. Die Nummern am linken Rand bei der Ausgabe sind Zeilennummern.

(gdb) br 15
      oder
(gdb) br <Funktionsname>

Breakpoint auf Zeile 15 oder auf einen Funktionsnamen (Label). Man kann keinen Breakpoint direkt auf das Startsymbol _start setzen. Falls man br _start eingibt, ignoriert GDB diesen Breakpoint und läuft ohne Unterbrechung zum Ende. Der Breakpoint wirkt frühestens an der zweiten Instruktion nach _start. Bei anderen Labels funktioniert hingegen alles wie erwartet.

(gdb) info br

Wo sind Breakpoints gesetzt?

(gdb) del 1

Löschen des Breakpoints mit Nr. 1 (siehe "info br")

(gdb) run

Damit man das Programm mit GDB untersuchen kann, muss es zunächst gestartet werden. Das Programm läuft dann mit voller Geschwindigkeit auf den nächsten Breakpoint.

Sollte man nicht mehr wissen, an welchem Breakpoint man sich im Programm befindet, dann gibt man das

(gdb) where

Kommando ein.

Mit

(gdb) si

schreitet man genau eine Instruktion weiter ("step instruction"). Als Ergebnis wird immer die nächste auszuführende Zeile ausgegeben. Nach einmaliger "si" Eingabe tippt man nur noch Return, Return, ... um mehrere Instruktionen auszuführen. Bei Aufruf eines Unterprogrammes bleibt die Ausführung bei der ersten Instruktion des Unterprogrammes stehen.

Verwandt damit ist die ni Anweisung, sie steht für "next instruction". Sie funktioniert wie si, ausser dass sie Unterprogrammaufrufe sofort komplett ausführt, als ob diese nur ein Maschinenbefehl wären.

(gdb) info reg

Ausgeben aller Register.

(gdb) p/x $eax

Ausgeben eines einzelnen Registers.

(gdb) x/16xw &data_items
(gdb) x/64xb &data_items

Ausgeben eines Speicherbereiches ("memory dump"). Die Angabe 16xw bedeutet, dass 16 hexadezimale Worte (ein Wort gleich vier Byte) ausgegeben werden sollen. Über die möglichen Formatanweisungen erfährt man mehr mit help x auf dem GDB Prompt. Statt einer festen Adresse (&data_items) kann man auch ein Register nehmen, das auf den Speicher zeigt, z.B. so: x/4xw $esp.

Wenn man das Programm wieder mit voller Geschwindigkeit ab dem aktuellen Punkt ausführen will, dann gibt man ein

(gdb) continue

oder kurz c.

Mit dem Kommando

(gdb) quit

verlässt man den Debugger.

2   Noch ein paar Kommandos

Die Adresse einer Variablen (Label) bekommt man mit dem addres-of Operator &:

(gdb) p &data_items
$7 = (<data variable, no debug info> *) 0x804909e

Einen Wert in das Array schreibt man mit folgendem Kommando:

(gdb) set {int} (&data_items + 2) = 99

Der Typ in geschweiften Klammern gibt die Grösse der Elemente an. Danach ist das Element mit Index 2 auf 99 gesetzt:

(gdb) x/6dw &data_items
0x804909e:      3       67      99      12
0x80490ae:      17      0

Die Ausgabe bekommt man auch mit print:

(gdb) p {int[6]} &data_items
$20 = {3, 67, 99, 12, 17, 0}

Noch elementarer kann man den Wert so setzen:

(gdb) set *(unsigned int *)0x804909e = 18

3   Nachtrag zum Text User Interface (TUI)

  1. Högl, Oktober 2015

In den letzten Jahren ist der GDB komfortabler geworden. Wenn man den Debugger mit der Option --tui startet, dann bekommt man über dem Kommandofenster auch noch ein Quelltextfenster angezeigt. Das Quelltextfenster lässt sich wieder in zwei Fenster aufspalten durch mehrfaches Eingeben der Tasten C-x 2. Man sieht nacheinander verschiedene Kombinationen der Fenster

  • Quelltext
  • Disassemblierter Maschinencode
  • Register

Der folgende Screenshot zeigt ganz oben die Register und in der Mitte den Assembler Quelltext. Wenn man in Einzelschritten durch das Programm geht, dann werden Register, die sich ändern, gut sichtbar grau hinterlegt.

gdb-tui.png

GDB mit "text user interface". Das Layout zeigt drei Fenster (von oben): Register, Quelltext, Kommandos. Die schwarze Statuszeile kommt übrigens vom Terminal-Multiplexer "tmux".

Mit C-x o kann man den "Fokus" nacheinander auf jedes der Fenster legen. In einem Fenster mit Fokus kann man mit den Pfeiltasten nach oben/unten scrollen.