Qemu + gdbでXen上のOSカーネルを見る


とりあえず、gdb上でどうやってユーザモードのバイナリを見るかというタッチの話。Xenにとって、上に載せるOSがアプリケーションに相当することに注意する。

サマリ

  • qemugdbスタブはこのようなシチュエーションのために作られた物でない
    • たとえば、ユーザモード側のシグナルをgdb側に通知する積極的な機構は無い。(page faultが即SEGVというわけではなく、qemuには正当なpage faultかどうかを判別することは出来ない。qemu専用のhookを追加し、vm_stop(EXCP_DEBUG)でstub側にイベントを通知することは出来る。)
    • Xengdbスタブはこれが出来、また、動作中のOSのcoreイメージを収集して動作イメージを調査することもできる。
    • Linuxのkgdbや、他のOSも同様にカーネル側のスタブを普通は使う。また、qemuのユーザモードエミュレーションも同様に発生したシグナルを通知できる。
  • (単純に、)全てのアドレスは仮想アドレスで取り扱われる。複数のコンテキストを扱うためのうまい機構は無い。
  • ブレークポイントなどは正常に動作する。メモリのウォッチポイントも利用出来る。
  • 独自のオブジェクトファイルフォーマットを使っている場合は、gdbに読み取らせるためのELFまたはPEを用意しておくのが好ましい。

ちょっとした準備(XenのDOM0としてx86_32のmini-osを起動する)

今のところ、x86_64なqemugdbスタブは正常でない。というわけで、Xenとmini-osの両方をx86_32でビルドする必要がある。
メモリマップの関係で、32bit環境ではmini-osをそのままDOM0として使うことは出来ない(もちろん、DOM0のための機能がmini-osには無いので、この修正をしてもmini-osはカーネルを開始することが出来ない)。
平たく言えば、ロードアドレスを0から0xc0000000に変更する。

        .ascii  ",VIRT_BASE=0xc0000000" /* &_text from minios_x86_32.lds */
        .ascii  ",ELF_PADDR_OFFSET=0xc0000000"
  • xen-3.4.2/extras/mini-os/arch/x86/minios-x86_32.lds
SECTIONS
{
  . = 0xc0000000;

あとは、普通に両者をビルドする。

(トップで、)
make XEN_TARGET_ARCH=x86_32 xen
(extras/mini-osで、)
make XEN_TARGET_ARCH=x86_32

stubを有効にしたqemuの起動

微妙に重要なのは-Sオプションで、これを指定すると、エミュレーションはpause状態から開始される。よってgdb側から実行開始を指示することが出来る。

qemu-system-x86_64 -cpu qemu32 -gdb tcp::1234 --kernel xen-3.4.2 -initrd ../../../extras/mini-os/mini-os -append "noreboot console=com1" -serial stdio -S -nographics

qemuはMultibootのローダを持っているのでそれを利用する。Dom0のイメージは何故かinitrdオプションで指定する。xenを普通にビルドするとgzipで圧縮してしまうので、予め圧縮を解除しておくのが好ましい。
あとは、普通にgdbを起動する。デバッグ対象としては、ユーザモードのプログラムを指定する。もし、OSが独自のバイナリフォーマットを採用していたり、事前にstripしているなら、それに変換する前のELFやPEのイメージを利用する。

oku@sandbox ~ $ gdb check/xen-3.4.2/extras/mini-os/mini-os
GNU gdb 6.8
Copyright (C) 2008 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu"...
(gdb) target remote localhost:1234
Remote debugging using localhost:1234
[New Thread 1]
0x0000fff0 in ?? ()
(gdb) b start_kernel
Breakpoint 1 at 0xc000e08b: file kernel.c, line 482.
(gdb) c
Continuing.

Breakpoint 1, start_kernel (si=Cannot access memory at address 0x8
) at kernel.c:482
482     {
(gdb)

起動後は、target remote localhost:1234でqemuに接続→ブレークポイントを設定→cで実行開始 のようにする。
もちろん、InsightとかDDDのようなgdbフロントエンドでも同様に作業を行える。
同様の手法でxenブレークポイントを置くことも出来るが、そのためにはxen-sym-3.4.2をgdbにロードする必要がある。gdbはMultibootバイナリをロードすることは出来ない。