xv6
Table of Contents
xv6
前回は、ちょっとアセンブラが出てきた。引き続きやってみよう。ハロワでは つまらないので、教育的なOSにする。そう、いにしえのOSである、uv6(on PDP11)をx86(i386)に移植した、xv6って奴ね。
https://github.com/mit-pdos/xv6-public.git (for x86)
https://github.com/mit-pdos/xv6-riscv.git (for riscv)
探ると2種出てくる。x86は、各種のしきたりが面倒で、教師も生徒も疲弊して しまうので、素直な石RISC-Vに鞍替えしたよとな。実際、x86版のgit logを見 ると、
Date: Mon Aug 10 14:28:02 2020 -0400 Be more explicit that we are not maintaining the x86 version anymore Date: Tue Sep 24 14:45:16 2019 -0400 Mention we switched to xv6 on RISC-V
こんな記述が見られた。理由は大人の事情で書かれていないけど、そういう事 だ。
はじめてのOSコードリーディング for uv6 (book)
xv6: シンプルで Unix 風な教育用オペレーティングシステム (Russ Cox)
xv6 on OpenBSD
上記は万人に受け入れられるリナで動作させるのが常識なんだろうけど、それ では面白みがないので、あえてOpenBSDでやってみる。裏の理由は、リナ側に Diskの肥やしになりそうな、QEMUを持ち込みたくないから。予想される困難は、 コンパイラー系が、gccじゃなくてClang系になっている事。
ob$ gmake gcc -fno-pic -static -fno-builtin -fno-strict-aliasing -O2 -Wall -MD -ggdb -m32 -Werror -fno-omit-frame-pointer -fno-pic -O -nostdinc -I. -c bootmain.c gmake: gcc: No such file or directory gmake: *** [Makefile:104: bootblock] Error 127
CC = $(TOOLPREFIX)gcc となっている所をccに変更。
ld -m -N -e start -Ttext 0x7C00 -o bootblock.o bootasm.o bootmain.o ld: error: unknown emulation: -N gmake: *** [Makefile:106: bootblock] Error 1
次なるは、リンカーのエラーだ。それも -Nってスイッチを知らないと。man ldすると、
--omagic, -N Set the text and data sections to be readable and writable, do not page align sections, link against static libraries.
ちゃんと列挙されてるんだけどな。リナでコンパイルしてみっかな。
ld -m elf_i386 -N -e start -Ttext 0x7C00 -o bootblock.o bootasm.o bootmain.o ld: warning: bootasm.o:missing .note.GNU-stack section implies executable stackld: NOTE: This behaviour is deprecated and will be removed in a future version of the linker l d: warning: bootblock.o has a LOAD segment with RWX permissions
該当部分は、こうなってた。決定的に違うのは、-m elf_i386
っての。
OpenBSDでは、このスイッチの引数が欠落してるので、次の-Nを喰ってしまい、
消化不良になったんだろう。
-m value Set target emulation.
やっぱり、引数として、どんなマシンを対象にするんやってのが必要とな。該 当するMakefileには、
# FreeBSD ld wants ``elf_i386_fbsd'' LDFLAGS += -m $(shell $(LD) -V | grep elf_i386 2>/dev/null | head -n 1)
こんな注意書きと共に、抽出方法が説明されてた。我がOpenBSDは辺鄙なOS だから勿論ない。ググるで、OpenBSD ld なんてキーワード検索すると、 ld.bfd(1)なんてのも有るよと出てきた。(1)って、ユーザー用のコマンドじゃ ん。manも見ずに、いきなりコマンドを叩いてみる。
ob$ ld.bfd -V GNU ld version 2.17 Supported emulations: elf_i386_obsd
叩くのは、まず自分の頭からだな。ググるに頼らず、OpenBSDの事はOpenBSDに、 まず聞いてみるのが先だろう。聞くのは簡単。
ob$ ld ; TAB ld ld.lld ldapctl ldattach ldd ldpd ld.bfd ldap ldapd ldconfig ldpctl
ldで始まるコマンドを列挙させれば良い。あるいは、
ob$ ls /usr/bin/ld* /usr/bin/ld* /usr/bin/ld.lld* /usr/bin/ldd* /usr/bin/ld.bfd* /usr/bin/ldap*
先へ進む。次なるエラーは、なんじゃらホイ。冒険物語だな。
ld.bfd -m elf_i386_obsd -N -e start -Ttext 0x7C00 -o bootblock.o bootasm.o bootmain.o objdump -S bootblock.o > bootblock.asm objcopy -S -O binary -j .text bootblock.o bootblock ./sign.pl bootblock boot block too large: 598 bytes (max 510) gmake: *** [Makefile:109: bootblock] Error 1
Uum… このエラーは致命的だな。作成されたブート・ブロック昔言葉で言う MBRのサイズが510バイト以内になってるかの監査に引掛ったって事だ。
objdumpとかobjcopyにGNUの変種が登録されていないか、聞いてみる。それに は、locateコマンドが最適。一度、doas sh /etc/weekly すると、locate用の DBが作成される。
ob$ locate objdump | grep bin /usr/bin/llvm-objdump /usr/bin/objdump /usr/src/gnu/usr.bin/binutils-2.17/binutils/doc/objdump.1 :
locate自身でも検索ファイルの展開指定が可能だけど、エスケーブがドウタラ と面倒なので、上記の様に2段階の検索にしてる。
で、boot blockが肥大化しちゃうのは、Clangなコンパイラーの影響ではなか ろうかと見当を付ける。普通のgccが欲しいな。portsを探ってみると、lang/gcc に8系と11系の2種類が存在してた。古い方が馴染が有るだろうって、根拠が無 い推測、いや、xv6がリリースされた頃には、11系なんて無かったはず。 ちなみに、Debian/WSL2のgcc12系では、変な所でコンパイルに失敗すると言う、 互換性の無視を体験したからね。まったく、GNUの連中ときたら、、(以下自粛)
で、8系を入れようとしたら、既に鎮座してた。多分Rあたりを入れた時に紛れ こんだものだろう。ちょっとMakefileを修正。
ob$ gmake qemu-nox : objdump -S _zombie > zombie.asm BFD: Dwarf Error: mangled line number section. BFD: Dwarf Error: mangled line number section. BFD: Dwarf Error: mangled line number section. objdump -t _zombie | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$/d' > zombie.sym ./mkfs fs.img README _cat _echo _forktest _grep _init _kill _ln _ls _mkdir _rm _sh _stressfs _usertests _wc _zombie nmeta 59 (boot, super, log blocks 30 inode blocks 26, bitmap blocks 1) blocks 941 total 1000 assertion "fbn < MAXFILE" failed: file "mkfs.c", line 270, function "iappend" gmake: *** [Makefile:186: fs.img] Abort trap (core dumped) gmake: *** Deleting file 'fs.img'
なんだ、こりゃ? 多分、ファイルシステム内に、コマンド類が収まりきれな いで、監視機構が文句を垂れたんだろう。思いっりすがすがしいファイルシス テムを作成してみる。
ob$ ./mkfs fs.img README _cat _echo nmeta 59 (boot, super, log blocks 30 inode blocks 26, bitmap blocks 1) blocks 941 total 1000 balloc: first 143 blocks have been allocated balloc: write bitmap block at sector 58
思った通りだ。必要としないバイナリーを、取りあえず1個削除する。じゃ駄 目だったので3個削除。
参考までに、バイナリーのサイズを比較してみる。
ob$ ls -l _* -rwxr-xr-x 1 sakae wheel 19904 Feb 23 07:05 _cat* -rwxr-xr-x 1 sakae wheel 12307 Feb 23 07:05 _forktest* -rwxr-xr-x 1 sakae wheel 22724 Feb 23 07:05 _grep* -rwxr-xr-x 1 sakae wheel 19326 Feb 23 07:05 _init*
Debian側はスリムだなあ。readelfぐらいを利用して、肥満分析でもするか。 いや、先を急ごう。
sakae@atom:~/xv6-public$ ls -l _* -rwxr-xr-x 1 sakae sakae 15180 Feb 23 06:56 _cat* -rwxr-xr-x 1 sakae sakae 8644 Feb 23 06:56 _forktest* -rwxr-xr-x 1 sakae sakae 17968 Feb 23 06:56 _grep* -rwxr-xr-x 1 sakae sakae 14684 Feb 23 06:56 _init*
起動するか。
ob$ gmake qemu-nox which: qemu: Command not found. qemu-system-i386 -nographic -drive file=fs.img,index=1,media=disk,format=raw -d qemu-system-i386: cannot set up guest memory 'pc.ram': Cannot allocate memory gmake: *** [Makefile:231: qemu-nox] Error 1
デフォの512では駄目とな。
Booting from Hard Disk... failed to allocate memory for stack: Cannot allocate memory gmake: *** [Makefile:228: qemu-nox] Abort trap (core dumped)
qemuで動かしているOpenBSDは、512Mなメモリー割付でも、ちゃんと動作する なあ。この違いは何?
boot ?
Makefileのターゲットを見ていたら、メモリーが256で動くものが用意されて た。んで、それをパクってきて、下記のターゲットを追加。
go: xv6memfs.img qemu-system-i386 -m 384 -nographic -no-fd-bootchk \ -net nic -net user,hostfwd=tcp::2022-:22 \ -drive file=xv6memfs.img,index=0,media=disk,format=raw -smp 2
objdump -t kernelmemfs | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$/d' > kernelmemfm dd if=/dev/zero of=xv6memfs.img count=10000 10000+0 records in 10000+0 records out 5120000 bytes transferred in 0.061 secs (82958047 bytes/sec) dd if=bootblock of=xv6memfs.img conv=notrunc 1+0 records in 1+0 records out 512 bytes transferred in 0.000 secs (13728750 bytes/sec) dd if=kernelmemfs of=xv6memfs.img seek=1 conv=notrunc 1480+1 records in 1480+1 records out 757813 bytes transferred in 0.009 secs (84109139 bytes/sec) qemu-system-i386 -m 384 -nographic -no-fd-bootchk -s \ -net nic -net user,hostfwd=tcp::2022-:22 \ -drive file=xv6memfs.img,index=0,media=disk,format=raw -smp 2
こんな風に準備が進められて、qemuが起動した。
SeaBIOS (version rel-1.16.2-0-gea1b7a073390-prebuilt.qemu.org) iPXE (http://ipxe.org) 00:03.0 CA00 PCI2.10 PnP PMM+16FD11A0+16F311A0 CA00 Booting from Hard Disk... failed to allocate memory for stack: Cannot allocate memory gmake: *** [Makefile:292: go] Abort trap (core dumped)
これは、暴走してるな。 gmake go した後、gdbを起動してステップ実行してみる。
ob$ gdb -q kernel : The target architecture is assumed to be i8086 [f000:fff0] 0xffff0: ljmp $0x3630,$0xf000e05b 0x0000fff0 in ?? () (gdb) si [f000:e05b] 0xfe05b: cmpw $0xffc8,%cs:(%esi) [f000:e062] 0xfe062: jne 0xd241d09e [f000:e066] 0xfe066: xor %edx,%edx [f000:e068] 0xfe068: mov %edx,%ss [f000:e06a] 0xfe06a: mov $0x7000,%sp : [f000:ec39] 0xfec39: or $0x1,%cx [f000:ec3d] 0xfec3d: mov %ecx,%cr0 [f000:ec40] 0xfec40: ljmpw $0xf,$0xec48 The target architecture is assumed to be i386 => 0xfec48: mov $0x10,%ecx => 0xfec4d: mov %ecx,%ds => 0xfec4f: mov %ecx,%es => 0xfec51: mov %ecx,%ss => 0xfec53: mov %ecx,%fs => 0xfec55: mov %ecx,%gs => 0xfec57: jmp *%edx => 0xefc3f: push %ebx => 0xefc40: sub $0x20,%esp => 0xefc43: call 0xec41c => 0xec41c: push $0xf51a0 => 0xec421: push $0xf049f => 0xec426: call 0xec409 => 0xec40d: mov 0x4(%esp),%edx => 0xec411: mov $0xf5128,%eax => 0xec416: call 0xebb83 => 0xebb83: push %ebp => 0xebb84: push %edi => 0xebb85: push %esi => 0xebb86: push %ebx => 0xebb87: sub $0x10,%esp 0x000ebb87 in ?? () ;; ここで暴走した。そしてqemuはコアを吐いた。 Remote connection closed
もう、これぐらいにしよう。主目的は、アセンブラのコードを読む事だったか らね。
それはそうと、 xv6 (2018-01-29) とか、2013年の冬にもやってる。我ながが進歩が無いな。
FreeBSDの事情
一応リサーチしておくと、やはりgccが肝になる。gccでしか通過出来ないコー ドが書かれているからね。しかも特定なバージョン(以下)じゃないと駄目と言 う駄目っぷりです。portsにどんなのが提供されてるかと言うと、
sakae@fb:/usr/ports $ ls -d lang/gcc* lang/gcc/ lang/gcc12/ lang/gcc14-devel/ lang/gcc10/ lang/gcc12-devel/ lang/gcc48/ lang/gcc11/ lang/gcc13/ lang/gcc6-aux/ lang/gcc11-devel/ lang/gcc13-devel/ lang/gcc9/
gcc10以下は過去の遺物をハンドリングする、考古学者用。gcc11以降は、一応 現役って事かな。gcc14は人柱用の地雷原です。数多の犠牲を強いられて、安 全地帯になるんでしょう。そして、gcc48は、Clangが走り始めた時の、記念碑 です。世界遺産入り確定です。きっと、御老体が昔を懐しがって、保守されて るんでしょう。
gcc48を入れるのは忍びないので、clangでコンパイル出来る様にMakefileを改 変して、ユーザーランドを作成してみた(勿論カーネルはgccに依存してるので、 コンパイルは失敗する)。
sakae@fb:~/xv6-public $ gmake fs.img : cc -fno-pic -static -fno-builtin -fno-strict-aliasing -O2 -Wall -MD -ggdb -m32 -Werror -fno-omit-frame-pointer -fno-stack-protector -c -o grep.o grep.c ld -m elf_i386_fbsd -N -e main -Ttext 0 -o _grep grep.o ulib.o usys.o printf.o umalloc.o llvm-objdump -S _grep > grep.asm llvm-objdump -t _grep | sed '1,/SYMBOL TABLE/d; s/ .* / /; /^$/d' > grep.sym : ./mkfs fs.img README _cat _echo _forktest _grep _init _kill _ln _ls _mkdir _rm _sh _stressfs _usertests _wc _zombie nmeta 59 (boot, super, log blocks 30 inode blocks 26, bitmap blocks 1) blocks 941 total 1000 balloc: first 613 blocks have been allocated balloc: write bitmap block at sector 58
バイナリーが肥大化しないので、間引く必要は無かった。サイズは、こんなも んだ。
sakae@fb:~/xv6-public $ ls -l _* -rwxr-xr-x 1 sakae wheel 14188 Feb 24 08:22 _cat* -rwxr-xr-x 1 sakae wheel 8612 Feb 24 08:22 _forktest* -rwxr-xr-x 1 sakae wheel 16552 Feb 24 08:22 _grep*
Linuxの事情
まずは、ArchLinux
[sakae@arch ~]$ pacman -Ss gcc | grep '^[a-z]' core/gcc 13.2.1-5 [installed] extra/gcc12 12.3.0-3 extra/riscv64-linux-gnu-gcc 12.2.0-1 (risc-v)
そしてDebinan
sakae@atom:~$ apt-cache search gcc | grep '^gcc-[1-9]' gcc-11 - GNU C compiler gcc-12 - GNU C compiler gcc-12-riscv64-linux-gnu-base
総じて、過去には見向きもしない、行け行けドンドンというお調子者です。
diff Makefile
OpenBSD用です。
ob$ diff Makefile.org Makefile 74c74 < CC = $(TOOLPREFIX)gcc --- > CC = $(TOOLPREFIX)egcc 76c76 < LD = $(TOOLPREFIX)ld --- > LD = $(TOOLPREFIX)ld.bfd 160c160 < gcc -Werror -Wall -o mkfs mkfs.c --- > egcc -Werror -Wall -o mkfs mkfs.c 170d169 < _echo\ 175d173 < _ln\ 181d178 < _usertests\ 286a284,289 > > go: xv6memfs.img > qemu-system-i386 -m 384 -nographic -no-fd-bootchk -S $(QEMUGDB) \ > -net nic -net user,hostfwd=tcp::2022-:22 \ > -drive file=xv6memfs.img,index=0,media=disk,format=raw -smp 2 >