H. Högl, 2017-10-22
Inhalt
Das GDB Text User Interface. Man sieht die drei Fenster (von oben): Register, Quelltext und Kommandoeingabe.
# main.s # H. Hoegl, 2017-10-23 # --- BSS section --- .section .bss .lcomm buffer, 256 # --- DATA section --- .section .data array: .long 5, 9, 1, 3, 8 ,7, 10, 6, 12, 11, 0 var1: .long 42 str1: .asciz "ABC" # --- TEXT section --- .section .text .globl _start _start: movl var1, %eax call f1 _L1: incl %eax jmp _L1 f1: movl $1, %eax ret # vim: expandtab ts=4 sw=4
Für GDB 7.11, 2016 (und neuer):
1 # .gdbinit 2 3 # add "set auto-load safe-path /" to your ~/.gdbinit 4 5 set confirm off 6 # do not pause during output 7 set pagination off 8 9 # siehe .gdb_history 10 set history save on 11 12 set logging file gdb_log.txt 13 set logging on 14 15 tui enable 16 layout regs 17 winheight src +3 18 winheight regs 7 19 set prompt (gdb) 20 file main 21 br _start 22 run
Damit startet man den GDB mit gdb oder gdbtui.
Man muss auch im Home-Verzeichnis eine Datei ~/.gdbinit anlegen, in der zumindest die eine Zeile steht:
set auto-load safe-path /
Auf das Symbol _start wird ein Breakpoint gesetzt. Nach dem run stoppt das Programm bei _start.
Die GDB Eingabeaufforderung (gdb) erinnert sich immer an das letzte eingegebene Kommando. Falls das z.B. s war, wird bei jedem Betätigen der Eingabetaste wieder s aufgerufen.
Man sollte das TUI einschalten, um übersichtlich Debuggen zu können. Wir bevorzugen das Layout auf dem man von oben nach unten sieht: Register, Quellcode und Kommandoeingabe.
Es gibt zwei Möglichkeiten:
Kommandozeilenoption: --tui
Auf dem Eingabeprompt anschliessen mehrmals C-x 2 eingeben, damit schaltet man nacheinander die verschiedenen Layouts durch bis das Gewünschte erreicht ist.
.gdbinit
Zum Aktivieren des bei Systemnaher Programmierung verwendeten TUI Layouts kann man in die Datei .gdbinit schreiben:
tui enable layout regs(bei moderneren GDB Versionen) oder
tui reg general(bei älteren Versionen).
Mit C-x o schaltet den Fokus auf ein anderes TUI Fenster um.
Programm starten und unterbrechen
# Programm von Anfang an starten (gdb) run [args] # Programm nach breakpoint wieder weiterlaufen lassen (gdb) continue # Laufendes Programm über Tastatur abbrechen (gdb) Strg-C
Breakpoints
(gdb) br _start
(gdb) br 26
(gdb) br main.s:26
(gdb) br *<addr>
(gdb) br f1
(gdb) br _L1 if $eax == 1000
# Führe Kommandos bei breakpoints aus
(gdb) br <label>
commands
...
end
# gibt aktuelle breakpoints aus
(gdb) info br
Num Type Disp Enb Address What
1 breakpoint keep y 0x0804807a main.s:28
breakpoint already hit 1 time
2 breakpoint keep y 0x08048084 main.s:33
# enable/disable breakpoint
(gdb) disable 2
(gdb) enable 2
# breakpoint loeschen
(gdb) del 2
Einzelschritte
Beachte den Unterschied zwischen step und next:
# s(step) - Eine Zeile in einer Hochsprache (gdb) s # s(tep) i(nstruction) - Ein Maschinenbefehl (gdb) si # n(ext) (gdb) n # n(ext) i(nstruction) (gdb) ni
Um das Programm wieder mit voller Geschwindigkeit auszuführen, gibt man c(ontinue) ein.
Register
(gdb) p $eax (gdb) set $eax = 42
An das $-Zeichen vor dem Registernamen im Debugger denken. Im Assembler muss es hingegen ein %-Zeichen sein.
Variablen
Die Variable var1 ist in der data section definiert:
.section .data
var1: .int 42
Hier sind ein paar nützliche gdb Kommandos:
# var1 ausgeben
(gdb) p var1
$1 = 42
# var1 in hex ausgeben
(gdb) p/x var1
$1 = 0x2a
# var1 auf Wert setzen
(gdb) set variable var1 = 43
set var1 = 43
# short
(gdb) set var1 = 43
set var1 = 43
# Adresse von var1 im Speicher ausgeben
(gdb) p &var1
$1 = (<data variable, no debug info> *) 0x80490a7
(gdb) set {int}0x80490a7 = 0xAFFE
(gdb)
(gdb) whatis var1
type = <data variable, no debug info>
(gdb) ptype var1
type = <data variable, no debug info>
Strings
Definition:
.section .data
str1: .asciz "ABC"
Kommandos:
(gdb) p str1 $6 = 4407873 (was ist das?) (gdb) p/x str1 #$8 = 0x434241 (gdb) p/x &str1 $5 = 0x80490a7 (gdb) x/s &str1 0x80490a7: "ABC" (gdb) x/4c &str1 0x80490a7: 65 'A' 66 'B' 67 'C' 0 '\000' (gdb) set (0x80490a7 + 1) = 'm (gdb) x/s &str1 0x80490a7: "AmC"
Arrays
Ausgeben der Elemente
(gdb) p array@5
$2 = {3, 67, 34, 12, 17}
(gdb) p array@6
$3 = {3, 67, 34, 12, 17, 0}
(gdb) p array@7
$4 = {3, 67, 34, 12, 17, 0, 1835008}
Ausgeben der Elemente (alternative Syntax)
(gdb) p {int[6]} &array
$5 = {3, 67, 34, 12, 17, 0}
Elemente verändern
(gdb) set *(unsigned int *)0x804909e = 18
(gdb) p array@5
$7 = {18, 67, 34, 12, 17}
Buffer
Definiert eine Variable buffer mit 256 Bytes im bss Abschnitt (alle 256 Bytes werden auf 0 initialisiert):
.section .bss .lcomm buffer, 256
GDB Kommandos:
(gdb) p buffer
$1 = 0
(gdb) p buffer@256
$2 = {0 <repeats 256 times>}
(gdb) p (char[256]) buffer
$1 = '\000' <repeats 255 times>
(gdb) p {char[256]} &buffer
$1 = '\000' <repeats 255 times>
(gdb) p &buffer
$3 = (<data variable, no debug info> *) 0x80490d8 <buffer>
(gdb) set {int} (&buffer+3) = 2017
(gdb) x/8xw &buffer
0x80490d8 <buffer>: 0x00000000 0x00000000 0x00000000 0x000007e1
0x80490e8 <buffer+16>: 0x00000000 0x00000000 0x00000000 0x00000000
# Vorsicht: 5 Worte offset
(gdb) set {char}(&buffer+5) = 'A'
(gdb) x/16xw &buffer
0x80490e0 <buffer>: 0x00000000 0x00000000 0x00000000 0x00000000
0x80490f0 <buffer+16>: 0x00000000 0x00000041 0x00000000 0x00000000
# Adresse statt &buffer (5 byte offset)
set {char[3]}(0x80490e0+5) = {'a', 'b', 'c'}
# Setze drittes Zeichen in Buffer auf 'y'
set {char}(&(char[1])buffer+3) = 'y'
# Setze ab dem fuenften Buchstaben den String "DEF\0"
set {char[4]}(&(char[1])buffer+5) = "DEF"
set {char[4]} &buffer = "ABC"
set {char[4]} (&buffer+4) = "DEF"
# Buffer "dump" (cb = char/byte; xw = hex/word)
x/32cb &buffer
x/8xw &buffer
# Vorsicht: malloc
(gdb) set &buffer+11 = {'a', 'b', 'c'}
evaluation of this expression requires the program to have a function "malloc".
Stack (Kommandozeilenargumente)
Auf dem gdb Prompt startet man ein Programm mit Argumenten arg1, arg2 und arg3 wie folgt (breakpoint auf _start setzen):
(gdb) run arg1 arg2 arg3
(gdb) p $esp
$6 = (void *) 0xbffff4d0
# Anzahl Argumente
(gdb) p/x *(int*)$esp
$8 = 0x4
# Alternative: (gdb) x/1xw $esp
(gdb) x/s *(char**)($esp+4)
0xbffff637: "/home/hhoegl/Sysprog/gdb-uebersicht/main"
# Alternative:
(gdb) x/s {char*}($esp+4)
0xbffff637: "/home/hhoegl/Sysprog/gdb-uebersicht/main"
(gdb) x/s *(char**)($esp+8)
0xbffff660: "arg1"
(gdb) x/s *(char**)($esp+12)
0xbffff665: "arg2"
(gdb) x/s *(char**)($esp+16)
0xbffff66a: "arg3"
Anleitung zu type-casts:
Kommandozeilenoptionen des GDB ausgeben:
gdb --help
GDB beenden mit q(uit) oder Strg-D.
GDB ohne "Banner" starten: gdb -q.