netbsd(3)

夏休みの課題図書じゃ無いけれど、岩波ジュニア新書から出ている『さわっておどろく!』って本を 手に取ってみた。副題は、点字・点図がひらく世界ってついてた。

想像出来ると思うけど、視的しょうがいの方がいかにコミュニケーションを取っていくかという 事について書かれている。

私は恥ずかしながらこの本によって、点字の世界を初めて知りましたよ。 明治の終わり頃には、先駆者の努力によって、日本語用の点字が確立されているんですね。 日本語用と言うからには、英語用の点字 も勿論有るわけで、他の言語用も世界で使われている。

身近な所にある点字としては、缶ビールとかの蓋に付いているあれですね。点字は、縦3個横2列 のドットの組み合わせで、1文字を表すきまりだ。6Bitだから、63種(ドットが1個も無いのは文字と みなさない)の文字を表現出来る。

ひらがなや濁音記号数字とかを含めると、63種では収まらないので、2字連結して、1字としてる とか、JIS文字で言う、切り替え符号があったりとか、何やらコンピュータ系の文字コードと そっくりになっている。ユニコードにも点字パターンが登録されてるしね。

ああ、コンピュータの文字コードは、点字に比べてずっと後輩になるんだね。そうすると、文字 コードは、点字からアイデアを頂いてきたのか。

3X2のドットの組み合わせと言うと、8進数2桁で表せるな。たとえば、あ、と言う点字は、040、 か、と言う点字は、041 と言った具合。

UNIXが最初に載った、PDP11も確か、8進数を使っていたな。od なんてコマンドは、歴史ある コマンドなんだな。何で最初は8進数かと想像したら、この頃のレジスタ数は8個だったから、8進数で 表すと収まりが良かった、なーんてね。あのeaxとか言うレジスタが有る石も、レジスタ数は8個 なんだよな。

歴史
     od コマンドは Version 1 AT&T UNIX から登場しました。

それが、いつの間にか、16進数が幅を利かせるようになっちゃったな。これは、1語長が32Bitとか になって、16進にすると圧縮率が高くなるって理由なんだな。 それとも、beefとかcafeとかdeadとかfaceとか、16進数で、意味有る単語を作りたかったから?

他に4文字16進数単語って有るのかな? ruby -pe 'p $_ if ...' とかの一行野郎を試して みようとしたが、期待通りに動かん。しょうがないので、replのお世話になってみる。 単語帖は、FreeBSDに備え付けの由緒あるやつを使いました。

irb(main):001:0> IO.foreach('/usr/share/dict/words') do |w|
irb(main):002:1*     puts w if w =~ /^[a-f]{4}$/
irb(main):003:1>   end
abac
abed
acca
adad
adda
affa
baba
babe
bade
baff
bead
beef
caba
cade
cede
dabb
dace
dada
dade
daff
dead
deaf
deed
ecad
edea
face
fade
faff
feed
=> nil

feedとかfadeとか、覚えておいて損はないか。

ああ、いつの間には脱線してるな。脱線ついでに、点字って読解スピードはどのぐらいなんで しょうか? 30wpmぐらいは出るんですかね? 聞く符合、モールス信号だと、そのぐらいは 判読出来るそうですよ。

ついでに、点字のドット間隔は、1mmぐらいが限界のようです。 (指センサーの分解能は個人差が大きいらしい。日頃から中華文化で盲牌で鍛えていると、それなり の分解能を達成出来るかなぁ)

点字フォントで検索してみたら、 墨点字フォントとか 点字フォント(Unicode6点) なんてのが出てきました。いろいろ有るんですね。

netbsd de kgdb

前回は、netbsdのsparc版で、カーネルにgdbを組み込もうとして、あえなく撃沈した。きっと、 今更sparcでごにょぼにょやろうなんて輩はいないんで、ちゃんとメンテナンスされていない んだろうね。ふと、エリック・レイモンドが言ってた事を思い出したぞ。目玉は多いほど良い。 だから、OSSなんだってね。

