qemu(2)

前回ちらっと書いた、GaucheをCから操るって話、興味が有ったので追試してみた。舞台は、作者 さんに倣ってウブ上です。

sakae@ubuntu:~/e-scheme$ make run
gcc -std=gnu99 -Ofast -Wall -Werror -o a.out `gauche-config -I` main.c `gauche-config -L` `gauche-config -l`
./a.out: error while loading shared libraries: libgauche-0.9.so.0.3: cannot open shared object file: No such file or directory
make: *** [run] エラー 127

sakae@ubuntu:~/e-scheme$ ldd ./a.out
        linux-gate.so.1 =>  (0x001de000)
        libgauche-0.9.so.0.3 => not found
        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0x00d4d000)
        /lib/ld-linux.so.2 (0x0066a000)

見事にエラーです。作者さんと舞台が違うんでしょうか。エラーにはなるも、a.outは出来上がって いたので、gauche系の舞台と言うより、それを支える土台がふら付いているような気がします。 で、基本の確認をした所、エンジンが見つからなかったんで、すっぽりと抜けたアプリが出来上がって ましたよ。

しょうがないので、

sakae@ubuntu:~$ cat /etc/ld.so.conf.d/gauche.conf
/usr/local/lib/gauche-0.9/0.9.3.3/i686-pc-linux-gnu

こんなのを用意してあげてから、ldconfigしてあげました。

sakae@ubuntu:~/e-scheme$ ldd ./a.out
        linux-gate.so.1 =>  (0x007b9000)
        libgauche-0.9.so.0.3 => /usr/local/lib/gauche-0.9/0.9.3.3/i686-pc-linux-gnu/libgauche-0.9.so.0.3 (0x00110000)
        libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0x003b8000)
        libdl.so.2 => /lib/i386-linux-gnu/libdl.so.2 (0x00a34000)
        libcrypt.so.1 => /lib/i386-linux-gnu/libcrypt.so.1 (0x00a9b000)
        libm.so.6 => /lib/i386-linux-gnu/libm.so.6 (0x00562000)
        libpthread.so.0 => /lib/i386-linux-gnu/libpthread.so.0 (0x00598000)
        /lib/ld-linux.so.2 (0x00cd6000)

これで動きましたとさ。

qemu for Windows

今までQEMUをLinuxや*BSDで動かしてきたけど、どうやらWindowsでも動くようだ。 MinGW 環境で QEMU 1.0.1 のビルドに、実例が 載ってた。けど、大変そう。。。で躊躇しちゃう。

さくっと、バイナリーを頂いてくるのが、Windows流だと思うぞ。フランスまで取りに行くなら、 QEMU on Windows、日本の勇士の方に甘えるなら、 歓迎光臨 TAKEDA, toshiya's HOME PAGE が良い。

私は、Dirがすっきりした(主観ですけど)フランス版を使う事にした。両者共、試験用のLinuxが 同梱されているので、動作確認は容易だろう。

折角入れたのだから、起動確認のLinuxだけじゃなくて、ちゃんとしたOSを動かしてみたい。付属の バッチには、Fedoraの例がREMになって載ってたけど、今更Fedoraでもあるまい。ネットをさ迷って みると、 qemu linux kernel なんてのをやってる方がいた。こういうのxv6のファイルシステムの作り方と通じるものがあって、 参考になるなあ。でもね。。。

