WasmLinux: 移植ターゲットの調査
Linuxのsyscall emulationを実装するにはLinux自体を使うのが良かろうということで、LKLとLinux libc(MUSLを想定)のブリッジを書いて、MUSL側に小規模のパッチを書くことでLinux環境を仕立てることを考える。
LKL
LKLはLinuxカーネルを普通のユーザランドライブラリにしたもので、いわゆるNO-MMU Linuxカーネルとして使える。
LKL自体は(一般的に期待されるような)完全なLinuxシステムではないため、不足ぶんは補ってやる必要があると考えている。
移植が必要なもの
LKLが期待する移植層は <uapi/asm/host_ops.h>
に集約されている。
おおきく分けて:
- スレッドやTLS
- (mmap類 -- カーネルではKAsanでしか使われないようだ)
- setjmp/longjmp。たぶんC標準のsetjmp/longjmpではなく、ファイバの代用として使っていると考えられる。(コンテキストスイッチのために呼んでるので)
- (同期プリミティブ類 -- 普通のPOSIX語彙で良い)
普通のC言語環境では直ぐに揃いそうなものばかりだが、WebAssemblyではなかなか難しいものが揃っている。
さくせん
WebAssembly自体にはスレッドや mmap
を可能にするような仕様は標準では存在しないので、一旦WASM3あたりのインタプリタを適当に改造し、あとからbinary translationで合わせこむ形にするのが良いだろう。つまり、
- 最初に、Wasmモジュールに見せるメモリを
mmap
した領域にしておき、シグナルハンドラを使って諸々のエミュレーションをする。これは例えばv8のようにシグナルハンドラをJavaScript用に使ってしまっている処理系では実装できない。 - 全てのlinear memoryアクセスを関数に開き、v8のようなインタプリタでも実行できるようにする。
- 関数化すべきメモリアクセスとそうでないメモリアクセスを 何らかの方法で 識別して最適化する。
例えば、ポインタ経由のメモリアクセスで、Wasmのページサイズ(64k)を跨がないと考えられるものはポインタさえ一度変換してしまえば他のアクセスは直接行える。v86のような仮想TLB方式 https://github.com/copy/v86/blob/master/docs/how-it-works.md も使えるだろう。
setjmp/longjmp関連はどうしようもないので、全体を asyncify https://github.com/WebAssembly/binaryen/blob/main/src/passes/Asyncify.cpp するしかない。stack-switching プロポーサルが存在するが、最近動きがない。
全体にAsyncifyを掛ける必要性から、マルチスレッド関連はいわゆるgreen threadで(も)実装できる。 thread_stack
はWebAssembly上では簡単には実装できないが、これはKAsanサポートにしか使用されていないようだ。
mmap
もカーネルでは使われていない。ただ、ユーザランド側がmmap無しだとキツいものがあるのでそのうち実装する必要はあるだろう。