OpenBSD 探検

『貧困大国アメリカ』(岩波新書)なんてのを読んでみた。オバマが大統領になるか ならないかの時期に書かれたものと、その後編の2冊。

日本の未来はアメリカの今を見れば分かると言われているんで、この本を読んで 身震いしたよ。

日本が誇る医療保険。米は医療保険は民間企業が受け持っている。馬鹿高い。 かかる部位によって保険が違う。歯は高いから保険に入らなかったんだとか、 日本も歯は保険外の治療とか言い出すし。儲かるんでしょうなあ。コンビニ より歯医者の方が多いんで、客の取り合い。一度捕まえた客は逃さない。 歯医者から、定期的に歯のお掃除の時期が来ました。今なら、再診の期間内です から、お安くなりますってさ。

外資系の年金も、そのうちに大幅カット。安倍ちゃんは、株に投資して自分で 蓄えを増やしてくださいって言い出すに決まってる。だから、景気良くなったと 一生懸命に宣伝してる。これで儲かるのは誰かって考えると、裏で意図引く人が 透けて見えますなあ。

困った時の刑務所便り。でも、アメリカは違う。スリーストライク法とかが出来て 悪い事を3回繰り返すと、犯罪の種類によらず、無期懲役。こうして、無料の 労働力が手に入る。勿論、刑務所は小さな政府の標榜も元にとっくに民営化済み。

民営化されてるんで、刑務所に居る間にも、居住費を没収される。出所出来ても、 借金が付いてくる。払えなくて、結局堀の中に逆戻り。奴隷だね。 このビジネスの成長性にウォール街は注目。損をしない投資先だってさ。

食品のピラミッド。数社がアグリビジネスを牛耳る世界。日本にも招きたいと 言われて、楯になってる農協の力を削いだのは、何処の誰だっけ?

食品は武器。イラク復興で、小麦の種と雑草を枯らす農薬をセットで提供したのは モンサントとか言う会社らしい。遺伝子操作済みの種に、イラク伝統の麦は 太刀打ちできない。気付いた時には、占領完了。

親切心を装って無料提供してるけど、こういう裏が有った。オイラーが小供の 頃に提供されてた給食。米の国の日本の習慣を変えちゃえ。それには小供を 手なづけるのが良い。パンって美味しいでしょ。揚げパンなんて大好きだったな。 かくして、洋食文化が広まった。

恐いですねぇ。この他、マスコミと政治の美味しい関係とかが説明されてた。 よーく考えると恐いことばかりですよ。

探検準備

前回は、minix3を試してみた。あわよくば kernelの動いている所を観察してやろうという 密かな欲望が有ったんだけど、ソースやらその説明をみて、ちょっとうんざりした。

で、今使ってるOpenBSDでやろうと思う。基本は、昔やったqemuのdebuggerサポート機能を 使って、接続出来ればと思う。

何かまとまった資料が無いかと思って探してみると、 Debugging the OpenBSD kernel via QEMU ぴったりのものが、OpenBSDの刻印と共に見つかった。リリース毎にマスコットのアイコンが 新出してくるけど、盗作疑惑はないのでしょうか。最近は、 2ちゃんねる”特定班(スネーク・鬼女)”に聞く、画像「パクリ」「盗用」の暴き方 こういう人がいますんで、ヘタ打つと挙げられますよ。そんな事を知ってか知らずか、ちゃんと オリジナルのブログ を上げて、讃えているのは、素晴らしい。ネット住民は、こうでなくちゃ。

ウブの上にqemuを入れて、その中でゲストOSとしてOpenBSD(for debug kernel)を 動かせばいいんだな。

説明では、OpenBSDをインストールした後、カーネルのソースを入れて、デバッグ用カーネルを コンパイル、インストール、ってなってるけど、おいらの所のqemuはアクセラレータが効かない んで(なんせ、安物セルロンな石ですから)そんな事をすると日が暮れちゃう。

