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