Debuggolás: gdb
¶
Fordítás¶
Fordítsuk a terminálból a debuggolandó kódot a gcc pelda.c -o pelda -Wall -m32 -g
utasítással.
A -g a debug ("hibakövetési") kapcsoló. Debug információk kerülnek a lefordított binárisba, így könnyebb lesz a debuggolás. A példák kedvéért a -m32 kapcsolóval fordítsunk 32 bites kódot.
Elindítás¶
GDB elindítása és a cél program betöltése¶
gdb ./pelda
Ha nem -g kapcsolóval volt a program fordítva, akkor a betöltéskor kiírt logok között azt látjuk, hogy no debugging symbols found
(amitől a debuggolás még elindítható, csak kevésbé lesz informatív)
Elindítás Text User Interface-el együtt (TUI)¶
gdb ./pelda -tui
Kettéosztott terminál ablak segítségével a forráskódot is látni fogjuk futás közben! Használata néha bug-os lehet.
A program gépi kódjának megnézése¶
disassemble
: az aktuális függvény gépi kódja. Kis nyíl jelöli éppen melyik utasításnál lévő breakpointban vagyunk.disassemble function
: A kért függvény gépi kódjának megnézésedisas
: rövidített írásmóddisas /m funcname
: a C utasítások és a hozzájuk tartozó gépi utasítások együtt nézhetőek
Program összes függvényének listázása¶
info functions [regexp]
Opcionálisan megadható egy reguláris kifejezés a találatok szűrésére
Breakpoint elhelyezése¶
-
break function
: breakpoint elhelyezése függvénynél -
b function
: rövidített írásmód -
b linenum
: adott sorhoz (b filename:linenum
több fájl esetén) -
Adott sor-pozícióhoz képesti eltolással:
b +/-OFFSET
-
b *addr
: ahol addr az utasításnak pl. a disassemble segítségével megtudott címe.Pl.
break *0x0000000000400722
Breakpointok listázása¶
info breakpoints
Breakpoint ki/be kapcsolása illetve törlése¶
disable N
: adott sorszámú breakpoint kikapcsolása (dis N
)enable N
: adott sorszámú breakpoint visszakapcsolása (en N
)clear function, clear linenum, clear filename:linenum
breakpoint törléshezdelete
: az összes breakpoint letörlése
A betöltött célprogram elindítása¶
run (r)
Használható arra is, hogy az aktuális debug sessiont újraindítsuk.
Következő sor végrehajtása¶
step (s)
next (n)
: a különbség, hogy nem lép bele függvényhívásokbas N
vagyn N
: egyszerre N darab sort lépünkfinish
: az aktuális függvényből való visszatérés helyére ugrik. Ha volt visszatérési érték, akkor kiírja.
Következő gépi utasítás végrehajtása¶
Ezekkel a parancsokkal assembly utasítás szintén tudunk lépdelni a kódban.
stepi (si)
nexti (ni)
: a különbség, hogy nem lép bele függvényhívásokba
Breakpoint utáni újraindítás¶
continue (c)
c N
: ahol N megadja, hányszor ne álljon meg a gdb az aktuális breakpointnál
Backtrace¶
Breakpointnál vagy hiba (pl segmentation fault) esetén megnézhetjük hogyan jutott el a program az adott (hibás) sor lefutásához.
backtrace (bt)
: az aktuális stack frame-től kezdve visszafelé haladva kiírja az összes frame-et, ami a stack-en van. Ha túl sokáig tart CTRL+C-vel le lehet állítani a kiíratást.bt n
: csak az utolsó n darab frame-t írja kibt -n
: csak az első n darab frame-t írja kibt full
: a frame-k lokális változóit is kiíratja. Kombinálható az előző két utasítással.
Információk kiíratása¶
-
info registers
Első oszlop: a regiszter neve, második oszlop: tartalom hexadecimális formában, harmadik oszlop: tartalom olvasható formában. Nézzük meg az EIP regiszter tartalmát.
-
layout regs
info variables
: globális és statikus változók értékeiinfo locals
: lokális változók értékeiprint symbol
: adott szimbólum értéke- röviden:
p symbol
p valtozonev
p fuggveny::valtozonev
p $eax
- röviden:
info address symbol
: adott szimbólum memóriacímeinfo frame
: az aktuális frame adatai (argumentumok, lokális változók, ebp, eip értékei)x 0x080485e9
: az x utasítással egy konkrét memóriacím tartalmát nézhetjük megx/16 address
: 16 érték legyen megjelenítve az adresstől kezdve (növekvő memóriacímek irányába)x/-16 address
: ugyanez, csak a 16 érték csökkenő memóriacímek irányába megy (gdb 8-tól működik)- Kiíratási módosítók megadása is lehetséges:
x/16x address
: 16 hexadecimális érték kiíratásax/16xg address
: 16 db 64 bites hexadecimális érték kiíratásax/16c address
: 16 karakter kiíratásax/s address
: C sztringként értelmezi az adott memóriaterületetx/d address
: előjeles, decimális értékként való megjelenítésx/i address
: utasításként próbálja értelmezni a memóriaterületet
- Verem tetjének megtekintése:
x/20x $esp
Változáskövetés¶
watch symbol
: változó, regiszter, memóriacím (vagy komplexebb kifejezés) értékének a váltazása esetén álljon meg a program futása (mint egy breakpoint érték változása alapján). Többszálú program esetén minden szálon van változásfigyelés.info watchpoints
: listázásukrwatch symbol
: megállítja a program futását, ha a szimbólumot olvastákawatch symbol
: megállítja a program futását, ha a szimbólumot írták vagy olvasták
Feltételes breakpointok elhelyezése¶
Használhatjuk a célnyelv utasításait, szimbólumait, hogy a breakpointokat feltételekhez kössük! Meglévő breakpointhoz is tudunk az azonosító száma alapján utólag feltételt rakni (pl. info breakpoints-al kideríthető)
break file.c:20 if i == 112
: breakpoint a 20-as soron, ha az i 112break file.c:17 if strcmp(input, "password") == 0
: breakpoint a 17-es soron, ha a string értéke passwordcond 8 *p == 78
: új feltétel a 8-as breakpointhozcond 8
: a 8-as breakpoint feltételének törlése
Függvényhívás¶
Lehetőségünk van kézzel meghívni a célprogram valamely függvényét (pl. ha direkt írtunk egyet debug céllal) Paramétereket is adhatunk meg neki.
call segedfgv()
.gdbinit használata¶
GDB konfigurációs fájl, mely a gdb indításakor automatikusan betöltődik. Segítségével lehet kicsit automatizálni a debuggolást. Helyei lehetnek:
- /etc/gdbinit: rendszerszintű beállítások
- ~/.gdbinit: user szintű beállítások
- ./.gdbinit: csak az adott munkakönyvtárból futó gdb-re vonatkozik. A lokális gdgbinit betöltése ki lehet kapcsolva, ekkor a megjelenő hibaüzenetek alapján lehet megoldást keresni (hozzáadni a biztonsági kivételt vagy teljesen kikapcsolni ezt az ellenőrzést)
Egy példafájl tartalma:
1 2 3 4 5 6 7 8 9 10 11 |
|
Ha elkészítettük ezt a fájlt már csak el kell indítani a gdb-t (és nem is kell most neki megadni kézzel a célprogramot). A gdbinit fájlban saját segédfüggvényeket is létrehozhatunk. Például:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
|
Ha így adjuk meg a gdbinit fájlunkat a breakpoint elérésekor automatikusan lefut majd a prnt tartalma. De kézzel is meghívhatjuk.