trussでtrussす

黒にゃー本を通読した。

私の本の読み方は、まずは、後書きからだ。shiroさんが同じ言語族に興味を引いたって のは、すごくうなずける。訳してる時、これは趣味? それとも仕事?かと、自問自答 されたに違いない。

そして次に読むのは、付録の部分。emacsいいよ、はともかくとして、他の(普通の)editorや 環境を上げてあるのは、Java屋さん達をひきづり込むにはいいな。括弧恐怖を払拭できれば いいんだけど。。。

後は頭から、流して読んだ。REPLでの結果が文中に豊富に出てくるので、わざわざキーを 叩かなくてもいいかな、と。それじゃ理由にならんよ。。。理由は、寒かったから。 炬燵の中でぬくぬくしながら、読みました。

面白いと思った所は、p146のカリー化と部分適用、いつかきた道ですねぇ。そして、p150 の、再帰ふたたび、、shiroさんが苦笑いしながら訳されたに違いない。

何方かが

schemerは再帰ばか
CLerの副作用ばか

と言ってたのを覚えているけど、clojureはどれにもmatchしない新種だな。 ああ、怠け者になろうって、Haskellのあの人も言ってたから、Haskellの亜種ですかい?

Lisp族の始祖は、McCarthyさん、彼の精神を受け継いだHickeyさんが50年経った今、 Goslingを下請けにして、新しいパラダイムに 基づいた、clojureを誕生させたのは、時代の要請だったのだろうか。 何から何までつっこんであるCLよりずっとすっきりしてるな。必要ならば Javaの世界へ降りていけばいいよ、てのがいい。

そうそう、この構図いろいろな所でお目にかかりますね。Ruby族の始祖はmatzさん。 彼の精神を受け継いだあの人が、Goslingを下請けにしてJrubyを誕生させた。これは、 何から何までJavaじゃなきゃ駄目という業界からの熱い要請があったからだ。Jrubyで 足りない所はJavaの世界へ降りていけばいいよ、と。

でも、ガラスの天井がある事を忘れてはなるまい。その事を理解して使う分には、問題 ないんだろうな。

通読し終わって、実機のREPLで実行したのが、(がめ)ってのがちょいとあれだけど、 結構、りんごを食うのはむずかしいって事が分かりました。だから、遊ぶのはやめて コードをじっくり読んでみる事にしますだ。 Swing経由でもうGUIに悩まされるのはおさらばだ。どこでもGUIっていいね。おいらは GUIに興味は無いんでどうでもいいんですが。。

そして、秋葉原のあの人の所では、第6章 並行プログラミングあたりから、読み合わせ 開始ですかね。きっと6章だけで、もういいやとなってしまうんじゃないかと思いますが 、折角だから、第7章のマクロまで、突っ走っていただいて、マクロ信者になって頂き、 あの人を懐柔して欲しいものです。

あの人が解説してる、日経LinuxのGoの記事も読みましたよ。2年で名前が一気に知れ渡ったとうらやましがられて ましたね。FreeBSDのports/langまでも、押し寄せていますからね。これは本物になるので しょうか? 過去にいろいろな言語が出ては消えという運命を辿った例がたくさんありますから、 模様眺めでしょうか?

模様眺めと言えば、巷で話題になってる、ChromeOS。入れてみようかと思いましが、時間 がかかりそうなので、諦めました。chrootしてのクリーンルーム内培養じゃ、めんどくさすぎ。 その代わり、無理やり日本語化の記事を読んでいたら、ファイル名だけ英語用のやつを 使って、中身は日本語用にして、OSを騙すっていうHacking。おいらも昔よくやり ました。みんな考える事は一緒ねと、にんまりしてしまいましたよ。

失敗しましただ

前回、opensolarisでモールス発生器を書き直した。すんなり動いたと思われるかも 知れないが、ちょいと恥ずかしい失敗をしてたんだ。

モールス音に混じって、通奏低音が聞こえたよ。これはまあQRM(混信)かと思えば より実践的にいいかなと思ったんだけど。絶対に許せないのは、dotSPEEDを変えると SEGVしちゃう事があった事。鬼の子供が更に居て、顔を出したんだな。