目玉が多いのはどんなプラットフォームだ? 考えるまでもなく、i386だよ。残念ながら。 kgdbを動かすって事は、下の層まで潜る必要性が無いって事だな。だったら、もう1000歩下がって (最初の1000歩は、sparc版でddb実現)、インテル系でnetbsd de kgdb で、いいじゃん。

早速i386cd-5.1.2.isoを落としてきて、VMWAREで、netbsdが入ったものを作りましたよ。 前週の案内では、リモートデバックをやるには、2台のnetbsd機が必要とな。そしてその間を シリアル回線で接続するそうな。

取りあえず1台作ったので、そこでgdbを動かす事にする。マシンの名前はremとした。 rem機で、gdbとddbを組み込んだ、カーネルを作る。そしたら、その機をそのまま、WIndows上で コピー。コピーしたマシンは名前をtagとでもしよう。そして、tag機の方で、カーネルを インストールする方針。 remは、リモートの略。tagはターゲットの略です。以下、rem機での作業。

   :
#    create  GENERIC/.gdbinit
rm -f .gdbinit
echo "source ../../../../gdbscripts/bdump" >> .gdbinit
echo "source ../../../../gdbscripts/cpus" >> .gdbinit
echo "source ../../../../gdbscripts/kdump" >> .gdbinit
echo "source ../../../../gdbscripts/lwps" >> .gdbinit
echo "source ../../../../gdbscripts/msgbuf" >> .gdbinit
echo "source ../../../../gdbscripts/pgrpdump" >> .gdbinit
echo "source ../../../../gdbscripts/procs" >> .gdbinit
echo "source ../../../../gdbscripts/vchain" >> .gdbinit
echo "source ../../../../gdbscripts/vdump" >> .gdbinit
#    create  vers.c
sh ../../../../conf/newvers.sh
#   compile  GENERIC/vers.o
cc  -ffreestanding -fno-zero-initialized-in-bss -g -O2 -std=gnu99 -fno-strict-aliasing   -Werror -Wall -Wno-main -Wno-format-zero-length -Wpointer-arith -Wmissing-prototypes -Wstrict-prototypes -Wswitch -Wshadow -Wcast-qual -Wwrite-strings -Wno-unreachable-code -Wno-sign-compare -Wno-pointer-sign -Wno-attributes -Wextra -Wno-unused-parameter  -Werror -march=i486 -mtune=pentiumpro  -Di386 -I. -I../../../../contrib/dev/ath/netbsd -I../../../../../common/include -I../../../../arch  -I../../../.. -nostdinc -DLKM -DMAXUSERS=32 -D_KERNEL -D_KERNEL_OPT -I../../../../lib/libkern/../../../common/lib/libc/quad -I../../../../lib/libkern/../../../common/lib/libc/string -I../../../../lib/libkern/../../../common/lib/libc/arch/i386/string   -I../../../../../common/include -I../../../../dist/ipf  -c vers.c

ほう、gdbが便利に使えるように、ドットファイルを作ってくれているのか。どんな便利機能が 入ってるか、後で確認しておこう。

#      link  GENERIC/netbsd
ld -Map netbsd.map --cref -T ../../../../arch/i386/conf/kern.ldscript -Ttext c0100000 -e start -X -o netbsd ${SYSTEM_OBJ} ${EXTRA_OBJ} vers.o
NetBSD 5.0.2 (SAKAE-$Revision: 1.915.2.3 $) #0: Sat Aug 25 13:26:15 JST 2012
   text    data     bss     dec     hex filename
9816851  423908  616532 10857291         a5ab4b netbsd
mv -f netbsd netbsd.gdb
strip -g -o netbsd netbsd.gdb
      635.47 real       224.92 user       325.65 sys

コンパイルの最後のフェーズでリンクが行われました。そして、gdbが使う情報が一杯詰まった カーネルの名前を、netbsd.gdbに改名。そのファイルから、gdb情報をそぎ落として、netbsdって 名前のファイルを作ってる。

10分強で、コンパイルしてるね。mips版のコンパイルに3時間弱かかった事を思えば、いかに シュミレータが遅いか良く分かると言うものだ。