糞石のアセンブラがまがりなりにも読めるようになった今、i386版のFreeBSDが良かろう。 入れてみるか。 日本にある FreeBSD 関連のサイトから、古めの FreeBSD7.3のCDを落としてきて入れた。(普通にFreeBSDを入れるなら、 今さら聞けない? 簡単にFreeBSD環境を作る3つの方法が お勧め、うんと過去に遡りたいなら、 ftp://ftp-archive.freebsd.org/pub/FreeBSD-Archive/old-releases/ とか、 ftp://ftp.tw.freebsd.org/pub/ISO-IMAGES-i386/かな)

qemu-img.exe create -f qcow2 F73.img 2G
qemu-system-i386w.exe -m 128 -cdrom ./disc1.iso -hda ./F73.img -boot d

一応インストールが終わったので、例に倣ってバッチを書いておいた。

REM Start qemu on windows.
@ECHO OFF

START qemu-system-i386w.exe -L Bios -m 128 -hda F73.img  ^
  -rtc base=localtime,clock=host ^
  -redir tcp:2222::22 ^
  -no-acpi -no-hpet  -vga std

バッチの継続行って、ハットするような記号なのね。知らんかったよ。普段はバックスラッシュの 世界に住んでいるからなあ。で、vgaの画面じゃログが取れないので、端末へリダイレクト するようにしておいた。

ssh -p 2222 localhost
$ ps
   :
  130  ??  Is     0:00.01 adjkerntz -i
  288  ??  Is     0:00.00 dhclient: em0 (dhclient)
  484  ??  Is     0:00.01 /sbin/devd
  530  ??  Ss     0:00.21 /usr/sbin/syslogd -s
  654  ??  Is     0:00.04 /usr/sbin/sshd
  701  ??  Is     0:00.29 sshd: sakae [priv] (sshd)
  705  ??  S      0:00.10 sshd: sakae@ttyp0 (sshd)
  698  v0  Is+    0:00.07 /usr/libexec/getty Pc ttyv0
  699  v1  Is+    0:00.02 /usr/libexec/getty Pc ttyv1
  700  v2  Is+    0:00.03 /usr/libexec/getty Pc ttyv2
  272 con- I      0:00.01 dhclient: em0 [priv] (dhclient)
  693 con- I      0:00.00 sh /etc/rc autoboot
  694 con- I      0:00.02 logger -p daemon.notice -t fsck
  696 con- I      0:00.00 sh /etc/rc autoboot
  697 con- I      0:00.01 sleep 60
  707  p0  Ss     0:00.12 -sh (sh)
  710  p0  R+     0:00.00 ps awx
$ ifconfig em0
em0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> metric 0 mtu 1500
        options=9b<RXCSUM,TXCSUM,VLAN_MTU,VLAN_HWTAGGING,VLAN_HWCSUM>
        ether 52:54:00:12:34:56
        inet 10.0.2.15 netmask 0xffffff00 broadcast 10.0.2.255
        media: Ethernet autoselect (1000baseTX <full-duplex>)
        status: active

端末から接続する時は、上記のように行う。勿論、FreeBSD側はNICにIPを振ってsshdが起動して いなければならない。この辺は、V-boxと同じだな。嗚呼、少し違った。このFreeBSD世界では 時計の進み方が超スローなんよ。起動してから1分後に走る、fsckも実時間で10分ぐらいは、 待たされるかな。

調子に乗って、kernelのdebugも出来るようにしようと思った。configファイルに

makeoptions     DEBUG=-g                # Build kernel with gdb(1) debug symbols
options         DDB
options         KDB
##options               DDB_NOKLDSYM
makeoptions     NO_MODULES=yes

こんな設定を書いてコンパイル。だけど、2時間待ってもコンパイルが完了しない。諦めてVMWAREで コンパイルしたよ。

linking kernel.debug
   text    data     bss     dec     hex filename
8304412  851264  424128 9579804  922d1c kernel.debug
objcopy --only-keep-debug kernel.debug kernel.symbols
objcopy --strip-debug --add-gnu-debuglink=kernel.symbols kernel.debug kernel
177.744u 227.007s 7:00.98 96.1% 6304+1038k 819+4413io 42pf+0w

そしたら、7分で終了した。それを持ってきてインストール。

mkdir -p /boot/kernel
install -p -m 555 -o root -g wheel kernel /boot/kernel
install -p -m 555 -o root -g wheel kernel.symbols /boot/kernel

ここまでは、Windows上で全てカタが付いたんだけど、debug(と言う観察会)やqemuのmonitorを 動かそうとすると、どうもWindows上じゃ都合が悪い。

ええい、FreeBSDの入ったDisk(F73.img)を持って、Linuxへ殴りこみだ。

FreeBSD on Linux

を、ArchLinux上でやります。Linxu上だと、-cursesが使えるので、起動は次のようにしました。

qemu -m 64 -hda F73.img -curses \
  -monitor telnet::4444,server,nowait \
  -S -gdb tcp::26001

変なオプションの -monitorは、このポート(4444)にlocalhostからtelnet接続すると、qemuのmonitorが出てきます。 後、gdbで追跡出来るようにもしてます。

別端末からgdbを起動して、少しづつ動かして行くんですが、kernelのソースを入れておかないと 魅力半減となってしまいますから、それも入れておきます。 gdbを動かす基点は、カーネルをコンパイルした場所になるので、こんなスクリプトを書いて おくと便利です。

here=`pwd`
cd sys/i386/compile/GENERIC
emacs
# gdb -q -x .gdbinit kernel.debug
cd $here

嗚呼、移動にcdを使ってるけど、スマートにpushd/popdの方が良かったかな?

[sakae@arch 7f]$ pushd sys/i386/compile/GENERIC/
~/7f/sys/i386/compile/GENERIC ~/7f
[sakae@arch GENERIC]$ gdb -q -x .gdbinit kernel.debug
Reading symbols from /home/sakae/7f/sys/i386/compile/GENERIC/kernel.debug...done
   :
target remote localhost:26001
0x0000fff0 in ?? ()
(gdb) b *0x7c00
Breakpoint 1 at 0x7c00
(gdb) c
Continuing.

Breakpoint 1, 0x00007c00 in ?? ()

これで、FreeBSDを起動した端末は、VGA Blank modeの表示から、次の画面に切り替わりました。

SeaBIOS (version 1.7.1-20121219_085028-stef)


iPXE v1.0.0-591-g7aee315
iPXE (http://ipxe.org) 00:03.0 C900 PCI2.10 PnP PMM+03FC82E0+03F882E0 C900


Booting from Hard Disk...

gdb端末で,continueすると、もう一度、breakがかかりました。0x7c00を2度訪れています。 そして、FreeBSDの端末の方は、

F1   FreeBSD

Boot:   F1

このようにOSのセレクタ画面が出てきましたよ。続いて、contします。

BTX loader 1.00  BTX version is 1.02
Consoles: internal video/keyboard
BIOS drive A: is disk0
BIOS drive C: is disk1
BIOS 639kB/64504kB available memory

FreeBSD/i386 bootstrap loader, Revision 1.1
(root@walker.cse.buffalo.edu, Sun Mar 21 04:39:19 UTC 2010)
Loading /boot/defaults/loader.conf
/boot/kernel/kernel text=0x7eb80a data=0xcfd40+0x678e0 /

こうしてカーネルのローディグが始まり、やがて、下図のような画面になったら、すかさずspace key とか叩きます。

 ?                                         ?      ______
 ?                                         ?     |  ____| __ ___  ___
 ?          Welcome to FreeBSD!            ?     | |__ | '__/ _ \/ _ \
 ?                                         ?     |  __|| | |  __/  __/
 ?                                         ?     | |   | | |    |    |
 ?  1. Boot FreeBSD [default]              ?     |_|   |_|  \___|\___|
 ?  2. Boot FreeBSD with ACPI disabled     ?      ____   _____ _____
 ?  3. Boot FreeBSD in Safe Mode           ?     |  _ \ / ____|  __ \
 ?  4. Boot FreeBSD in single user mode    ?     | |_) | (___ | |  | |
 ?  5. Boot FreeBSD with verbose logging   ?     |  _ < \___ \| |  | |
 ?  6. Escape to loader prompt             ?     | |_) |____) | |__| |
 ?  7. Reboot                              ?     |     |      |      |
 ?                                         ?     |____/|_____/|_____/
 ?                                         ?
 ?                                         ?
 ?                                         ?
 ?  Select option, [Enter] for default     ?
 ?  or [Space] to pause timer  9           ?
 ???????????????????????????????????????????