vmwareで動かしてるOpenBSDでコンパイルしたのを持ってくればいいじゃん。そういう方針で 行く。まずは、openBSDを入れる所から。

qemu-img create -f qcow2 disk  2G
qemu-system-i386 -m 256M -net nic -net user -cdrom install57.iso disk

Xmingを起動しておくと、勝手にOpenSDの画像版端末が上がってくる。 最小のインストールって事で、baseとcompだけを入れた。manは潔く無し。

sakae@uB:~/OpenBSD$ ls -lh disk
-rw-r--r-- 1 sakae sakae 342M  9月 14 05:48 disk

インストール後の仮想diskの容量。こんなもので済んじゃうって素敵。初回の起動時は、ssh用の キーを作成するんで、大分時間がかかった。

次は、本物のOpenBSD(vmwareの方)で、カーネルをコンパイルしとく。

obsd# cd /usr/src/sys/arch/i386/conf
obsd# cp GENERIC DEBUG
obsd# vi DEBUG
makeoptions DEBUG="-g"      ## <-- ADD this line
obsd# config DEBUG
obsd# cd ../compile/DEBUG
obsd# make depend
obsd# COPTS="-O0" make

ここまで行ったら、rsync等を使って、sys/ をウブに持ってくる。

sakae@uB:~/OpenBSD$ du -sh sys
217M    sys

大分大きいな、いらない物、たとえば

sakae@uB:~/OpenBSD/sys$ find . -type d -name amd64
./arch/amd64
./arch/amd64/amd64
./lib/libkern/arch/amd64

こういうのを削除しておこうか。それとも、

sakae@uB:~/OpenBSD/sys/arch/i386/compile$ du -sh DEBUG/
113M    DEBUG/

こちらが大きいか。この中で必要なのは、bsdとbsd.gdbだな。合わせて56Mって事は、約半分が ごみ(*.d, *.o)だ。Makefileを見ると

# The install target can be redefined by putting a
# install-kernel-${MACHINE_NAME} target into /etc/mk.conf
MACHINE_NAME!=  uname -n
install: install-kernel-${MACHINE_NAME}
.if !target(install-kernel-${MACHINE_NAME}})
install-kernel-${MACHINE_NAME}:
        cmp -s bsd /bsd || ln -f /bsd /obsd
        cp bsd /nbsd
        mv /nbsd /bsd
.endif

最後の工程は、出来たbsdを配置してるだけだ。上記を皆減量したら、128Mになった。 後はこれを、qemuのdiskに転送するとな。scpって -r を付けるとRecursively copy entire directories してくれるのか。 転送して配置してからmake installすればいいんだな。

scp -pr -P2022 sys localhost:/usr/src/sys 

まてよ、ゲストのOpenBSDに必要なのは、debug-buildしたカーネル(bsd)だけだろう。それだけを 転送してから、Makefileに倣って、配置すればいいんだ。コンパイルを失敗した事を想定して、 今動いているカーネルをobsdにrenameしてるんだ。

使い方

説明によれば

cd sys/arch/i386/compile/DEBUG
$ gdb bsd.gdb
(gdb) target remote :1234

こうして、待ちうけにしといて、起動するとな。ちがった。OpenBSDを起動しておいてから、 ウブ側で、gdbを立ち上げ、OpenBSDにアタッチする感じだ。

少し自動化しとく。DEBUG-dir内に、.gdbinitを作成。

sakae@uB:~/OpenBSD/sys/arch/i386/compile/DEBUG$ cat .gdbinit
target remote :1234

そして、これを有効化する為、HOME-dir直下の .gdbinitに

add-auto-load-safe-path /home/sakae/OpenBSD/sys/arch/i386/compile/DEBUG/.gdbinit

を追加。こうしておいて、DEBUG-dir内でemacsを起動。後は、M-x gdbして、

Run gdb (like this): gdb -i=mi bsd.gdb

内容が大きいので、暫く待たされるけど