sakae@solaris:~/work$ echo 310 | ./mosc
sakae@solaris:~/work$ export dotSPEED=150
sakae@solaris:~/work$ echo 310 | ./mosc
セグメント例外 (core dumped)

何とかすべぇ

脳内diffするまでもなく、ピンときましたが、楽しい? 鬼ごっこに参加してみる 事にしました。お遊びですから、gdbなんて言うのは無しの方向で行きます。 すると、traceぐらいかなあってなるんですが、調べてみたら straceが入っていました。 これって、Linux臭くねぇ。他に何かないの?

はるか昔、FreeBSDで trussを使った事があるな。思わず、FreeBSDでmanしちゃいまし たよ。そしたら

歴史
     truss コマンドは Sean Eric Fagan が FreeBSD 用に作成しました。 System V
     Release 4 や SunOS で利用可能な類似のコマンドをモデルにしました。

元祖は、Sunつう事が分かりました。よって、親しみを込めてtrussを使ってみます。

sakae@solaris:~/work$ echo 310 | truss ./mosc
execve("mosc", 0x08047910, 0x08047918)  argc = 1
mmap(0x00000000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0) = 0xD2BB0000
mmap(0x00000000, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANON, -1, 0) = 0xD2BA0000
memcntl(0xD2BBF000, 27724, MC_ADVISE, MADV_WILLNEED, 0, 0) = 0
mmap(0x00000000, 4096, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON, -1, 0) = 0xD2B90000
memcntl(0x08050000, 3280, MC_ADVISE, MADV_WILLNEED, 0, 0) = 0
resolvepath("/usr/lib/ld.so.1", "/lib/ld.so.1", 1023) = 12
getcwd("/export/home/sakae/work", 1018)         = 0
resolvepath("/export/home/sakae/work/mosc", "/export/home/sakae/work/mosc", 1023) = 28
stat64("/export/home/sakae/work/mosc", 0x080475B4) = 0
open("/var/ld/ld.config", O_RDONLY)             Err#2 ENOENT
sysconfig(_CONFIG_PAGESIZE)                     = 4096
stat64("/usr/ccs/lib/libc.so.1", 0x08046DB4)    Err#2 ENOENT
stat64("/lib/libc.so.1", 0x08046DB4)            = 0
resolvepath("/lib/libc.so.1", "/lib/libc.so.1", 1023) = 14
open("/lib/libc.so.1", O_RDONLY)                = 3
mmapobj(3, 0x00020000, 0xD2B904D0, 0x08046E20, 0x00000000) = 0
close(3)                                        = 0
memcntl(0xD2A40000, 188400, MC_ADVISE, MADV_WILLNEED, 0, 0) = 0
mmap(0x00010000, 24576, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANON|MAP_ALIGN, -1, 0) = 0xD2A30000
getcontext(0x08047414)
getrlimit(RLIMIT_STACK, 0x0804740C)             = 0
getpid()                                        = 1411 [1410]
lwp_private(0, 1, 0xD2A32A00)                   = 0x000001C3
setustack(0xD2A32A60)
sysi86(SI86FPSTART, 0xD2B8800C, 0x0000133F, 0x00001F80) = 0x00000001
brk(0x08061A28)                                 = 0
brk(0x08063A28)                                 = 0
brk(0x08063A28)                                 = 0
brk(0x08067A28)                                 = 0
    Incurred fault #6, FLTBOUNDS  %pc = 0xD2A73F6B
      siginfo: SIGSEGV SEGV_MAPERR addr=0x08068018
    Received signal #11, SIGSEGV [default]
      siginfo: SIGSEGV SEGV_MAPERR addr=0x08068018

brkって、領地を分けてもらうやつですね。4枚の畑をOSから借りてきてるんだな。 そこまでは良いとして、その後で落ちてる。これじゃ決定的証拠不足で、あの人 みたいに立件出来ないじゃない。何とかならんかね。truss君!