このrem機をそのままVMWARE上のdir毎コピーして、vmwareの設定ファイルを編集。tag機を起動すると、 移動したんかコピーしたんか聞かれるので、コピーしたって答えてあげる。 後は、カーネルをインストール。

続いて、vmwareの設定で、シリアルを追加。パイプを選んで、rem機はサーバー(tag機はクライアント) 、相手はvmwareって設定しておく。

vmwareのシリアルポート は、アプリにもパイプを使って接続出来るみたいなので、何かの時に、役に立つかも知れないな。

これで準備万端整った。まずは、tag機を、boot -d して起動するんだな。そうすると、よそからの gdb接続待ちになるとな。

が、そんな風にはならずに、いきなり、fatal breakpoint とか言って、ddbに落ちちゃったよ。 なんで、そうなるの? ぐぐる先生に聞いても、知らんと言われるし。結局、ソース嫁。

kgb waitingを頼りに、ソースを漁ると、arch/i386/kgb_machdep.c に、kgb_connectっていう、 いかにもってのが見つかった。こやつは、何処から呼ばれる?

調べてみたら、arch/i386/machdep.cに有った。

#ifdef DDB
        if (boothowto & RB_KDB)
                Debugger();
#endif
#ifdef IPKDB
        ipkdb_init();
        if (boothowto & RB_KDB)
                ipkdb_connect(0);
#endif
#ifdef KGDB
        kgdb_port_init();
        if (boothowto & RB_KDB) {
                kgdb_debug_init = 1;
                kgdb_connect(1);
        }
#endif

        if (physmem < btoc(2 * 1024 * 1024)) {
                printf("warning: too little memory available; "
                       "have %lu bytes, want %lu bytes\n"
                       "running in degraded mode\n"
                       "press a key to confirm\n\n",
                       ptoa(physmem), 2*1024*1024UL);
                cngetc();
        }
}

これを見ると、DDBの方が優先される。しょうがないので、DDBのオプション無しでKGDBだけを イネーブルにして、コンパイルし直したよ。とほほ。関係ないけど、2M以下のメモリーだと やばいんか。

run gdb

紆余曲解の末、やっと走らせる事が出来た。

tag$ cat /boot.cfg
menu=Boot normally:boot netbsd
menu=Boot single user:boot netbsd -s
menu=Disable ACPI:boot netbsd -2
menu=For KGDB debugging:boot -d
menu=Drop to boot prompt:prompt
default=4
timeout=5

tag機は、boot時にbootメニューに一度落ちてから、boot -dしなきゃいけないんだけど、面倒 なので、boot.cfgを改変しといたよ。

waitingが出てきたら、rem機でrootになってから、次のようにする。rem機のシリアルもtty00を 使うようにした。

rem# gdb netbsd.gdb
GNU gdb 6.5
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386--netbsdelf"...
(gdb) set remotebreak 1
(gdb) set remotebaud 9600
(gdb) target remote /dev/tty00
Remote debugging using /dev/tty00
0xc057287c in breakpoint ()
(gdb) bt
#0  0xc057287c in breakpoint ()
#1  0xc0574bc7 in kgdb_connect (verbose=1)
    at ../../../../arch/i386/i386/kgdb_machdep.c:242
#2  0xc0576fba in init386 (first_avail=13225984)
    at ../../../../arch/i386/i386/machdep.c:1974
