今日は月曜日から飲み会になってしまい、進捗が少ない。。。
GDBの動きは結局どうなっているんだろうね?命令の実行前で止まるのか、そしてそのときのPCはどこを差しているのか? まあ、あれこれ文献調べるよりも、実際に試してみたほうが良いと思うのでやってみる。
GDBの挙動を、命令単位で確認する
試したのは、以下のプログラムだ、非常に簡単!
int main (void) { int i; int total = 0; for (i = 0; i < 10; i++) { total += i; } return total; }
ここで、"total+=i"の部分にブレークポイントを仕掛けると、どのような挙動になるのだろうか?
$ gdb a.out (gdb) symbol-file a.out Load new symbol table from "a.out"? (y or n) y Reading symbols from a.out...done. (gdb) source . .: Success. (gdb) b gdb_test.c:6 Breakpoint 1 at 0x40050a: file gdb_test.c, line 6.
ブレークポイントまで実行する
以下のところで停止した。
+--gdb_test.c----------------------- |1 int main (void) |2 { |3 int i; |4 int total = 0; |5 for (i = 0; i < 10; i++ B+>|6 total += i; |7 } |8 |9 return total; |10 }
このときの状態をdisasmで表示してみよう。
(gdb) disassemble Dump of assembler code for function main: 0x00000000004004f6 <+0>: push %rbp 0x00000000004004f7 <+1>: mov %rsp,%rbp 0x00000000004004fa <+4>: movl $0x0,-0x4(%rbp) 0x0000000000400501 <+11>: movl $0x0,-0x8(%rbp) 0x0000000000400508 <+18>: jmp 0x400514 <main+30> => 0x000000000040050a <+20>: mov -0x8(%rbp),%eax 0x000000000040050d <+23>: add %eax,-0x4(%rbp) 0x0000000000400510 <+26>: addl $0x1,-0x8(%rbp) 0x0000000000400514 <+30>: cmpl $0x9,-0x8(%rbp) 0x0000000000400518 <+34>: jle 0x40050a <main+20> 0x000000000040051a <+36>: nop 0x000000000040051b <+37>: pop %rbp 0x000000000040051c <+38>: retq End of assembler dump.
ふむう、PCは0x040050aを差している。つまり、ブレークポイントを貼った当該命令の場所でしっかりと止まった。 このときの%eaxの値は?
(gdb) p/x $eax $1 = 0x4004f6
なんだこりゃ、ゴミか?どうやら、「命令実行前」の状態らしい。まだ当該命令が実行されていないので、%eaxの値はゴミが入っているのか。
stepiを実行して、次の状態を確認する
(gdb) stepi (gdb) disassemble Dump of assembler code for function main: 0x00000000004004f6 <+0>: push %rbp 0x00000000004004f7 <+1>: mov %rsp,%rbp 0x00000000004004fa <+4>: movl $0x0,-0x4(%rbp) 0x0000000000400501 <+11>: movl $0x0,-0x8(%rbp) 0x0000000000400508 <+18>: jmp 0x400514 <main+30> 0x000000000040050a <+20>: mov -0x8(%rbp),%eax => 0x000000000040050d <+23>: add %eax,-0x4(%rbp) 0x0000000000400510 <+26>: addl $0x1,-0x8(%rbp) 0x0000000000400514 <+30>: cmpl $0x9,-0x8(%rbp) 0x0000000000400518 <+34>: jle 0x40050a <main+20> 0x000000000040051a <+36>: nop 0x000000000040051b <+37>: pop %rbp 0x000000000040051c <+38>: retq End of assembler dump. (gdb) p/x $eax $2 = 0x0
次の命令に移動した。そして、%eaxにはちゃんと0が設定されている。ただしこのときに、"total+=i(i=0)"なので、%eaxの値は「加算前」か、「加算後」か分からない。 次に、continueをして、どうなるかを確認する。
(gdb) continue Continuing. Breakpoint 1, main () at gdb_test.c:6 (gdb) disassemble Dump of assembler code for function main: 0x00000000004004f6 <+0>: push %rbp 0x00000000004004f7 <+1>: mov %rsp,%rbp 0x00000000004004fa <+4>: movl $0x0,-0x4(%rbp) 0x0000000000400501 <+11>: movl $0x0,-0x8(%rbp) 0x0000000000400508 <+18>: jmp 0x400514 <main+30> => 0x000000000040050a <+20>: mov -0x8(%rbp),%eax 0x000000000040050d <+23>: add %eax,-0x4(%rbp) 0x0000000000400510 <+26>: addl $0x1,-0x8(%rbp) 0x0000000000400514 <+30>: cmpl $0x9,-0x8(%rbp) 0x0000000000400518 <+34>: jle 0x40050a <main+20> 0x000000000040051a <+36>: nop 0x000000000040051b <+37>: pop %rbp 0x000000000040051c <+38>: retq End of assembler dump. (gdb) p/x $eax $3 = 0x0
一度ループを通過しても、まだ%eaxの値は0だった。だから、2回目のループに入っていても、%eaxの加算は実行されていない。 つまり、ブレークポイントで指定された命令のところで停止するが、その命令はまだ実行されていない、ってことか。分かったぞ。
今度は、これをベースにISSを修正していこう。