GDBまとめ ②GDBの基本的な使い方(その1)

参考書籍 : Debug Hacks


デバッグオプション付きでコンパイル、ビルドする
gccの-gオプションで、デバッグ情報付きでコンパイルする。

$ gcc -Wall -O2 -g ソースファイル
  • Wallは警告オプションがすべて無効になる。-O2は最適化オプション

gdbの起動
以下のコマンドで、gdbを起動する。

$ 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

コアファイルとデバッグ対象の実行ファイルがあれば、後にコアファイルを生成した時点でのレジスタやメモリの値を確認できる。