#3  0xc01002c2 in begin ()
(gdb) f 3
#3  0xc01002c2 in begin ()
(gdb) l
44      #if defined(__GNUC__) && !defined(_LKM)
45      static struct cpu_info *x86_curcpu(void);
46      static lwp_t *x86_curlwp(void);
47
48      __inline static struct cpu_info * __unused
49      x86_curcpu(void)
50      {
51              struct cpu_info *ci;
52
53              __asm volatile("movl %%fs:%1, %0" :
54                  "=r" (ci) :
55                  "m"
56                  (*(struct cpu_info * const *)offsetof(struct cpu_info, ci_self)));
57              return ci;
58      }
59
60      __inline static lwp_t * __attribute__ ((const))
61      x86_curlwp(void)
62      {
63              lwp_t *l;

macdep.cって、巨大なソースなんだよな。XENのコードも大分入っているし。。。Linuxとも 仲良くしたいんかな。博愛主義だなあ。リナスは、BSDの事を鼻にもひっかけない のにね。debianはRMSの博愛主義が効いてて、FreeBSDをカーネルにしたって良いよって態度。 親分の主張が、正義なんだなあ。

(gdb) b sys_execve
Breakpoint 1 at 0xc048920b: file ../../../../kern/kern_exec.c, line 465.
(gdb) c
Continuing.
Ignoring packet error, continuing...
Ignoring packet error, continuing...

ありゃら、接続が切れちゃったよ。何でかな? 考えても分からないので別な方向へ手を出して みる。 Remote Kernel Debugging via Qemu なんてのをFreeBSDでやってるお方が居た。NetBSDでも一緒でしょ。

NetBSDでもQemu

qemuのソースを取ってきて、一からコンパイルしてもいいんだけど、いろいろと前準備が 必要なので、pkgになってるのを使わせてもらいましょ。

rem# PKG_PATH="ftp://ftp5.jp.netbsd.org/pub/NetBSD/packages/5.1/i386/All/"
rem# export PKG_PATH
rem# pkg_add -f qemu-*

pkg_add )に、-fを付けているのは、無理やりインストールするぞって印、全ては事故責任だからね。 ついでに、sudoとscreenも入れておいた。

rem$ ls /usr/pkg/bin/qem*
/usr/pkg/bin/qemu                       /usr/pkg/bin/qemu-system-mips64
/usr/pkg/bin/qemu-ga                    /usr/pkg/bin/qemu-system-mips64el
/usr/pkg/bin/qemu-img                   /usr/pkg/bin/qemu-system-mipsel
/usr/pkg/bin/qemu-io                    /usr/pkg/bin/qemu-system-ppc
/usr/pkg/bin/qemu-nbd                   /usr/pkg/bin/qemu-system-ppc64
/usr/pkg/bin/qemu-system-arm            /usr/pkg/bin/qemu-system-ppcemb
/usr/pkg/bin/qemu-system-cris           /usr/pkg/bin/qemu-system-s390x
/usr/pkg/bin/qemu-system-lm32           /usr/pkg/bin/qemu-system-sh4
/usr/pkg/bin/qemu-system-m68k           /usr/pkg/bin/qemu-system-sh4eb
/usr/pkg/bin/qemu-system-microblaze     /usr/pkg/bin/qemu-system-sparc
/usr/pkg/bin/qemu-system-microblazeel   /usr/pkg/bin/qemu-system-sparc64
/usr/pkg/bin/qemu-system-mips           /usr/pkg/bin/qemu-system-x86_64

色々入りましたなあ。おまけでperlとかも付いてきたぞ。 そんじゃ、qemu環境にnetbsdを入れてみる。

rem$ qemu -m 128 -cdrom ./i386cd-5.1.2.iso -hda ./DISK.img  -nographic -boot d
Welcome to the NetBSD 5.1.2 installation CD
===============================================================================

ACPI should work on all modern and legacy hardware, however if you have
problems, please try disabling it.

If you encounter problems on hardware manufactured after 1998 with ACPI
enabled, please file a problem report including output from the 'dmesg'
command.

     1. Install NetBSD
     2. Install NetBSD (no ACPI)
     3. Install NetBSD (no ACPI, no SMP)
     4. Drop to boot prompt

Choose an option; RETURN for default; SPACE to stop countdown.
Option 1 will be chosen in 0 seconds.
booting cd0a:netbsd
10090076+518916+618576 [521184+509293]=0xbb2988
Loading /miniroot.kmod

最初、sgabios.bin が無いって言われて終了しちゃったよ。しょうがないのでLinux側からROMを 引っこ抜いてきて/usr/pkg/share/qemu内にインストールしたら、動き出した。けど、上記の メッセージのままで、うんともすんとも言わなくなっちゃった。

しょうがないので、Linux側で

