robust_quine.pl

Ruby にできて Perl にできないことは無いという格言があります。

http://d.hatena.ne.jp/ku-ma-me/20140219/p1

$$qq.=q';printf"\$\$qq.=q%c%s%c;;##%c;\n\$\$uu.=q%c%s%c;;##%c;\n\n\$\$ii||=%c\$_;;\$\$\$ii.=\$\$qq;;s#^;.{217}\$#\$\$qq#ee;;#;#^^;\n\$\$nn||=%c\$_;;\$\$\$nn.=\$\$uu;;s#;.{217}\$#\$\$uu#ee;#;",39,$&,39,39,39,$&,39,39,92,92;exit';;##';
$$uu.=q';printf"\$\$qq.=q%c%s%c;;##%c;\n\$\$uu.=q%c%s%c;;##%c;\n\n\$\$ii||=%c\$_;;\$\$\$ii.=\$\$qq;;s#^;.{217}\$#\$\$qq#ee;;#;#^^;\n\$\$nn||=%c\$_;;\$\$\$nn.=\$\$uu;;s#;.{217}\$#\$\$uu#ee;#;",39,$&,39,39,39,$&,39,39,92,92;exit';;##';

$$ii||=\$_;;$$$ii.=$$qq;;s#^;.{217}$#$$qq#ee;;#;#^^;
$$nn||=\$_;;$$$nn.=$$uu;;s#;.{217}$#$$uu#ee;#;

まぁ Perl はだいぶラクですな…

mingn

MinGW 的な、ミニマルな GCC 開発環境を NaCl 上で動かしたいなってことで冬休みくらいから遊んでた (gcc のポート自体は一昨年からあったが…) ものをちまちま naclports という NaCl に移植したアプリ集みたいなところにコミットして、なんとなく区切りがついたような気がしたのでデモサイトを作ってみました。

http://commondatastorage.googleapis.com/mingn/index.html

できることは gcc で適当なものをコンパイルしたり、 vim で編集したり rubypythonnethack が動くくらいです。欠点としては redirect と pipe と sub-shell が無いので使いものにならないことです。 ICFPC 2006 の UMIX とかも動くようにコードを置いておきました。

bootstrap の bash と unzip 以外は HTML5 File System に突っ込んだバイナリを起動してるんで、 Chrome OS でオフライン開発する夢に一歩近付いたかなぁと思ってるんですけど、まだ10歩くらいはある道程なのでしんどい。

まあ TODO は死ぬほどたくさんあるんだけど、死ぬほどたくさんありすぎて、かなり巨大なやる気が来ないと解決されないので…

ARM test environment on Debian

$ cat /etc/apt/sources.list.d/emdebian.list
deb http://www.emdebian.org/debian/ sid main

とか置いておいて、

$ sudo apt-get update
$ sudo apt-get install g++-4.7-arm-linux-gnueabihf qemu gdb-arm-none-eabi

とかで。 gdb-arm-none-eabi は gdb-multiarch はどうもおかしかったので。

実行は

$ arm-linux-gnueabihf-gcc-4.7 hello.c
$ qemu-arm -L /usr/arm-linux-gnueabihf ./a.out

デバッグ

$ qemu-arm -L /usr/arm-linux-gnueabihf -g 1234 ./a.out
$ arm-none-eabi-gdb ./a.out
(gdb) set sysroot /usr/arm-linux-gnueabihf
(gdb) target remote :1234
(gdb) b main
(gdb) cont

など。

Ubuntu はデフォルトでももうちょい色々入ってたと思います。

ARM にゅうもん

例のごとく、書かないと覚えられません。

参考文献

日本語命令セット: http://www.ced.is.utsunomiya-u.ac.jp/lecture/2013/aca/ARMjp-vH.pdf

コンパイラのマニュアルらしいけど概要的に便利なやつ: http://infocenter.arm.com/help/topic/com.arm.doc.dui0056d/DUI0056.pdf

registers

r0 から r3 は caller save なレジスタで引数に使う。 r4-r11 はたぶん普通は callee save 。

