FPGA開発日記

カテゴリ別記事インデックス https://msyksphinz.github.io/github_pages , English Version https://fpgadevdiary.hatenadiary.com/

ISSとGDBを接続してxv6をデバッグ(x86のGDBで挙動確認)

今日は月曜日から飲み会になってしまい、進捗が少ない。。。

msyksphinz.hatenablog.com

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を修正していこう。