GDBまとめ ②GDBの基本的な使い方(その1)
参考書籍 : Debug Hacks
①デバッグオプション付きでコンパイル、ビルドする
gccの-gオプションで、デバッグ情報付きでコンパイルする。
$ gcc -Wall -O2 -g ソースファイル
- Wallは警告オプションがすべて無効になる。-O2は最適化オプション
$ gdb 実行ファイル名
メッセージが表示され、gdbのプロンプトが出る。
③ブレークポイントの設定
ブレークポイントは、関数名や行番号などに設定できる。
breakもしくはbでブレークポイントを設定できる。
(gdb) b main Breakpoint 1 at 0x1050: file hello.c, line 5. (gdb) b hello.c:6 Breakpoint 2 at 0x1060: file hello.c, line 6.
"b 関数名"、"b 行番号"、"b ファイル名:行番号"などの形式でブレークポイントをセットする。
共有ライブラリが読み込まれていない場合は確認が出るので、yを入力する。
④実行
runもしくはrコマンドで実行を開始する。runコマンドには引数を与える事ができる。
(gdb) run Starting program: /root/work/hello Breakpoint 1, main () at hello.c:5
startコマンドを使うと、自動でmainにブレークポイントをセットし、mainまで実行される。
④スタックフレームの表示
その関数がどこから呼ばれたかなどの情報を得るために、バックトレースを確認する必要がある。
バックトレースは、スタックフレームに積み上がる。
backtraceもしくはbtコマンドで、ブレークポイントで一時停止したときのスタックフレームを表示できる。
whereコマンド、info stackコマンド、info sコマンドもbacktraceと同じ動作をする。
(gdb) backtrace #0 foo () at hello.c:5 #1 0x000055555555515a in main () at hello.c:11
backtrace Nで最初のN個のフレームだけバックトレースを表示できる。
backtrace -Nで最後のN個のフレームだけバックトレースを表示できる。
backtrace fullでローカル変数も表示できる。
fullはNや-Nと組み合わせて、backtrace full -Nでローカル変数とバックトレースを表示できる。
⑤値の表示
printもしくはpコマンドで変数を表示できる。
(gdb) print argv $1 = (char **) 0x7fffffffece8 (gdb) print *argv $2 = 0x7fffffffeedd "/root/work/hello" (gdb) print argv[0] $3 = 0x7fffffffeedd "/root/work/hello" (gdb) print argv[1] $4 = 0x7fffffffeeee "asdf"
info registersもしくはinfo regコマンドでレジスタを表示する。
(gdb) info registers rax 0x10 16 rbx 0x0 0 rcx 0x0 0 rdx 0x0 0 rsi 0x555555556016 93824992239638 rdi 0x7ffff7fb34c0 140737353823424 rbp 0x7fffffffec00 0x7fffffffec00 rsp 0x7fffffffebe0 0x7fffffffebe0 r8 0xffffffff 4294967295 r9 0x10 16 r10 0x7fffffffeedd 140737488350941 r11 0x0 0 r12 0x555555555060 93824992235616 r13 0x7fffffffece0 140737488350432 r14 0x0 0 r15 0x0 0 rip 0x5555555551b4 0x5555555551b4 <main+93> eflags 0x206 [ PF IF ] cs 0x33 51 ss 0x2b 43 ds 0x0 0 es 0x0 0 fs 0x0 0 gs 0x0 0
特定のレジスタのみ表示したいときは、printに$レジスタ名で表示できる。
(gdb) print $rax $5 = 16
print/フォーマット 変数 でフォーマットを指定して表示できる。
print/x $raxで16進数で表示する。他にもoで8進数、cでASCIIとして表示などがある。
xコマンドでメモリの中身を表示できる
(gdb) x $rip 0x5555555551b4 <main+93>: 0xb8 (gdb) x/i $rip => 0x5555555551b4 <main+93>: mov $0x0,%eax
x/iで機械語命令として表示することができる。
x/32xwで、32ワード(4byte*32個)を16進数で表示することができる。
(gdb) x/32xw $rsp 0x7fffffffebe0: 0xffffece8 0x00007fff 0x55555060 0x00000002 0x7fffffffebf0: 0xffffece0 0x00007fff 0x00000000 0x00000000 0x7fffffffec00: 0x555551c0 0x00005555 0xf7e1ae0b 0x00007fff 0x7fffffffec10: 0x00000000 0x00000000 0xffffece8 0x00007fff 0x7fffffffec20: 0x00040000 0x00000002 0x55555157 0x00005555 0x7fffffffec30: 0x00000000 0x00000000 0xd9bb8cd7 0x89af1217 0x7fffffffec40: 0x55555060 0x00005555 0xffffece0 0x00007fff 0x7fffffffec50: 0x00000000 0x00000000 0x00000000 0x00000000
x/<数><フォーマット><単位> 変数 で表示する。
単位は、wのワードだけでなく、bのバイト、hのハーフバイト(2byte)、gのジャイアントバイト(8byte)がある。
disassembleもしくはdisasで逆アセンブルできる。
disassembleで現在の関数をすべて逆アセンブルする。
(gdb) disas Dump of assembler code for function main: 0x0000555555555157 <+0>: push %rbp 0x0000555555555158 <+1>: mov %rsp,%rbp 0x000055555555515b <+4>: sub $0x20,%rsp 0x000055555555515f <+8>: mov %edi,-0x14(%rbp) 0x0000555555555162 <+11>: mov %rsi,-0x20(%rbp) 0x0000555555555166 <+15>: movl $0x0,-0x4(%rbp) 0x000055555555516d <+22>: lea 0xe90(%rip),%rdi # 0x555555556004 0x0000555555555174 <+29>: callq 0x555555555030 <puts@plt> 0x0000555555555179 <+34>: mov $0x0,%eax 0x000055555555517e <+39>: callq 0x555555555145 <foo> 0x0000555555555183 <+44>: mov -0x14(%rbp),%eax 0x0000555555555186 <+47>: mov %eax,%esi 0x0000555555555188 <+49>: lea 0xe82(%rip),%rdi # 0x555555556011 0x000055555555518f <+56>: mov $0x0,%eax 0x0000555555555194 <+61>: callq 0x555555555040 <printf@plt> 0x0000555555555199 <+66>: mov -0x20(%rbp),%rax 0x000055555555519d <+70>: mov (%rax),%rax 0x00005555555551a0 <+73>: mov %rax,%rsi 0x00005555555551a3 <+76>: lea 0xe6a(%rip),%rdi # 0x555555556014 0x00005555555551aa <+83>: mov $0x0,%eax 0x00005555555551af <+88>: callq 0x555555555040 <printf@plt> => 0x00005555555551b4 <+93>: mov $0x0,%eax 0x00005555555551b9 <+98>: leaveq 0x00005555555551ba <+99>: retq End of assembler dump.
disassemble プログラムカウンタ(rip) でプログラムカウンタの値を含め関数全体を、
disassemble 開始アドレス 終了アドレス で範囲内のアドレスを逆アセンブルする。
⑥実行の継続
nextまたはnでステップオーバーが、stepもしくはsでステップインができる。
next/stepはソースコードの1行ごとに実行するのに対し、nexti/stepiはアセンブリ命令ごとに実行できる。
continueまたはcで、ブレークポイントで停止中のプログラムを再開する。
⑦ウォッチポイント、ハードウェアブレークポイント
大規模なソフトウェアなど、変数がどこで変更されているのかわからないときはウォッチポイントを使う。
watch 式 で式が変更されたときに実行を一時停止する。式は変数や定数。
awatch 式 で式が参照、変更されたとき実行を一時停止する。
rwatch 式 で式が参照されたとき実行を一時停止する。
ハードウェアブレークポイントは、hbreakコマンドでセットできる。
ハードウェアブレークポイントの数には限りがある。
到達するとプログラムが停止する点はソフトウェアブレークポイントと同じだが、ハードウェアブレークポイントは一時停止後にブレークポイントが解除される。
⑧ブレークポイント、ウォッチポイントの削除
deleteもしくはdでブレークポイント、ウォッチポイントの削除ができる。
delete 番号 でブレークポイント、ウォッチポイントを削除する
番号はブレークポイント、ウォッチポイント設定時に表示される。
⑨変数の値の変更
set variableコマンドで変数の値の変更ができる。
set variable <変数>=<式> で変更する。
⑩コアファイルの生成
generate-core-fileコマンドで、デバッグしているプロセスのコアファイルを作成できる。
(gdb) generate-core-file Saved corefile core.2365