r12 は別名 ip 。 r12 は ARM Thumb の切り替え時に必要なら使うらしい…が、ぱっと見短い R_ARM_CALL なら使わない気もする。まぁいずれにせよ caller save

r13 は sp 。 r14 は lr で bl か blx 時に戻りアドレスが勝手に入る。 r15 は pc 。いじると jmp になる。

d0-d31 倍精度浮動小数レジスタ。 s0-s31 までは単精度浮動小数レジスタで、 d0 は s0:s1 。 VFP とか言う。 SIMD 拡張が NEON で、 q0 が d0:d1 みたいな感じで 128 bit の SIMD レジスタになる。

あと NaCl は r9 が thread pointer で触れない

calling convention

r0 から r3 が引数と戻り値。レジスタ一つに入らない struct とかは stack 使う。戻り値は double と long long で r0 r1 使って、 128bit の値は r0-r3 使う。 64bit struct とかは stack 、ぽい。入り切らなくなったら stack 使うけど、これで long long が分割されたりもする。

浮動小数まわりの ABI は GCC だと三種類あって、 -mfloat-abi=soft, -mfloat-abi=softfp (android), -mfloat-abi=hard (chrome) 。 soft だと VFP レジスタをいっさい使わなくて、 softfp は使うけど引数渡しは汎用レジスタ使う、 hard は引数渡しにも VFP レジスタ (NaCl だと d0-d7) 使う。

レジスタ渡しする calling convention は可変長引数が面白いことになりがちですが、 ARM も面白くて、浮動小数レジスタを絶対使わなくなる。つまり正しい宣言が無いと呼び出しが混乱して、

int main() {
  va_func(42.0);
  extern void va_func(float f, ...);
  va_func(42.0);
}
void va_func(float f, ...) {
  printf("va_func: %f\n", f);
}

とかだと一個目の結果がおかしくなる。浮動小数の ABI は混ぜるな危険もいいところなんで、 .o の .ARM.attributes って section に ABI 情報が入ってる。

ARM Thumb interwork

Thumb モードは命令エンコードが ARM モードより短い変わりに、少し命令がリッチじゃないモード、らしい。一般的には ARM モードが速いとされてる、らしい。基本的に decoder の state を変えるだけなので変更は一瞬らしい(つっても pipeline は乱れる?)。

Thumb モードの関数ポインタは奇数。 ARM モードは偶数。レジスタ指定のブランチ命令 (bx と blx) はレジスタに入ってる値が偶数なら ARM に、奇数なら Thumb モードに切り変える。 24bit 相対のブランチ命令 (b, bx, bl と blx) は x がついてるやつは ARM Thumb 逆転。 relocation が R_ARM_CALL になってると、飛び先のモードに応じて、 link time に bl と blx を変えたりするっぽい。

今 ARM Thumb モードどっちか、ってのは cpsr ってレジスタの 0x20 の bit に入ってるぽい。 gdb とかはたぶんこれ見て状態を確認してるんだと思われる。ちなみに pc は Thumb でも常に偶数ぽい。

リンカとか objdump とかが適切に ARM Thumb 出せるのは、たぶんシンボルの値が奇数なら Thumb 、とかやってるのかなぁと思います…たぶん。 objdump -b binary とかだとシンボル情報無いんで、 Thumb のコードを見たい、って時は -Mforce-thumb とかつければ良いぽい。

instructions

よく言われる特徴的なのは ARM mode ではたいていの命令は condition bit を見て条件実行する 4bit が先頭についてること。常時実行が 0b1110 なので、 ARM mode の命令はだいたい 0xe で始まってる。ただ、スペースが明らかに足りてない、 24bit 即値ブランチとかは条件無し。

あとは RISC なんで即値代入が2命令にわかれてるのが特徴的か。やたらと movw movt ってのを見る感じ。 0x23456789 とかを代入すると、

  10:   e3062789        movw    r2, #26505      ; 0x6789
  14:   e3422345        movt    r2, #9029       ; 0x2345

とかになる。即値が二箇所に飛び散るからエンコードされたものが読みにくい…