困った時のman頼みで、manしてみると、楽しいオプションを発見!

     -u [!]lib,...:[:][!]func,...

         ユーザーレベルの関数呼び出しを追跡します。lib,.. は、 動
         的なライブラリ名 (.so.n 接尾辞を除く) をコンマで区切った
         リストです。func,.. は、関数名をコンマで区切ったリストで
         す。どちらの場合でも、名前の表現にメタ文字 *、?、[] を使
         用できます。これらのメタ文字の指定は  sh(1) における指定
         と同じ意味を持ちますが、ファイルに対してではなくライブラ
         リ名または関数名に対して使用されることになります。ライブ
         ラ リまたは関数のリストを空にすると、デフォルトで * が使
         用され、ライブラリ内のすべてのライブラリまたは関数が追跡

SunOS 5.11             2004 年 8 月 30 日                       5

ユーザーコマンド                                         truss(1)

         さ れます。リストの先頭に ! を付けると、追跡から除外され
         るライブラリまたは関数の名前を指定したことになります。 1
         つのライブラリを除外すると、そのライブラリ内のすべての関
         数が除外されます。つまり、ライブラリ除外リストのあとに続
         く関数リストは無視されます。

         関数リストとライブラリリストを分離する 1 つの : は、ライ
         ブラリの外部から、それらのライブラリに対する呼び出しは追
         跡しますが、ライブラリ内部の他の関数からの呼び出しは除外
         することを意味します。2 つの :: は、呼び出し元に関係なく
         すべての呼び出しを追跡することを意味します。

         ライブラリのパターンには、正確な一致がないかぎり、実行可
         能 ファ イルと動的リンカーのいずれとも対応付けはしません
         (l* は ld.so.1 に対応付けられない)。これらのオブジェクト
         の どちらかに含まれる関数を追跡するには、       truss -u
         a.out -u ld  .. のように名前を明確に指定する必要がありま
         す。a.out はこの目的で使用されるリテラル名であり、実行可
         能ファイルの名前を意味するわけではありません。a.out 関数
         呼び出しを追跡すると、すべての呼び出しが暗黙に追跡されま
         す (デフォルトは ::)。

         -u オプションは複数回指定することが可能で、この場合左 か
         ら順に受け付けられます。プロセスが -lthread にリンクして
         いる場合は、呼び出しの追跡出力に関数呼び出しを行っ た ス
         レッドの ID が含められます。truss は、関数名を見つけるた
         めに各ライブラリ内の動的シンボルテーブルを検索するととも
         に、ストリップされてなければ標準のシンボルテーブルも検索
         します。

ちゃんと日本語になっている所を見ると、Sunのtrussが好きって人がいっぱい いるんでしょうね。ちなみに、Linuxからやって来たstraceは、人気が無い せいか、日本語にはなっていませんでした。

sakae@solaris:~/work$ ldd ./mosc
        libc.so.1 =>     /lib/libc.so.1
        libm.so.2 =>     /lib/libm.so.2
sakae@solaris:~/work$ echo 310 | truss -u libc ./mosc
   :
/1@1:   -> libc:memcpy(0x8067f98, 0x8061640, 0x20, 0xd2a9abe2)
/1@1:   <- libc:memcpy() = 0x8067f98
/1@1:   -> libc:memcpy(0x8067fd8, 0x8061640, 0x20, 0xd2a9abe2)
/1@1:   <- libc:memcpy() = 0x8067fd8
/1@1:   -> libc:memcpy(0x8068018, 0x8061640, 0x20, 0xd2a9abe2)
/1:         Incurred fault #6, FLTBOUNDS  %pc = 0xD2A73F6B
/1:           siginfo: SIGSEGV SEGV_MAPERR addr=0x08068018
/1:         Received signal #11, SIGSEGV [default]
/1:           siginfo: SIGSEGV SEGV_MAPERR addr=0x08068018

これを見ると、memcpyを呼んだ所で、領地侵犯が起きて、切腹を命じられています。 きっとOSは動かぬ証拠を掴んだので強権発動したんですね。memcpyと言う道具は あちこちで使われていて問題無いでしょうから、後は使い方の問題ですね。