qemu-system-i386 -m 128 -cdrom i386cd-5.1.2.iso -hda DISK.img -boot d -curses
qemu-system-i386 -m 128 -hda ./DISK.img -curses

して、作ってあげたよ。そして、debug用のカーネルもインストールしたDISK.imgを、netbsd側に輸入した。 こういう時って、OSを色々入れておくと便利だ。

qemuとkgdbのコラボ

rem$ qemu -s -m 64 -hda DISK.img -redir tcp:2222::22  >/dev/null 2>&1

どうやら、pkgから入れたqemuでは、stdioとのやり取りが旨くいかないようなんで、ネット 経由で、ターゲットマシンに入るのが吉。画面がチカチカして見苦しかったので、std{out,err}を 闇に葬る事もやってます。

rem$ ssh -p 2222 localhost
  :
mini$ dmesg|more
 :
total memory = 65136 KB
avail memory = 52072 KB
timecounter: Timecounters tick every 10.000 msec
timecounter: Timecounter "i8254" frequency 1193182 Hz quality 100
Bochs Bochs
mainbus0 (root)
cpu0 at mainbus0 apid 0: Intel 686-class, id 0x633
ioapic0 at mainbus0 apid 1: pa 0xfec00000, version 11, 24 pins
 :
mini$ df -h
Filesystem        Size       Used      Avail %Cap Mounted on
/dev/wd0a         868M       232M       592M  28% /
kernfs            1.0K       1.0K         0B 100% /kern
ptyfs             1.0K       1.0K         0B 100% /dev/pts
procfs            4.0K       4.0K         0B 100% /proc

カーネルとコンパイル環境だけだと、こんなもので 済んじゃうですねぇ。

本題に戻って、qemuの場合は、オプションに -sを渡す事により、kgdbの待ちに 入るようだ。こういう状態になったら、別端末から、gdbを起動する。

rem# gdb ./netbsd.gdb
GNU gdb 6.5
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "i386--netbsdelf"...
(gdb) target remote localhost:1234
Remote debugging using localhost:1234
[New thread 1]
0xc0572915 in x86_stihlt ()
(gdb) bt
#0  0xc0572915 in x86_stihlt ()
#1  0xc048b0f6 in idle_loop (dummy=0xc6007c80)
    at ../../../../kern/kern_idle.c:81
#2  0xc01002e1 in lwp_trampoline ()

ちゃんと止まってくれたね。でも、VMWAREと違って、止まる所が異なってる。

(gdb) b sys_execve
Breakpoint 1 at 0xc048920b: file ../../../../kern/kern_exec.c, line 465.
(gdb) c
Continuing.

Breakpoint 1, sys_execve (l=0xc70f6d40, uap=0xc710fd00, retval=0xc710fd28)
    at ../../../../kern/kern_exec.c:465
465     {
(gdb) l
460      * exec system call
461      */
462     /* ARGSUSED */
463     int
464     sys_execve(struct lwp *l, const struct sys_execve_args *uap, register_t *retval)
465     {
466             /* {
467                     syscallarg(const char *)        path;
468                     syscallarg(char * const *)      argp;
469                     syscallarg(char * const *)      envp;
(gdb) detach
Ending remote debugging.
(gdb) q
rem$

CTRL-Cも効くし、これは便利。

rem$ cat ./.gdbinit
source ../../../../gdbscripts/bdump
source ../../../../gdbscripts/cpus
source ../../../../gdbscripts/kdump
source ../../../../gdbscripts/lwps
source ../../../../gdbscripts/msgbuf
source ../../../../gdbscripts/pgrpdump
source ../../../../gdbscripts/procs
source ../../../../gdbscripts/vchain
source ../../../../gdbscripts/vdump
file netbsd.gdb
set remotebreak 1
target remote localhost:1234

更に、上記のような設定をしておくと、gdbだけで、debug?を始められるんで、ルンルン(死語) です。

おまけ

gdbを使って、カーネルにパッチを当てるなんて事、出来るのね。初めて知りましたよ。

gdb --write /netbsd
set rtc_offset=-540
quit