Type '?' for a list of commands, 'help' for more detailed help.
OK

ここで、boot とか、boot -d (ddbに落ちる)とかして、kernelを動かすわけです。

 ?  or [Space] to pause timer  0           ?
 ???????????????????????????????????????????


ACPI autoload failed - no such file or directory
KDB: debugger backends: ddb
KDB: current backend: ddb
Copyright (c) 1992-2010 The FreeBSD Project.
Copyright (c) 1979, 1980, 1983, 1986, 1988, 1989, 1991, 1992, 1993, 1994
        The Regents of the University of California. All rights reserved.
FreeBSD is a registered trademark of The FreeBSD Foundation.
FreeBSD 7.3-RELEASE #0: Tue Feb 26 11:03:22 JST 2013
    root@k73.localdomain:/usr/src/sys/i386/compile/GENERIC i386
Timecounter "i8254" frequency 1193182 Hz quality 0
CPU: QEMU Virtual CPU version 1.2.1 (2240.79-MHz 686-class CPU)
  Origin = "GenuineIntel"  Id = 0x633  Stepping = 3

起動した後は(途中でもいいけど)、gdbからチョッカイを出せます。

Continuing.
^C
Program received signal SIGINT, Interrupt.
cpu_idle_default () at ../../../i386/i386/machdep.c:1163
1163    }
(gdb) l
1158             * we must absolutely guarentee that hlt is the
1159             * absolute next instruction after sti or we
1160             * introduce a timing window.
1161             */
1162            __asm __volatile("sti; hlt");
1163    }
1164
1165    /*
1166     * Note that we have to be careful here to avoid a race between checking
1167     * sched_runnable() and actually halting.  If we don't do this, we may waste

gdb端末で、CTRL-Cして止めてみました。割り込みをイネーブルにしてhaltせいってやってますな。 少しは糞石のコードを読めるようになったぞ。

最後は、FreeBSDを止めた後

^C
Program received signal SIGINT, Interrupt.
strcmp (s1=0xc0b90a8e "dcons_poll", s2=0xc1db21b5 "rocess_exit")
    at ../../../libkern/strcmp.c:48
48              return (*(const unsigned char *)s1 - *(const unsigned char *)(s2 - 1));
(gdb) detach
Detaching from program: /home/sakae/7f/sys/i386/compile/GENERIC/kernel.debug, Remote target
Ending remote debugging.
(gdb) q
[sakae@arch GENERIC]$ popd
~/7f
[sakae@arch 7f]$

これで、終了。

Tips

boot -d とかでdebugしてる時、関数のアドレスは分かるけど、それが何処で定義されてるか 知りたくなる事が有る。そんな時は、下記が役に立つだろう。

[sakae@secd ~]$ addr2line -h
Usage: addr2line [option(s)] [addr(s)]
 Convert addresses into line number/file name pairs.
 If no addresses are specified on the command line, they will be read from stdin
 The options are:
  @<file>                Read options from <file>
  -a --addresses         Show addresses
  -b --target=<bfdname>  Set the binary file format
  -e --exe=<executable>  Set the input file name (default is a.out)
  -i --inlines           Unwind inlined functions
  -j --section=<name>    Read section-relative offsets instead of addresses
  -p --pretty-print      Make the output easier to read for humans
  -s --basenames         Strip directory names
  -f --functions         Show function names
  -C --demangle[=style]  Demangle function names
  -h --help              Display this information
  -v --version           Display the program's version

addr2line: supported targets: elf32-i386-freebsd elf32-i386 pei-i386 coff-i386 elf32-little elf32-big srec symbolsrec verilog tekhex binary ihex
Report bugs to <http://www.sourceware.org/bugzilla/>

一つ実例をば

[sakae@arch GENERIC]$ addr2line -e kernel.debug 0xc0b01590
/usr/src/sys/i386/compile/GENERIC/../../../i386/i386/machdep.c:2174
[sakae@arch GENERIC]$ addr2line -e kernel.debug -f 0xc0b01590
init386
/usr/src/sys/i386/compile/GENERIC/../../../i386/i386/machdep.c:2174

これ、gdbにも、組み込まれているんだろな。元はasとかと一緒に付いてくるみたいだけど。

gdb for qemu

ArchLinuxのqemuは確かpacmanと言うパッケージシステム経由で入れたやつ。残念ながらstrip されてて(Realなら大歓迎だけど)、gdbの俎上に上らない。ついでなんで、gdbにかけられる qemuを作ってみる。

./configure --prefix=/home/sakae/MINE \
   --target-list=i386-softmmu \
   --enable-debug-tcg \
   --enable-debug \
   --disable-strip

わざわざストリップ禁止を指示する所が変わってるな。これ特殊な人のためのオプションなんだな。 で、コンパイルを始めたら、最後の最後で、こんなエラーが出たよ。

  AR    i386-softmmu/libqemu.a
  LINK  i386-softmmu/qemu
/usr/bin/ld: vl.o: シンボル 'timer_settime@@GLIBC_2.2' への未定義参照です
/usr/bin/ld: 注: 'timer_settime@@GLIBC_2.2' は DSO /usr/lib/librt.so.1 内で定義 されているのでリンカのコマンドラインに追加してみてください
/usr/lib/librt.so.1: could not read symbols: 無効な操作です
collect2: エラー: ld はステータス 1 で終了しました
make[1]: *** [qemu] エラー 1
make: *** [subdir-i386-softmmu] エラー 2

どうしたらいいかちゃんと指示が出た。最近のGNUはclangを意識して、親切になったのかな。 指示通り、i386-softmmu/Makefile (これも指示してくれればいいのに、最初top-directoryの方を いじっちゃったぞ)にちと追加してあげます。

8:    LIBS=-lrt

これで、無事にコンパイル出来ました。さて、実行してみるか。

[sakae@arch xv6]$ gdb -q /home/sakae/MINE/bin/qemu
Reading symbols from /home/sakae/MINE/bin/qemu...done.

(gdb) set args -nographic -hdb fs.img xv6.img -m 384
(gdb) b main
Breakpoint 1 at 0x805447d: file /home/sakae/MINE/qemu-0.11.1/vl.c, line 4826.
(gdb) run
Starting program: /home/sakae/MINE/bin/qemu -nographic -hdb fs.img xv6.img -m 384
warning: Could not load shared library symbols for linux-gate.so.1.
Do you need "set solib-search-path" or "set sysroot"?
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib/libthread_db.so.1".

Breakpoint 1, main (argc=7, argv=0xbffff644, envp=0xbffff664)
    at /home/sakae/MINE/qemu-0.11.1/vl.c:4826
4826        const char *gdbstub_dev = NULL;
(gdb) l
4821        return buf;
4822    }
4823
4824    int main(int argc, char **argv, char **envp)
4825    {
4826        const char *gdbstub_dev = NULL;
4827        uint32_t boot_devices_bitmap = 0;
4828        int i;
4829        int snapshot, linux_boot, net_boot;
4830        const char *initrd_filename;
(gdb) c
Continuing.
xv6...
cpu0: starting
init: starting sh

ああ、動いてる。でも、こういう起動方法より、先にqemuを動かしておいて、後でgdbをアタッチ した方がいいかなあ。xv6/Makefieに QEMU = /home/sakae/MINE/bin/qemu って書いておけば、 gdb付きのqemuを使ってくれるしね。