この記事は自作OS Advent Calendar 2018の12日目の記事です。
はじめに
最近xv6の教科書を読んでみている。
xv6の教科書ではQEMUの-sオプションを用いたデバッグが推奨されている。-sオプションは-gdb tcp::1234の省略版でこのオプションを付けてQEMUを使うと1234番ポートでgdbserverを立ち上げてくれる。
あとはgdbでtarget remote :1234とかをするとQEMUにアタッチされていい感じにデバッグできる。
FreeBSDでこれができたらコードリーディングとか捗りそう...
いろいろ試行錯誤してみたらできました。
ということでQEMUの-sオプションでFreeBSDのカーネルのデバッグをしてみました。
デバッグ構成
今回デバッグするのに2つのFreeBSDが必要になる。
| 内容 | OS | 仮想化 | ここでの名称 |
|---|---|---|---|
| デバッグする側 | FreeBSD 11.2 | KVM | Source |
| デバッグされる側 | FreeBSD 11.2 | QEMU | Target |
| ホスト | Ubuntu 18.04.1 Desktop | × | Host |
Sourceの方はKVMを使っていますがなんでもよいと思います。
ただし、Sourceの方でカーネル再構築を行います。
Hostの20022番ポートがTargetの22番ポートにリダイレクトされるように設定した。
HostとSourceは192.168.122.0/24で通信できるようにした。
この辺はHostからFreeBSDに相互で通信できれば特に問題ないと思われる。

やり方
1. FreeBSDのインストール
まずはSourceとTargetを用意します。
詳しいインストールの方法1 は割愛します。
Targetのインストール準備としてHostで以下のコマンド実行します。-redir tcp:20022::22のオプションでHostの20022番ポートがTargetの22番ポートにリダイレクトされるようにしています。
$ qemu-img create -f qcow freebsd_debug.img 30G
$ qemu-system-x86_64 -m 4096 -hda freebsd_debug.img -boot c -cdrom FreeBSD-11.2-RELEASE-amd64-dvd1.iso -redir tcp:20022::22※Sourceはsrcを含むようにしましょう※

SourceとTargetともに起動させます。
2. カーネルの再構築
Sourceのカーネルを再構築してデバッグ情報を含むようにします。
カーネルの再構築はFreeBSDのバージョンによって多少異なる点があるので自分が選択したバージョンに合わせましょう。
まずはもとの設定ファイルをコピーします。
$ cd /usr/src/sys/amd64/conf
$ cp GENERIC MYKERNELMYKERNELの設定に以下を足します。
makeoptions DEBUG=-g
options KDB
options DDB実際のMYKERNELをgistsにあげておきます。 該当箇所は24/ 83/ 85行目です。
再構築をします。
$ cd /usr/src
$ make buildkernel KERNCONF=MYKERNEL
$ make installkernel KERNCONF=MYKERNELmake buildkernelは時間がかかるのでここでコーヒーブレイクです。
終わったらSourceを再起動させましょう。
3. 転送
Sourceの/boot/kernel/以下のファイルを全てTargetの/boot/kernel/に送りつけます。
Hostで以下のコマンドを実行しました。
$ scp -r root@192.168.122.2:/boot/kernel .
$ scp -P 20022 kernel/* root@127.0.0.1:/boot/kernel2行目のコマンドの前にHostでstrip -xとかするとシンボル情報がなくなってスマートになります。
$ du kernel -h
123M kernel
$ sudo strip -x kernel/*.ko
$ sudo strip -x kernel/kernel
$ du kernel -h
117M kernel4. 再起動
Targetをシャットダウンさせます。
Targetを-sオプションを付けて起動させます。
これでgdbserver立ち上げてくれます。
$ qemu-system-x86_64 -m 4096 -hda freebsd_debug.img -boot c -cdrom FreeBSD-11.2-RELEASE-amd64-dvd1.iso -redir tcp:20022::22 -sこれに加えて-Sオプションを付けると起動からデバッグできます。
5. kgdb
kgdb2はカーネルデバッグのためのデバッガーです。
$ kgdb /boot/kernel/kernel
GNU gdb 6.1.1 [FreeBSD]
...
This GDB was configured as "amd64-marcel-freebsd"...
(kgdb) target remote 192.168.122.1:1234
...
(kgdb) b sys_mkdir
Breakpoint 1 at 0xffffffff80bdac14: file /usr/src/sys/kern/vfs_syscalls.c, line 3337.
(kgdb) c
Continuing.そしておもむろにTargetでmkdir testを実行すると
きちんと止まった!でけた!

クリックすると画像は大きくなります。
これだけでもシステムコールがどういう感じで呼ばれてるか少しわかるかと思います。
やったー!
終わりに
これでいい感じにFreeBSDのカーネルコードリーディングができそうです。
やっぱりコードとにらめっこするよりも実行しながらできると楽しいですね。
たぶん、本来は2つをシリアル通信でつなげてやるんだと思うんですがQEMUの機能でやってみました。
なぜFreeBSDなのかという疑問については聞かないでください。
13日目の自作OS Advent Calendar 2018はgarasuboさんの
「RustでArm Cortex-Mプログラミングをする 2018」です。
お!Rust好きとしては楽しみです。
参考文献
https://www.freebsd.org/doc/en/books/handbook/bsdinstall.html 同じバージョン、アーキテクチャでインストールしていきます。
私の環境ではFreeBSD-11.2-RELEASE-amd64を選択しました。 ↩︎https://en.wikipedia.org/wiki/KGDB
Sourceでkgdbを起動させてHostで起動しているQEMUにアタッチします。 この時点でTargetは停止します。
ここからは好きにデバッグしてみてください。 ここでは動作の確認のためにsys_mkdirにブレイクポイントを設定して、Sourceの動作を再開させます。 ↩︎