Reading symbols from bsd.gdb...done.
0xd02008b3 in cpu_idle_cycle ()
(gdb) b sys_exit
Breakpoint 1 at 0xd0426302: file ../../../../kern/kern_exit.c, line 83.
(gdb) c
Continuing.

オイラーが知ってるシステムコールにBPを設定してから継続。OpenBSDのコンソールでunameとかの 直ぐに終了するコマンドを実行。コマンドの終了でbreakがかかった。

Breakpoint 1, sys_exit (p=0xd33e3440, v=0xf1de1f40, retval=0xf1de1f60) at ../../../../kern/kern_exit.c:83
83              struct sys_exit_args 
(gdb) bt
#0  sys_exit (p=0xd33e3440, v=0xf1de1f40, retval=0xf1de1f60) at ../../../../kern/kern_exit.c:83
#1  0xd067ae50 in mi_syscall (p=0xd33e3440, code=1, callp=0xd0d7ee2c <sysent+12>, argp=0xf1de1f40, retval=0xf1de1f60) at ../../../../sys/syscall_mi.h:84
#2  0xd067ac50 in syscall (frame=0xf1de1fa8) at ../../../../arch/i386/i386/trap.c:634
#3  0xd0200bcc in Xsyscall ()
#4  0xf1de1fa8 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)
(gdb) c
Continuing.

後は、煮るなり焼くなり誤自由に。

gdbを終了する時は、カーネルに割り込み(C-cを2度叩く) をかけて、gdbに制御を戻し、BPを解除、 それからdetachすればよい。


  C-c C-c
Program received signal SIGINT, Interrupt.
0xd02008b3 in cpu_idle_cycle ()
(gdb) del breakpoints
(gdb) detach
Detaching from program: /home/sakae/OpenBSD/sys/arch/i386/compile/DEBUG/bsd.gdb, Remote target
Ending remote debugging.
(gdb)

OpenBSDが起動してれば、いつでもアタック(アタッチ)出来るぞ。これは中々便利だな。

なお、この方法だとカーネルが定常状態になった時の動きしか把握出来ない。一番面白いと 思われる、bootからカーネルが立ち上がってくる過程は追えないんだ。 そういう時は、OpenBSD kernel hack memo を参考にさせて貰うとよい。

systemcallを洗い出す

システムコールしか、追い駆けるポイントが無いと言う素人なんで、何か参考になるものが 欲しい。あるコマンドを実行した時に、どんなシステムコールが発せられるか調べて おけばいいな。 そんなおあつらえなコマンドが有ったよ。

$ ktrace -tc uname -a
OpenBSD debug.my.domain 5.7 DEBUG#1 i386
$ kdump
   :
 31552 uname    CALL  write(1,0x81770000,0x29)
 31552 uname    RET   write 41/0x29
   :
 31552 uname    CALL  exit(0)

使い終わったら ktrace -Cしてトレースを中止しとく事。-tcは、システムコールのみトレース。 既に走っているプロセスのログも取れる。なお、kdumpは、ktraceが吐き出した、ktrace.outを デコード・表示してる。

$ ktrace -p 10244
$ ktrace -C
$ kdump
 10244 newlisp  CALL  mmap(0,0x1000,0x3<PROT_READ|PROT_WRITE>,0x1002<MAP_PRIVATE|MAP_ANON>,-1,0)
 10244 newlisp  RET   mmap -2109030400/0x824ac000
 10244 newlisp  CALL  write(1,0x7c2f1000,0x2f)
 10244 newlisp  GIO   fd 1 wrote 47 bytes
       "(439 268435456 411 1 0 2048 0 10244 10602 130)
   :

この他に、

$ systrace -A ./newlisp
newLISP v.10.6.2 32-bit on BSD IPv4/6 UTF-8, options: newlisp -h

