Open3

WasmLinux: 目指せユーザーランド

okuokuokuoku

ついに(とりあえずの)ゴールが見えてきた。。

ここまでで、 read write pipe の3つのsyscallが正常に動作することを確認できた。LKLの仕組み上、普通にsyscallすると全てカーネルコンテキスト(要するにinit)で実行されてしまい、ユーザランドのエミュレーションする目的としては厳しいもんがある。

というわけで、何とかカーネル内で clone を実行してユーザーランドのpidを追加したい。

okuokuokuoku

作戦を考える

これマジで難しいゾ...

kernel_clone 直接法

fork.cuser_mode_thread を使うのが一番簡単だろうか。。つまり、 /init の実行時にも使われている、

https://github.com/lkl/linux/blob/3023e6f25fbf6d5f95b4e7ebd011fa688434ce5f/kernel/fork.c#L2737-L2748

のように kernel_clone を直接呼ぶ方法が良い気がしている。この場合は、作成されたカーネルスレッドから(新規追加する) lkl_ops経由で上に載るユーザーコードを実行させることになる。

vfork

他の手法としては、nommu Linuxの伝統通り、vforkしてしまうというのも考えられる。この場合は、 clone3CLONE_VFORK|CLONE_VM を渡すことになる。

clone後はそのままユーザー側に戻ってくることになるので、適当なbinfmtを書いて execve をhookすることになる。 ...手間としては kernel_clone 直接法とあんまり変わらんな。。

ハイブリッドカーネル法 ★

そもそも、カーネルから見た個々のプロセスでアドレス空間とかを共有するのは変わんないんだから、主従を逆転させてsyscallの発行タイミングでプロセスコンテキストを紐付けてしまう方が簡単なのではないか説。

https://github.com/lkl/linux/blob/3023e6f25fbf6d5f95b4e7ebd011fa688434ce5f/arch/lkl/kernel/syscalls.c#L52-L66

syscallの実行コンテキストであるhost taskを作成する際に CLONE_THREAD を常に指定している。これ抜きのhost taskを作成する方法を考えれば良いんではないか。

okuokuokuoku

ハイブリッドカーネル法で往く

つまり、ユーザープロセスのforkとかスレッドの生成はLinux側の clone を使わずにエミュレーションする。これはvforkとかexecveも同様にエミュレーションしないといけないということになる。

Linuxカーネル側には wasmlinux_* として、それ用のhookを追加した。

https://github.com/okuoku/lkl-wasm/commit/3847b0a54e69191fba424a2e26ab2436f0882d4c

でWasm側の呼出しコードも同様に実装:

https://github.com/okuoku/lkl-wasm/commit/407b7298d75cfbb2d81b35558015e73715581baf

これで、晴れて pid = 27 が割り当てられた。

write res = 4
res = 4
[stdout]: out
TLS[58]: 1 -> 383fa400
Oneshot timer: 8 2455730
Rearm: 2455730
Wait: 2455730
TLS[62]: 1 -> 383fa800
Run: 383e1410 0x7ffd74000bc0
Throw: 4144fc8c 0x7ffd74000bc0
(user) pid = 27

[stdout]: out は、init(pid = 1)で生成したfdを読み出すスレッドが出力していて、これはプロセス間で fd を渡せていることを意味する。