あとは push/pop で任意個の複数レジスタを好き勝手 push/pop できるのが面白いですかね。関数の入口出口でまとめて退避できて便利。レジスタ 16 本なんで 16bit でどれを退避…みたいな感じになる。

objdump とか gas 読む時は基本 destination が左で、まぁ読みやすい。 str だけ destination が右になるけど。

NaCl のメモリレイアウトについて

前から図でも書こうかなぁと思ってたのですが、機会があったのでとりあえずスプレッドシートを作ってみました。

https://docs.google.com/spreadsheet/ccc?key=0AolcvzoWgN21dG5PVldnM0VJUzByU2hqcXFQaGVVdWc&usp=drive_web

ひとつ前で書いた通り、 NaCl では text は最初の 256MB に位置していて、 data はその後の 768MB を使うことになっています (x86-64 では data は 3840MB)。普通の linux バイナリでは text の直後に data が配置されるようになってるため、大雑把に言うと

main binary の text
main binary の data
隙間
libc.so の text
libc.so の data
隙間
...

というように配置されるわけですけど、 NaCl (glibc) では

main binary の text
libc.so の text
...
隙間
main binary の data
libc.so の data
...

というように配置されます。こういうことを実現するために、 NaCl では ELF binary を作った時点で text と data の距離が 256MB 離れているバイナリが生成されます。例えば適当に作った hello は、通常の linux バイナリでは

$ readelf -l a.out | grep LOAD
  LOAD           0x000000 0x08048000 0x08048000 0x0059c 0x0059c R E 0x1000
  LOAD           0x00059c 0x0804959c 0x0804959c 0x00118 0x0011c RW  0x1000

という感じで、 PT_LOAD が連続して配置されているんですけど、 NaCl バイナリでは

$ readelf -l a.out | grep LOAD
  LOAD           0x010000 0x01000000 0x01000000 0x10000 0x10000 R E 0x10000
  LOAD           0x020000 0x11000000 0x11000000 0x00230 0x00230 R   0x10000
  LOAD           0x020230 0x11010230 0x11010230 0x0010c 0x0010c RW  0x10000
  LOAD           0x030000 0x11020000 0x11020000 0x00000 0x00020 RW  0x10000

のように、最初の PT_LOAD (text) とその次 (rodata) の間に 256MB==0x10000000 bytes の隙間があります。このため、一つ前で mmap された場所によって、 text が入っても data が入らない場合や、 data が入っても text が入らない場合があるので、 text と data の大きい方のサイズ分だけ両方の領域が消費されます。例えば一つ目のバイナリの text が 1MB で data が 2MB の場合、次のバイナリは text も data も 2MB 進んだところに配置されるし、逆に text が 2MB で data が 1MB の場合も同様です。

このため、空間効率の良い NaCl バイナリは text と data のサイズが近いバイナリってことになるんで、データだけが入ってる so とかは text がムダになるのであまり作りたくない感じになっています。

現状、 so は dlclose されても munmap はするけど消費された領域は再利用しない実装になってるため (直す予定はあるみたいですけど) dlopen と dlclose を繰り返すと領域を使い果たします。そのあたりのコードは https://chromium.googlesource.com/native_client/nacl-glibc/+/master/sysdeps/nacl/nacl_dyncode_alloc.c にあります。余談ですけどこのコードはなんでこんなややこしい書き方するんだろ、ってコードになってて読みにくいです…

