re-boot (crunchgenの事)

『情報便利屋の日記 専門図書館への誘い』なんてのを読んでみた。

誘いってなってるから、専門図書館へどうぞいらしてくださいかと思うと、この専門図書館 ってのは、法的な保護は何もないそうな。街にある図書館とか学校にある図書館は、 図書館設置法とかで、規制と保護があるそうな。対して、専門図書館はそういう法は 全く無く、野放し。勝手に作って勝手に潰れろ、だそうだ。

漫画図書館から始まって、企業が業務を遂行する為にライブラリーを併設するとか、 勝手にやって宜しい。だから、図書の分類も標準に従う必要は無く、使いやすいように アレンジするとか。

そりゃそうだ。図書の分類で言うと332、社会の経済に相当する分野だけを深く集めた のが専門図書館になるんで、分類したら、みんな332になっちゃって、役に立たない。 そんな事で、目的に沿って、色々な角度から分類するそうな。

著者は、経団連に付属する専門図書館に長年勤めた方。その方の長年にわたる日記を 選りすぐって本にし、後輩、同業他社に役立ててもらう事を願い出版された。

資料の分類と言うと、NHKの音楽ライブラリーが凄いと絶賛されてた。番組で使う 音楽をライブラリーし、どんな要求にも即座に答えられるよう分類に工夫をしてるとか。

例えば、作曲家名は、オリジナル名に徹する。クラシックな有名作曲家にバッハさんが いる。これをバッハですませてしまうと、使い物にならないインデックスになっちゃう とか。必ず、原名で登録するそうだ。気が長くなる話だなあ。でも、地道な用意が 後で利いてくるのね。

オイラーの専門図書館はは、OpenBSD一式にsrc.tar.gzとsys.tar.gzを組み込んだ ものだな。

動く実物と説明書と設計図と工作室が一緒になってて、どこかのリナ某みたいに うろうろと狼狽える事が全く不要。コンパクトなOSライブラリーでんな。

mtree

普通なら、以前やった boot の続き記事を書く時、boot(2)とかするんだけど、今回は、re-bootとしてみた。re-は、再びって意味が有るからね。

miniroot.fsを作るレシピ(Makefileの事ね)を、ざっとなぞったけど、正直に消化不良と 告白しておこう。

分からなかった所は色々あるけど、特に??なのは、mtreeとは、難題って事。

MTREE(8)を見て、英語を克服したはずなんだけど、ピンとこない。

最後の方に書かれている使用例に、トロイの木馬対策で開発されたとからしい。コマンドに 歴史有り。こういうちょっとしたいきさつが載ってると、つっけんどんに説明されるより 親しみが増すな。

実際は、どんな風に使われるの? ちょいと実例を探ってみた。

otsune's FreeBSD memo :: thin jailの作り方

FreeBSD を read-only で起動

まてまて、いきなりmtreeと言うより、前々回のMakefileの実行ログを眺めて、順序立てて追って いくのが筋だろう。

本当はMakeの実行ログなんて言わないで、いきなりMakefileを解析するのがハッカーだろうけど、オイラーはそんな芸を出来ません。

では、行きます。ログをじっと眺める。

crunchgen

実際のログを少し見やすいように、長いpath名を省略してみる。

awk -f makeconf.awk CBIN=instbin list list.local > instbin.conf
crunchgen -E -D /usr/src -L /usr/lib  -c instbin.c -e instbin -m instbin.mk inst
bin.conf
make -f instbin.mk SRCLIBDIR=/usr/src/distrib/amd64/ramdisk_cd/../../../lib all

ふむ、何やらawkのスクリプトを走らせて、instbin.confというファイルを生成してるな。 そして、それを引数にして、オイラーの知らないコマンド、crunchgenってのを起動してる。 次はinstbin.mkってMakefileを使って makeか。すると、鍵は crunchgenだな。

説明書が有る事を期待してman。

NAME
     crunchgen – generates build environment for a crunched binary

crunchedの意味を引いたけど、かみ砕くとか意味不。それを無視すると、ビルド環境を 作るっては、そんなものなの。確かに、instbin.mkは初出なんで、ここに結果が収まって、 次のmakeに繋げると思われ。C語で言う、ポインター渡しで、結果をそこに入れて貰う 作りだな。それの元ネタがinstbin.confか。

折角manを開いているので、用例が無いか精査してみた。