第一引数で、新たに種を植え付ける場所を決めています。元種の大きさは 0x20 ですから、次に撒く場所は、0x20分隔たっていなければなりません。しかし ここでは、0x40分隔たっています。飛び飛びに種を撒いていったせいで、与えられた 領地をはみ出してしまったのです。

そうです。最初は、fillの中で、次に種を撒く場所を

     buf +=  sizeof(short) * SIZE;

にしてたのが原因でした。ここは、SIZE分増加させなければいけなかったのでした。 修正して走らせてみました。

sakae@solaris:~/work$ truss -u libc ./mosc
   :
/1@1:   -> libc:open(0x805135c, 0x2, 0x8047848, 0xd2a9b4c1)
/1:     open("/dev/audio", O_RDWR)                      = 3
/1@1:   <- libc:open() = 3
/1@1:   -> libc:ioctl(0x3, 0x40844101, 0x8047810, 0xd2a9b4c1)
/1:     ioctl(3, AUDIO_GETINFO, 0x08047810)             = 0
/1@1:   <- libc:ioctl() = 0
/1@1:   -> libc:ioctl(0x3, 0xc0844102, 0x8047810, 0xd2a9b4c1)
/1:     ioctl(3, AUDIO_SETINFO, 0x08047810)             = 0
/1@1:   <- libc:ioctl() = 0
/1@1:   -> libc:_filbuf(0x8061668, 0x8061640, 0x80478ec, 0x80511e9)
/1:     ioctl(0, TCGETA, 0x08047770)                    = 0
/1:     fstat64(0, 0x080476D0)                          = 0
/1:     read(0, 0xD2B88018, 1024)       (sleeping...)
310
/1:     read(0, " 3 1 0\n", 1024)                       = 4
/1@1:   <- libc:_filbuf() = 51
/1@1:   -> libc:write(0x3, 0x8061a30, 0x1c20, 0x80511e9)
/1:     write(3, "\0\0D3 .D5 V 1 rEC | v u".., 7200)    = 7200
/1@1:   <- libc:write() = 7200
/1@1:   -> libc:write(0x3, 0x8063658, 0x960, 0x80511e9)
/1:     write(3, "\0\0\0\0\0\0\0\0\0\0\0\0".., 2400)    = 2400
/1@1:   <- libc:write() = 2400
/1@1:   -> libc:write(0x3, 0x8061a30, 0x960, 0x80511e9)
/1:     write(3, "\0\0D3 .D5 V 1 rEC | v u".., 2400)    = 2400
/1@1:   <- libc:write() = 2400
/1@1:   -> libc:write(0x3, 0x8063658, 0x960, 0x80511e9)
/1:     write(3, "\0\0\0\0\0\0\0\0\0\0\0\0".., 2400)    = 2400
/1@1:   <- libc:write() = 2400
/1@1:   -> libc:close(0x3, 0x8063658, 0x960, 0x80511e9)
/1:     close(3)                        (sleeping...)
/1:     close(3)                                        = 0
/1@1:   <- libc:close() = 0
/1@1:   -> libc:free(0x8061a30, 0x8063658, 0x960, 0x80511e9)
/1@1:   <- libc:free() = 0
/1@1:   -> libc:free(0x8063658, 0x8063658, 0x960, 0x80511e9)
/1@1:   <- libc:free() = 0
/1@1:   -> libc:exit()
/1:     _exit(0)

いろいろ分かった面白いっす!

trussでtrussす

上でmemcpyの引数を凝視してる時、不思議な事に気が付いた。memcpyって、アリティーが 3の関数だよね。でも、第4引数が載ってるよ。こやつは一体何物ぞ? どうやって調べるかなあ? そんなのソース嫁は、一応却下ね。

ここは、schemerっぽく、再帰馬鹿になってみる。自分で自分を調べるんだな。 ログが大量に出そうなのでファイルに落とす事を指定し、親子共々追跡すればいいかな。

sakae@solaris:~/work$ truss -f -u '*' -o log.txt truss ./mosc

で、結果を見ると

1511/1@1:         <- libc:strcpy() = 0x8046c2b
1511/1@1:         -> libc:open(0x8046c20, 0x402, 0x8047438, 0xd29e9eed)
1512/1:                 *** process otherwise traced, releasing ...
1511/1@1:         <- libc:open() = -1
1511/1@1:         -> libc:strerror(0x5, 0xd2a1b000, 0x8047438)