また、固定で置き場所が決まっているバイナリなどがいくつかあります

  • 0x10000 から 0x20000 は verify されない、 service runtime が NaCl syscall の entry point への jmp 命令を置く空間になってます。直接 service runtime のコードへの jmp は verifier が許さないので、 Pepper API を使う場合などは、ここを経由して NaCl syscall することになっています
  • 0x20000 から 0x1000000 までの空間はダイナミックローダ (runnable-ld.so) の text が置かれます。同様に 0x10020000 から 0x11000000 は runnable-ld.so の data が配置されます。残った空間は nacl_dyncode_create や mmap で使うことはできると思いますけど。
  • 0x1000000 から 0x0fa00000 までの空間は、 so が順次置かれていきます。 nacl_dyncode_create で JIT したコードを置きたい場合は、後続の dlopen で使う領域と競合されないように、 0x0fa00000 より少し小さいアドレスを適当に使うと良いみたいです。まぁこのへんは service runtime 側でちゃんと allocation して欲しい感じで、まぁ直す予定はあるみたいです…
  • 0x0fa00000 から先は IRT が使います。 IRT は text と data の gap が 256MB ではなくて、 untrusted code が使える data 領域の最後の部分、具体的には 0x3ef00000 から先が使われるようになってるみたいです。
  • so が配置された後、 IRT の前の data 領域は、 NaCl syscall の mmap が後ろから順に適当に使っていくみたいです。たくさん allocation すると so のための領域とぶつかるので、 dlopen やら mmap が失敗しはじめるはずです。
  • 今回調べてて気付いたのですが、 IRT の data 領域の後の領域は main thread の stack になるみたいです。ここに stack guard 無いのは、あった方がいい気しかしないです…こわい

というようなことをまとめたのが最初の spread sheet になっています。

newlib や static link した glibc のバイナリは so が無いのでシンプルで、 newlib の場合 runnable-ld.so が置かれる場所に、 static link した glibc のバイナリは dynamic link された場合の main binary の場所に main binary が置かれます。

残念ながら、これも NaCl のメモリレイアウトについての世界一詳しい記述だと思いますね…

NaCl について

カーネル/VM Advent Calendar 2013 にさっき登録しました。需要の無さそうな NaCl について語ります。

https://qiita.com/advent-calendar/2013/kernelvm

NaCl はグーグルが作ったものの中で一番好きくらいに好きなものです。理由は低レイヤコンポーネント集だから。概要としては安全に実行できる(ここでいう安全はブラウザが動いてる OS 上での任意コード実行ができない、という意味) Active X というか、 C/C++ でコードが書ける Java Applet というか、まぁそういう感じの。

NaCl はおおざっぱに言って、

  • 検証可能なバイナリを出力するコンパイラツールチェイン (gcc, binutils, etc.)
  • ユーザプログラムを検証して起動する service runtime
  • service runtime と libc の間に安定した ABI を提供する integrated runtime
  • newlib と glibc から選べる libc
  • ブラウザで色んなオペレーションを実現する Pepper API とのやりとり

あたりの部品があります。以下では一つずつ見ていきます。

そのまえに用語として、 trusted code とかそのあたりが混乱を招きがちなので最初に。

  • trusted code は chrome とかのコードの一部になってるコードの大部分を指します。ホスト側とか言ったりもすると思います。
  • untrusted code はプラグイン側のコードで、何するかわからんので信用してません。この文章ではゲスト側とかも言います。まぎらわしいですが、 NaCl サイドのコードにも一部 untrusted code もあります。 nexe と言ったりします。ネクシーと発音するのがオシャレぽいです。

Software Fault Isolation

NaCl といえばこの話、という感じなので NaCl に興味を持ったことがある人は知ってる話ではあると思います。

C/C++ でコードが書けるのに安全、というのはおかしな話です。どうやってやってるかというと、コンパイラに手が入ってて、以下の二つが検証可能なバイナリしか吐けないようになっています。

  • jmp/call の飛び先が制限されている
  • メモリの読み書き先が制限されている

検証は実行バイナリを読むことによってなされています。 jmp/call の方はアドレス空間の最初の 256MB にしか飛ぶことができず、かつ 16 or 32 byte align されてるアドレスにしか飛べません。 256MB の制限の方は、直前に and 命令で 上位の bit をクリアしてない jmp/call は禁止、というルールでなされてます。

align の方も下位の bit をクリアしてないものを禁止するという方法でやってます。 align を強制する理由は、この境界をまたぐ命令を許したくないからです。許してしまうと operand jump で予想外の解釈がありえるバイナリができてしまうからです。また、いくつかのビットをクリアして call 、などはひとかたまりの naclcall 命令として認識されていて、この間に jmp するのも禁止です。そうでないとビットをクリアせずに call したりできてしまうので。