EXAMPLES
     Here is an example crunchgen input conf file, named kcopy.conf:

           srcdirs /usr/src/bin /usr/src/sbin

           progs test cp echo sh fsck halt init mount umount myinstall
           ln test [       # test can be invoked via [
           ln sh -sh       # init invokes the shell with "-sh" in argv[0]

           special myprog objpaths /homes/leroy/src/myinstall.o # no sources

           libs -lutil -lcrypt

     This conf file specifies a small crunched binary consisting of some basic
     system utilities plus a home-grown install program “myinstall”, for which
     no source directory is specified, but its object file is specified
     directly with the special line.

     The crunched binary “kcopy” can be built as follows:

           % crunchgen -m Makefile kcopy.conf    # gen Makefile and kcopy.c
           % make objs             # build the component programs' .o files
           % make                  # build the crunched binary kcopy
           % kcopy sh              # test that this invokes a sh shell
           $                       # it works!

     At this point the binary “kcopy” can be copied onto an install floppy and
     hard-linked to the names of the component programs.

どうやら、複数のコマンドをひとまとまりにするためのアプリ作成用Makefileを作るようだ。 何処かに、この使用例が無いか聞いてみた。

Crunched Binary作成編

なるほど、想像通りだな。ようするにランチャーか。こんなの最近よく見かけるね。 git init, git crone, git pull とか、 go build, go fmt とか。

オイラーも試してみるか。ひとまとまりにIPv6関係のコマンドでも 集めてみるか。名前はIPv6でいいか。短い名前 v6 か。それじゃ某芸能界のあの人達と 間違われる。どうせ間違われるなら、今年いっぱいのsmap。いかん、いかん。スイス アーミー ナイフの頭文字を取って、sak にするか。

で、そこに詰め込む物は? itojunさん作の、ping6、ndp、それに nc と 簡単なやつで echo ぐらいでいいだろう。

[ob: ~]$ which ping6 ndp nc echo
/sbin/ping6
/usr/sbin/ndp
/usr/bin/nc
/bin/echo

まずは、所在地の確認。次は、内蔵物の検査。

[ob: ~]$ which ping6 ndp nc echo | xargs -n 1 ldd
/sbin/ping6:
        Start            End              Type Open Ref GrpRef Name
        0000108f12501000 0000108f1295d000 dlib 1    0   0      /sbin/ping6
/usr/sbin/ndp:
        Start            End              Type Open Ref GrpRef Name
        00001de7da900000 00001de7dad06000 exe  1    0   0      /usr/sbin/ndp
        00001dea5e188000 00001dea5e652000 rlib 0    1   0      /usr/lib/libc.so.88.0
        00001dea18b00000 00001dea18b00000 rtld 0    1   0      /usr/libexec/ld.so
/usr/bin/nc:
        Start            End              Type Open Ref GrpRef Name
        00000d2c52a00000 00000d2c52e8a000 exe  1    0   0      /usr/bin/nc
        00000d2e5d701000 00000d2e5db0a000 rlib 0    1   0      /usr/lib/libtls.so.11.0
        00000d2e547c4000 00000d2e54c1e000 rlib 0    2   0      /usr/lib/libssl.so.39.0
        00000d2f20379000 00000d2f20948000 rlib 0    3   0      /usr/lib/libcrypto.so.38.0
        00000d2f3f761000 00000d2f3fc2b000 rlib 0    1   0      /usr/lib/libc.so.88.0
        00000d2f0f500000 00000d2f0f500000 rtld 0    1   0      /usr/libexec/ld.so
/bin/echo:
        Start            End              Type Open Ref GrpRef Name
        0000195af8461000 0000195af8880000 dlib 1    0   0      /bin/echo

これをconfにまとめ上げる。そして、Makefileを作る。

[ob: sak]$ cat sak.conf
srcdirs  /usr/src/sbin /usr/src/usr.sbin /usr/src/usr.bin /usr/src/bin
progs    ping6 ndp nc echo
libs     -ltls -lssl -lcrypto
[ob: sak]$ crunchgen -m Makefile sak.conf
[ob: sak]$ ls
Makefile   sak.c      sak.cache  sak.conf

どうやらMakefileとまとめのCファイルと、細かい手順のキャッシュが出来上がったようだ。 例に倣ってコンパイルしてみる。

[ob: sak]$ make objs
cd /usr/src/sbin/ping6 && exec make -f Makefile ping6.o
cc -O2 -pipe  -Wall -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wshadow -Wpointer-arith -Wcast-qual -Werror-implicit-function-declaration  -c ping6.c
Assembler messages:
FATAL: can't create ping6.o: Permission denied
*** Error 2 in /usr/src/sbin/ping6 (<sys.mk>:87 'ping6.o')
*** Error 1 in /home/sakae/sak (Makefile:55 'ping6_make')

ふうむ、ソースの有る所へ移動して、そこにあるMakefileを使ってコンパイルしたいとな。 現地に結果が残るので、書き込み権限が無いとだめなのね。

[ob: sak]$ sudo make objs
cd /usr/src/sbin/ping6 && exec make -f Makefile ping6.o
cc -O2 -pipe  -Wall -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations -Wshadow -Wpointer-arith -Wcast-qual -Werror-implicit-function-declaration  -c ping6.c
cd /usr/src/usr.sbin/ndp && exec make -f Makefile ndp.o gmt2local.o
cc -O2 -pipe  -Werror-implicit-function-declaration  -c ndp.c
cc -O2 -pipe  -Werror-implicit-function-declaration  -c gmt2local.c
cd /usr/src/usr.bin/nc && exec make -f Makefile netcat.o atomicio.o socks.o
cc -O2 -pipe  -Werror-implicit-function-declaration  -c netcat.c
cc -O2 -pipe  -Werror-implicit-function-declaration  -c atomicio.c
cc -O2 -pipe  -Werror-implicit-function-declaration  -c socks.c
cd /usr/src/bin/echo && exec make -f Makefile echo.o
cc -O2 -pipe  -Werror-implicit-function-declaration  -c echo.c
[ob: sak]$ make
cc -O2 -pipe  -fno-pie  -c sak.c
cc -O2 -pipe  -fno-pie  -c ping6_stub.c
ld -dc -r  -nopie -o ping6.lo ping6_stub.o /usr/src/sbin/ping6/ping6.o
crunchgen -h  -k __crunched_ping6_stub ping6.lo
cc -O2 -pipe  -fno-pie  -c ndp_stub.c
ld -dc -r  -nopie -o ndp.lo ndp_stub.o /usr/src/usr.sbin/ndp/ndp.o /usr/src/usr.sbin/ndp/gmt2local.o
crunchgen -h  -k __crunched_ndp_stub ndp.lo
cc -O2 -pipe  -fno-pie  -c nc_stub.c
ld -dc -r  -nopie -o nc.lo nc_stub.o /usr/src/usr.bin/nc/netcat.o /usr/src/usr.bin/nc/atomicio.o /usr/src/usr.bin/nc/socks.o
crunchgen -h  -k __crunched_nc_stub nc.lo
cc -O2 -pipe  -fno-pie  -c echo_stub.c
ld -dc -r  -nopie -o echo.lo echo_stub.o /usr/src/bin/echo/echo.o
crunchgen -h  -k __crunched_echo_stub echo.lo
cc -static -L.  -nopie -o sak sak.o ping6.lo ndp.lo nc.lo echo.lo -L/usr/lib -ltls -lssl -lcrypto
nc.lo: In function `main':
socks.c:(.text+0x3369): warning: warning: mktemp() possibly used unsafely; consider using mkstemp()
sak.o:(.data+0x8): undefined reference to `_crunched_ping6_stub'
sak.o:(.data+0x18): undefined reference to `_crunched_ndp_stub'
sak.o:(.data+0x28): undefined reference to `_crunched_nc_stub'
sak.o:(.data+0x38): undefined reference to `_crunched_echo_stub'
ping6.lo: In function `summary':
ping6.c:(.text+0x1076): undefined reference to `fmax'
collect2: ld returned 1 exit status
*** Error 1 in /home/sakae/sak (Makefile:22 'sak')

このエラーを見ると、リンクの問題が大きく分けて2種類あるな。一つは、_crunched_XX_stub が解決出来ないという共通性のあるエラー。もう一つはping6に固有の問題。

まずは、共通性のある方。その前に原理を探っておくか。簡単そうなechoを例にする。

[ob: sak]$ cat echo_stub.c
int _crunched_echo_stub(int argc, char **argv, char **envp) { return main(argc, argv, envp); }

自動生成されたスタブ(Makefile中に埋め込まれている)。mainを呼ぶ関数が定義されてる。このmainは、echo.loを作る時に echoのmainと合体と言うか一致が取られる。そして、sakを作る時に、echo.lo等を合体。

ld -dc -r  -nopie -o echo.lo echo_stub.o /usr/src/bin/echo/echo.o

sak.cは、

struct stub entry_points[] = {
        { "ping6", _crunched_ping6_stub },
        { "ndp", _crunched_ndp_stub },
        { "nc", _crunched_nc_stub },
        { "echo", _crunched_echo_stub },
        { EXECNAME, crunched_main },
        { NULL, NULL }
};

こんなのが定義されてて、sakの第一引数が echo だったら、_crunched_echo_stub を 介して、本物のechoのmainが呼び出されるとな。原理は合ってて、どこにも矛盾は無いなあ。 取り合えず、pending。

もう一つのping6問題。srcdirへ移動して、単独アプリを作ってみると

cc  -static -pie -o ping6 ping6.o -lm

mathライブラリィーをリンクしてるよ。lddで確かめた時は列挙されないと言う罠かな。 sak.conf のlibsに -lmを追加してからやり直したら、2番目のエラーは消えた。 今日の教訓、lddは信用するな。

残るは、1番目の問題か。ふと疑問が出てきた。今は、4つの単独アプリをまとめて、 ランチャーのsakにしようとしてる。単独アプリはそれぞれ main を持ってる。 更に言えば、単独アプリ間で、同名の関数やグローバル変数だって現れる可能性が 十分にある。

こんな状況でリンカーが混乱しないはずはない。きっと、アプリそれぞれは独立した 扱いをしてるんだろう。そして、ランチャーからは、_crunched_xxx_stubしか見えないし、 それぞれのアプリは、_crunched_xxx_stubしか提供しない。

そんな観点で、実行ログを眺めると、それぞれのアプリでこんなのが実行されてる。

crunchgen -h  -k __crunched_echo_stub echo.lo

これって、出来上がったアプリで、シンボルは隠せ(-h)、だけど、__crunched_xx_stub だけ は、残す(-k)って指示。このシンボルは、ダブルスコアーじゃなくて、シングルスコアのはず。

でも、ご丁寧に、マニュアルの該当部分では、

     symbols to keep visible, one symbol per line.  Note that the C compiler
     prepends an underscore in front of symbols, so to keep the C function
     “foo” visible, the option “-k _foo” must be used.

こうなったら、信じられるのは自分だけ。Makefileのそれぞれのダブルスコアーをシングルの アンダースコアに変更。そして再コンパイルしてみる。

[ob: sak]$ rm *.lo
[ob: sak]$ make
ld -dc -r  -nopie -o ping6.lo ping6_stub.o /usr/src/sbin/ping6/ping6.o
crunchgen -h  -k _crunched_ping6_stub ping6.lo
ld -dc -r  -nopie -o ndp.lo ndp_stub.o /usr/src/usr.sbin/ndp/ndp.o /usr/src/usr.sbin/ndp/gmt2local.o
crunchgen -h  -k _crunched_ndp_stub ndp.lo
ld -dc -r  -nopie -o nc.lo nc_stub.o /usr/src/usr.bin/nc/netcat.o /usr/src/usr.bin/nc/atomicio.o /usr/src/usr.bin/nc/socks.o
crunchgen -h  -k _crunched_nc_stub nc.lo
ld -dc -r  -nopie -o echo.lo echo_stub.o /usr/src/bin/echo/echo.o
crunchgen -h  -k _crunched_echo_stub echo.lo
cc -static -L.  -nopie -o sak sak.o ping6.lo ndp.lo nc.lo echo.lo -L/usr/lib -ltls -lssl -lcrypto -lm
nc.lo: In function `main':
socks.c:(.text+0x3369): warning: warning: mktemp() possibly used unsafely; consider using mkstemp()
strip sak

どうやら、コンパイルに成功した。実際に試してみる。

[ob: sak]$ ./sak
Usage: sak <prog> <args> ..., where <prog> is one of:
 ping6 ndp nc echo sak
[ob: sak]$ sak echo hello world
hello world
[ob: sak]$ sak ndp -a
Neighbor                             Linklayer Address  Netif Expire    S Flags
fe80::20c:29ff:fe11:2233%em0         00:0c:29:11:22:33    em0 permanent R l

アプリが使う全てのライブラリィーは、ランチャーの中に格納され、アプリが共有して使う。 よって、ランチャーは、一本の大きなファイル。これだけ有れば、何処でも格納されてる アプリが動く。これって、インストーラーみたいな不自由な環境では便利な機能。

FreeBSDでは、どうよ

ライバルのFreeBSDでも確認してみた。作ったのは、ndpとechoを内蔵したやつ。

 :
echo "int _crunched_echo_stub(int argc, char **argv, char **envp){return main(argc,argv,envp);}" >echo_stub.c
cc -O2 -pipe -c echo_stub.c -o echo_stub.o
echo_stub.c:1:68: warning: implicit declaration of function 'main' is invalid in
      C99 [-Wimplicit-function-declaration]
int _crunched_echo_stub(int argc, char **argv, char **envp){return main(argc...
                                                                   ^
1 warning generated.
cc -nostdlib -Wl,-dc -r -o echo.lo echo_stub.o /usr/src/bin/echo/echo.o
crunchide -k _crunched_echo_stub echo.lo
cc -static -o sak sak.o ndp.lo echo.lo -lc -lm
strip sak

こちらは、シンボルを隠す専用コマンド、cruncideが用意されてた。ダブルスコアー問題も ちゃんと解決していたぞ。

そして親切な事にMakefileに、subcleanと言うターゲットが有った。これを実行すると、 ソース領域に作られた、コンパイルの残骸を削除してくれる。これ便利だな。

頑張れ、OpenBSD!!

/rescue

この間のNHKの先取りとかいう番組で、古民家の廃材をレスキューする なんてのをやってた。 レスキューって救出って事だから、プチ違和感を感じつつ、なんとなく納得。

unixを使っていて、diskの一部が壊れた。そんな時に大事なファイルを救出したいって事が あるそうな。(オイラーは、大事なデータは電子媒体には保存しない主義なんで、関係ないけど。逆に、これ幸いとばかり、新しいOSでも入れてみるかって欲望が頭をもたげます)

で、freeBSDには、レスキュー用のコマンドが配置されている。場所は、/resucueという 特等席。この下に、救出に必要と思われるコマンドが置いてある。選りすぐった精鋭だな。 一体幾つあるの?

[sakae@fb11 /rescue]$ ls -l
-r-xr-xr-x  142 root  wheel  9578456 Dec  8 05:15 [*
-r-xr-xr-x  142 root  wheel  9578456 Dec  8 05:15 atmconfig*
-r-xr-xr-x  142 root  wheel  9578456 Dec  8 05:15 badsect*
-r-xr-xr-x  142 root  wheel  9578456 Dec  8 05:15 bsdlabel*
  :
-r-xr-xr-x  142 root  wheel  9578456 Dec  8 05:15 zdb*
-r-xr-xr-x  142 root  wheel  9578456 Dec  8 05:15 zfs*
-r-xr-xr-x  142 root  wheel  9578456 Dec  8 05:15 zpool*

リンクカウントが142ってなってるんで、142個、重要なのが有るって分かる。全て同一ファイル サイズ。と言う事は、これらは、同一のファイルで名前を変えてあるだけ。

こいつら(精鋭ですから、こいつらとは失礼な呼びかけだぞ)の作り方は、/usr/src/rescureに書いてある。READMEを見ると

1) Produce a reliable standalone set of /rescue tools.
2) Demonstrate robust use of crunchgen.
3) Produce a toolkit suitable for small distributions.

先にやった、crunchgen の機構を使って、一つの集団に仕立て上げている。

ちなみに、FreeBSDから出てるコマンドは幾つあるかと数えてみれば

[sakae@fb11 ~]$ ls /bin /sbin /usr/bin /usr/sbin | wc -l
     969

約7人に1人の割合で抜擢された精鋭ですよ。unix使いを自認するなら、こいつら精鋭の 癖や使い処を掌握しておくのは、司令官の義務でしょうな。 さあ、片っ端からmanしてみろよ。

ちなみに、リナ陣営でも同じ仕組みのbusyboxが有るけど、余り優遇されていない模様。 そのうちに、busyboxを核として、クーデターが起きるに違いない。

ブクブクにGUIで太ったやつは嫌いだ。メモリーが128Kでも、キビキビ動くリナを 作ったら、賛同される方は是非使ってあげてくださいってね。

それじゃ、OpenBSDに戻って、ちょっと実験。まずは、作ったsakを別名でハードリンク

[ob: sak]$ ln -h sak echo
[ob: sak]$ ./echo hello world
hello world

うん、ちゃんと動く。そんじゃ、ソフトリンクでは?

[ob: sak]$ ln -sf sak echo
[ob: sak]$ ./echo hello world
hello world

こちらもちゃんと動く。だったら、レスキュー部隊もソフトリンクでも良いのでは? だめだよ。ソフトリンクだと実体は一人しかいないから、それが倒れたら、全員役立たずに なっちゃう。その点、ハードリンクだと、一人がダメージを受けても、意志を受け継いだ 他の隊員が活躍出来る。それに、倒れた隊員の復旧もlnするだけで済むんで、こういう用途には うってつけなんさ。

[ob: sak]$ ln -h sak hoge
[ob: sak]$ ./hoge hello world
sak: hoge not compiled in
Usage: sak <prog> <args> ..., where <prog> is one of:
 ping6 ndp nc echo sak

そんじゃ、悪乗りして、登録時以外でリンクしたら? こちらは、原本の影武者が 生まれた事になる。(生まれをちゃんと報告するあたり、自信過剰ぎみではありますが)

[ob: sak]$ rm sak
[ob: sak]$ ln -hf hoge echo
[ob: sak]$ ./echo hello world
hello world

だから、こういう事も可能。レスキュー部隊のDNAが一つでも残っていれば、未来へその 意志を託す事が出来るんだ。

instbin.conf

ここまで、instbinと言うランチャーの作り方を見てきたけど、その元となる仕様書 instbin.confの事をないがしろにしてた。ここで、光を当ててみる。

大本は、listとlist.localって言う、2つのファイル。ここでは、listの方を 取り上げよう。

#       $OpenBSD: list,v 1.40 2016/04/02 12:23:46 rpe Exp $

SRCDIRS distrib/special

# copy the crunched binary, link to it, and kill it
COPY    ${OBJDIR}/instbin                       instbin
LINK    instbin                                 bin/arch
LINK    instbin                                 bin/cat
LINK    instbin                                 bin/chmod bin/chgrp sbin/chown
 :
# and the installation tools
SCRIPT  ${CURDIR}/../../miniroot/dot.profile    .profile
SCRIPT  ${CURDIR}/../common/install.md          install.md
SCRIPT  ${CURDIR}/../../miniroot/install.sub    install.sub
SPECIAL chmod 755 install.sub
SYMLINK install.sub                             autoinstall

何となくフォーマットが分かるようだ。左の列は、どうしろとって指示だな。真ん中の列は、 ファイルの供給元、右の列は、それの落ち着き先って事だろう。

これを、makeconf.awkに食わせると、crunchgen 用の仕様書が出来上がるとな。

[ob: tmp]$ awk -f makeconf.awk list
#
# This file is automatically generated by `makeconf'
#

srcdirs distrib/special

progs dd resolv.conf mount_cd9660 md5 df mount arch sync
 :
ln ksh -sh

libs -lstubs -lutil -lm -lc

libdirs distrib/special/libstubs

雑にスクリプトの働きを推測すると、左側のフィールドがLINKになってる所を見つけて、 右のフィールドを / で分割。作るべきプログラムを列挙するぐらいかなあ。

awkのスクリプトを開いてみる。 予想してたのより、もう少し機能が豊富だった。一行に1ファイルって言う原則を破って いるので、複雑さに拍車をかけているのか。その分、書く人は楽出来る。

左、中、右ってのが、$1,$2,$3 と、読み替えしなきゃいけないのは、awkのお約束事項だな。 NFってのは、フィールドの個数で、$0は行全体を表すか。

コードを読む時は、出力する部分、END { ... } から、逆に読んでくのが楽。 いずれにしろ、コメントが入っているので、perlの判じ物を読むより楽だわい。

で、いよいよ mtreeに取り掛かろうと思うのだけど、長くなりそうなので、to be continue.

etc

WindowsユーザーがTensorFlowをインストールしてみた(Docker版)

TensorFlowがWindowsサポートしたのでインストールしてみた

Pythonを呼べるLispを作った話

高校生でもわかるニューラルネットワークの基礎解説