>
$ less .systrace/home_sakae_newlisp
Policy: /home/sakae/newlisp, Emulation: native
        native-issetugid: permit
        native-mprotect: prot eq "PROT_READ" then permit
        native-getentropy: permit
         :
        native-fsread: filename eq "/home/sakae/.inputrc" then permit
        native-exit: permit

なんてのもあるけど、ちょっと邪道かな? newlispを起動した時に使われたシステムコールに 対して、アクセス許可が与えられる。(-Aオプション)

次回、そのポリシーでもって(-a)起動する。

$ systrace -ae ./newlisp
newLISP v.10.6.2 32-bit on BSD IPv4/6 UTF-8, options: newlisp -h

> (sys-info)
systrace: deny user: sakae, prog: /home/sakae/newlisp, pid: 24173(0)[0], policy: /home/sakae/newlisp, filters: 37, syscall: native-getpid(20), args: 0
(439 268435456 411 1 0 2048 0 -1 10602 130)

(sys-info)の呼び出しで、新しいシステムコールが使われたけど、許可リスト内に無いので エラーになった。(-eでstderrに表示)

後は、sys/sysの中に有る、syscall.hが参考になるぞ。

About man

qemu上で動くゲストOSには、当初 マニュアル(man)を入れてなかった。manする事が有ったら 本当のOpenBSDを起動すれば済むと踏んでいたから。 でも、面倒なんだよな。上のコマンドを探し当て、引数を調べてる時に、つくづく感じたよ。 だったら、manを追加で入れちゃえ。

圧縮したmanは約9M。展開すると10倍ぐらいに膨れるかと思いながら、転送して / の所で 展開。ついでに、sh /etc/weekly して、man.dbを更新。

OpenBSDのコンソールは表示が遅くて、man するには不向きなので、

sakae@uB:~$ ssh -p2022 localhost

で、OpenBSDを引き寄せてから見てる。

$ du -sh /usr/share/man
37.8M   /usr/share/man

容量よくまとまってるな。man群に man3p なんてのが混じっていた。パアルのドキュメントが OpenBSDでは公式なのね。知らなかったぞ。

BSD系はLinuxと違って、manのような文書を非常に大切に扱っている。Linuxは餓鬼が気分の 趣くままにコードを書き散らしたもので出来上がっているけど、BSDはドキュメントも 成果の一部ですって態度。その一端が、manに如実に現れているね。

man 2 intro
man 3 intro

なんてすると違いが一目瞭然。man(2)はエラー番号の例示から始まって、言葉の定義が きちんと行われている。man(3)の方は、簡潔なライブラリーの紹介。オイラーが興味を 引いたのは、

     libkvm (-lkvm)
             Kernel memory interface library.  Provides a uniform interface
             for accessing kernel virtual memory images, including live
             systems and crash dumps.  See kvm(3).

これってひょっとして、psの時のデータ仕入れ先? 後で調べてミレーー。 kvmもオープンしてからr/wしろとな。/dev/kvmなんで、抽象化されてるんだな。

$ man kvm_read
KVM_READ(3)                Library Functions Manual                KVM_READ(3)

NAME
     kvm_read, kvm_write - read or write kernel virtual memory

SYNOPSIS
     #include <kvm.h>

     ssize_t
     kvm_read(kvm_t *kd, u_long addr, void *buf, size_t nbytes);

     ssize_t
     kvm_write(kvm_t *kd, u_long addr, const void *buf, size_t nbytes);
      :

これはいいんだけど、使う時にはどのライブラリーをリンクしたら良いか、念力で推測しろ ってのが、ちと残念。確かNetBSDのマニュアルには、リンクすべきライブラリー名も提示 されてたはず。今度確かめてみよう。

streem

streem

golang で streem を実装した

Vim script で streem を実装した

seq(100) | {x ->
  if x % 15 == 0 {
    "FizzBuzz"
  }
  else if x % 3 == 0 {
    "Fizz"
  }
  else if x % 5 == 0 {
    "Buzz"
  }
  else {
    x
  }
} | stdout

気持ち分かるよ。