後で詳しく説明しますが、 NaCl のコード検証などを行なったりする service runtime と呼ばれる部分は同じプロセスで動いています。同じプロセスで動いてるってことは、 service runtime 内のメモリを読み書きされるとコード検証をスキップできたりしちゃってまずいので、メモリの読み書きも制限されています。この制限の仕方は x86 ではセグメントレジスタで、 x86-64 と ARM ではメモリの読み書きのたびに直前で and 命令で対象領域を制限させることによってなされています。このオーバヘッドは x86-64 と ARM では大きくて、 x86 で普通のバイナリと比べた時のオーバヘッド(平均的に 5% 強くらいとかだとされてたと思います)に対して、 x86-64 と ARM では 30% くらいのオーバヘッドがあると考えた方がいいみたいです。

読み書きできるメモリは、 x86 と ARM では先頭から 1GB 、 x86-64 では 4GB となってます。 x86-64 は実際には先頭から 4GB ではなく、とあるアドレスから 4GB というような感じで、そのとあるアドレスは常に R15 に入っていて、 R15 は変更禁止なレジスタになってます。

ちなみに x86-64 は 4GB しかメモリアドレスしか無いわけですが、それにあわせてポインタサイズも 32bit で、実体としては完全に x32 の ABI となっています。ただなぜか ELF64 を使ってしまってるんで、それだけおかしいです。なんかなおすつもりはあるみたいですけど。

あと特筆すべきこととしては NaCl バイナリの page size は 64kB です。

追記: x86 についてはそこらじゅうに解説がありますけど、 x86-64 と ARM についてはあまり解説が無いので、ドキュメントへのリンクはっときます。 ARM の定数ロードのしかたとか、結構たのしいです。

https://developer.chrome.com/native-client/reference/sandbox_internals/x86-64-sandbox

https://developer.chrome.com/native-client/reference/sandbox_internals/arm-32-bit-sandbox

Service Runtime

さっきチラっと書いた通り、よくあるサンドボックスだと supervisor は別のメモリ空間にいることが多い気がするんですが、 NaCl では同一プロセスにいます。 service runtime の仕事は

  • NaCl コンパイラコンパイルされた untrusted code を検証する
  • untrusted code をロードして実行する
  • untrusted code に NaCl system call を提供する

の3つだと思います。検証については一つ前に書いたので、ロードから。

service runtime はローダとしての機能を持っています。ローダっていうとだいたい、

  • プログラムをロードする
  • プログラムを再配置 (relocate) する
  • 適切にパラメータをセットしてプログラムを起動する
  • dlopen/dlclose に対応する

あたりが重要な仕事なわけですけど、 relocate と dlopen は service runtime はやらないです。つまりだいたい kernel がやることと同じだと思えば良いです。 ELF 読んでメモリに貼って auxv 渡しつつ起動する、と。 auxv は AT_SYSINFO に nacl_irt_query という特殊な関数ポインタを入れるだけです。 nacl_irt_query についてはまた後で。

service runtime の提供する NaCl system call は、まぁざっくり POSIX ぽいものが色々あります。 file 関係、 mmap 、 thread 、 futex 、時間関係、などです。 service runtime は二つのバイナリにリンクされてます。

片方が sel_ldr というもので、 unittest を Chrome の外で走らせる時なんかに便利なものです。もう片方は nacl_helper というもので、 Chrome の上で走らせる時に使われているものです。デフォルトでは file 関係の API とかは動かないようになっています。つながってるとサンドボックスの意味ないんで。 NACL_DANGEROUS_ENABLE_FILE_ACCESS=1 とか環境変数を指定してやると一応ファイル関係もつながるので、テスト時などは便利ではあります。

nacl_helper はホストのシステムで実行されるバイナリなんで、例えば Linux では glibc にリンクされてます。 Windows などでも POSIXAPI として WindowsAPI の一部を提供しているので、 NaCl は portable POSIX としての一面もあるということになります。