肝心な部分がマスクされてたよ、orz。しょうがないのでgdbを取り出してみる。

Breakpoint 1, fill (buf=0x8061a30, seed=0x80615e0, n=3) at zcw.c:58
58        cnt = dot * n;
(gdb) n
59        while( cnt-- ){
(gdb) n
60           memcpy(buf, seed, sizeof(short) * SIZE);
(gdb) s
0xd2afaae8 in _ti_bind_guard () from /lib/libc.so.1
(gdb)
Single stepping until exit from function _ti_bind_guard,
which has no line number information.
0xd2afab89 in _ti_bind_clear () from /lib/libc.so.1
(gdb)
Single stepping until exit from function _ti_bind_clear,
which has no line number information.
0xd2a73f50 in memcpy () from /lib/libc.so.1
(gdb)
Single stepping until exit from function memcpy,
which has no line number information.
fill (buf=0x8061a30, seed=0x80615e0, n=3) at zcw.c:61
61           buf +=  SIZE;

表示されたアドレスが近いよね。と言う事は、libc.so.1がメモリーに載った時、memcpy の実行アドレスを示してるのかな?

もっと詳しく調べようとして、trussを見てたら、-Tオプションを使うと、一時的に 走らせているアプリを切り離していろいろ出来る(たとえばgdbにattach 出来るとか) みたい。頭の片隅に入れておこう。

pファミリィー

libcがメモリーのどのあたりにロードされてるか調べてみるかなと思って、/procへ 直行しました。それらしいのを catするも、相手はバイナリーみたいで歯が立ちません。

何かコマンドが用意されてるだろうと思って、man procを基点に探ってみました。 そしたら、 /usr/proc/binの中には、pファミリィーなコマンド群がひっそりと格納されてる事が判明。 どんなものか、ちょいと使ってみましたよ。

sakae@solaris:~$ pgrep mosc
1886
sakae@solaris:~$ pmap -x 1886
1886:   ./mosc
 Address  Kbytes     RSS    Anon  Locked Mode   Mapped File
08046000       8       8       8       - rwx--    [ stack ]
08050000       8       8       -       - r-x--  mosc
08061000       4       4       4       - rwx--  mosc
08062000      16      12      12       - rwx--    [ heap ]
D2A30000      24      12      12       - rwx--    [ anon ]
D2A40000    1280    1280       -       - r-x--  libc_hwcap1.so.1
D2B80000      28      28      28       - rwx--  libc_hwcap1.so.1
D2B87000       8       8       8       - rwx--  libc_hwcap1.so.1
D2B90000       4       4       4       - rwx--    [ anon ]
D2BA0000       4       4       4       - rw---    [ anon ]
D2BB0000       4       4       4       - rw---    [ anon ]
D2BBF000     180     180       -       - r-x--  ld.so.1
D2BFC000       8       8       8       - rwx--  ld.so.1
D2BFE000       4       4       4       - rwx--  ld.so.1
-------- ------- ------- ------- -------
total Kb    1580    1564      96       -

pmapはメモリー上の配置ね。stackに実行属性が付いているのは、ちょっと頂けませんなあ。libcと一口に言っても、opensolaris風味になってるっぽい。 下のやつは、親子関係だ。

sakae@solaris:/usr/proc/bin$ ptree 1886
409   /usr/lib/ssh/sshd
  1863  /usr/lib/ssh/sshd
    1864  /usr/lib/ssh/sshd
      1867  -bash
        1873  screen
          1874  screen
            1875  /bin/bash
              1886  ./mosc

きりがなにので、 Solaris用 と銘うったコマンド集があったので、載せておきます。これであなたもSolaris通。

それにしても、Solarisは個別コマンドを量産するのが好きでんなあ。ひょっとして、個別 コマンドを量産するのは、新人プログラマに課題を出して、それを採用する事で、 モチベーションを向上させるのが真のねらいだったりして。

最後にふと思い出したんだが、通奏低音のあの音は、こだわりの deadbeaf かな?