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を使ってくれるしね。