nacl_helper はホストのシステム上で動かすため、先頭 1GB のアドレスが libc などに使われてしまう環境ではハックが必要になります。 ARM なんかがそうで、そういう環境では nacl_helper_bootstrap というスタティックリンクしたバイナリが nacl_helper を起動するようになっています。 nacl_helper_bootstrap は最初に 1GB の欲しい領域を mmap で確保してから nacl_helper を exec するだけのバイナリで、 wine-preloader とかと同じ役割と持っていると言えます。

あとそうだ、うっかり NaCl のコード検証とかの仕組みが突破された場合に備えてか、 seccomp-bpf sandbox も一応やってます。

それともうひとつ、コード検証は結構遅いんで一度検証したバイナリは大丈夫だよーってキャッシュをしたりしてますな。

Integrated Runtime (IRT)

NaCl syscall は Linux syscall 同様、 libc/user code から直接呼ぶこともできるわけですけど、メモリレイアウトとかを変えるとすぐに互換性が壊れてしまうし、古いインターフェイスがイマイチだった時の変更とかもしにくい…ということで、バイナリ互換性を担保するための仕組みとして、 IRT があります。

IRT は基本的には NaCl syscall を呼ぶだけの小さな関数が大量に定義されてるだけのバイナリで、 service runtime によってロードされる untrusted なバイナリです。 untrusted code ですが、 IRT はシステムの一部として chrome と一緒に配布されてます。

IRT に入ってる関数を呼ぶには、 service runtime が libc のローダに渡している、 nacl_irt_query って関数ポインタを使う必要があります。 nacl_irt_query は文字列を受け取って関数ポインタ群を返す関数で、文字列は "nacl-irt-filename-0.1" などのようなフォーマットになっています。バージョンが入ってるので互換性に問題のある変更があった場合も安心、と。

NaCl SDK に入ってる irt_core_.nexe みたいなファイルの説明はこれでいいわけですけど、 Chrome に付属している IRT は Pepper の API を IPC で call する仕組みが入ってるので、 Chrome IPC を喋るコードとか、インターフェースで使ってる型 (skia とか) が入ってるので結構でかいです。まぁ概念は同じ。

libc (glibc or newlib)

NaCl は glibc と newlib をサポートしてます。 static link して小さいバイナリを作りたい場合は newlib 、そうでなければ glibc 、という感じで使いわけることが想定されてる気がします。

基本的にはオリジナルの libc そのまんまなわけですけど、それなりには変更が入っています。具体的には

  • Linux syscall のかわりに NaCl syscall を呼ぶ
  • glibc loader の起動のされ方が全然違うのでその対応
  • NaCl 流 thread local storage サポート

あたりが大きいのではないかと思います。昔は service runtime に futex 相当が無くて、 mutex で futex 実装したりしてたのでそこも大きかった気がしますが。

Linux kernel は普通メインプログラムを mmap してくれて、 auxv に AT_BASE や AT_PHDR なんかを通じて、ローダの情報やメインプログラムの情報を送ってくるわけですが、 service runtime は mmap せずに argv に入ってるプログラムをローダが mmap してね、ってスタンスで auxv も AT_SYSINFO に nacl_irt_query が入ってるだけなので、そのへんはまぁまぁおおきい、はず。

NaCl の thread local storage についての情報は結構詳しくここに書いてあります。

http://www.chromium.org/nativeclient/design-documents/thread-local-storage-tls-implementation

ざっくりまとめると、 NaCl じゃない場合と同じで、 TLS のためにレジスタ一個犠牲にして、そのレジスタは untrusted code からは自由に書き変えられない、ってな感じです。本当は IRT 側に必要な TLS とユーザコード側に必要な TLS とかあってややこしいわけですけど。

thread はだいたい clone と exit 相当の NaCl syscall / IRT が用意されてる感じです。違いは thread 終了時に clone の第七引数相当に futex wake が呼ばれないとか、そういう細かいの。たぶん。

NaCl glibc のバイナリは chrome に同梱されてなくて、ユーザプログラムと同時に配布さることが期待されています。このために ABI 互換性を留保するために IRT があるとも言えるし、 glibc newlib 両方が同じコード持つのがめんどくさいから IRT があるとも言えるし、まぁそんなかんじです。

Pepper API

ブラウザで NaCl 動かす場合は、そのままだと sandbox のせいで何もできないので、 Chrome IPC の口がつながっていて、そこを通じて Pepper API という関数群を呼べるようになっています。

ユーザコードから見ると、プログラム起動時から FD が一個 IPC 用に開いてて、 Pepper API 呼ぶと IRT 内のコードが sendmsg/recvmsg してブラウザとやりとりしてくれる感じ。

Pepper API 使うといろいろできます。 file 触ったり絵をかいたり、いろいろ。重要なのは JavaScript 側にメッセージを投げられる PostMessage ってやつでしょうか。

C の API は使いかたがやや難しいので、 C++ のやつを使うといいみたいです。最近は ppapi_simple という簡単なやりかたもあります。

Pepper API はまぁだいたい html5 として JavaScript ができるようにしてあることは、だいたいできるようなできないような…ってくらいの API がそろってるような気がします。たぶん。

余談もいいところですが Pepper のコードはむつかしいです。有史以来人類は IPC/RPC 難しいと言ってる気がして、進歩無いですな。 IPC って概念を説明すると強烈に簡単なんですけどね…

naclports

NaCl のデモとして色んな Unix で動くツールがポートされてるものです。言語処理系でいうと ruby python lua 、初期の頃からあった nethack だの SDL だの、あとは snes9x だの vim だのとにかく色々入ってます。

最近のやつは ppapi_simple ってやつと Pepper API 使って html5 filesystem に保存したりできる nacl_io ってのを使うのが基本ぽいです。 nacl_io は 3 つめの似た試みだと思いますね…

GDB (nacl-gdb)

GDB にロードされたバイナリを教えるのは通常ローダなわけですけど、 Chrome で動く NaCl の場合、 untrusted 側の glibc loader が知ってるパスはホスト側のシステムから見えるパスと全く一致してないんで、そのへんを解決する必要があります。

Chrome 上で動く NaCl の場合、 NMF (NaCl Manifest File) ってやつがブラウザから見える URL とバイナリの名前の対応を取ってるんで、その NMF てのを読めるようにしてあったり、 IRT も読ませたりできます。

あと、 service runtime はセキュリティ的な理由で untrusted code と違う stack で実行されてるんで、そのへんの対応も入ってるみたいです。

nacl_dyncode_create

JIT するものは動かすために、動的に生成したコードを NaCl の検証器に検証してもらう仕組みがあります。この API はかなり使い勝手が悪くて、 text 内の使うアドレスを自分で指定しないといけないので、自分のプロセスのメモリレイアウトをよく知ってる必要があります…

まぁ NaCl は text 領域の扱いがかなり適当で、 nacl-glibc も dlopen/dlclose 繰り返すだけでメモリ使い切れる素敵仕様だったりします。

デモとして、 mono と v8 がポートされてて naclports に入ってた気がするんですが、なんか両方ビルドされなくなってるような…

PNaCl

で、 PNaCl どうなん? って話があると思うんですが、あんまよく知らないんですよね…拡張入り LLVM bitcode を各 architecture の NaCl code に翻訳する、わけですよねたぶん…

まとめ

NaCl はいろいろ低レイヤなとこが入っててながめててたのしいです。難点はドキュメントが無いことです。この文章は書き散らかし文章ですけど、世界一詳しい NaCl についての概要文章なおそれすらある。

glibc signature quiz

会社のホワイトボードでシグネチャから関数名を当てるという遊びをやっていて、これが意外とかなり難しくて面白かったので、適当に html で作ってみました。

http://shinh.skr.jp/quiz/signature.html

一応軽くユニークなことは確認してありますが、ひょっとしたら複数解あるやつとかあったりするかも。

なにかあれば下記メールアドレスへ。
shinichiro.hamaji _at_ gmail.com
shinichiro.h