WasmLinux: 目指せHello World
とりあえずユーザランドのlibc移植は目処が付いたので、Hello Worldの実行を目指す。
うーん。。pipe(2)して片方をカーネル側でprintしつづけるのが良いかな。。とりあえずユーザランドをprintfデバッグできるようにするのが先決。その後libcのprintf → 引数を渡せるように変更 → マルチプロセス対応 → busyboxのtelnetdと進んでいくのが良いんじゃないだろうか。
なんかAssertする
mplite.c:287: mplite_unlink: Assertion `(handle->aCtrl[i] & MPLITE_CTRL_LOGSIZE) == iLogsize' failed.
メモリ破壊が起きてるな。。
mplite_unlink(handle=0x000000000296c320, i=16519040, iLogsize=6) at mplite.c:287:5
データブレークポイントを張る:
(lldb) watchpoint set variable mpool.aCtrl[16519040]
... カーネル側から書かれてるのはおかしいよな。。Wasm側アドレス 1076035456
は 0x7ffe37c51f80 で確かにwatchpointのアドレスになっている。
(lldb) bt
* thread #2, name = 'runner', stop reason = watchpoint 1
* frame #0: 0x000000000040305b runner`i64_store(mem=0x00007ffdf0000b80, addr=1076035456, value=0) at lin.c:164:1
frame #1: 0x0000000001244333 runner`w2c_kernel_number(instance=0x00007ffdf0000b70, var_p0=1076035573, var_p1=1076035560, var_p2=0, var_p3=1076035480) at lin.c:1583867
frame #2: 0x000000000123fae5 runner`w2c_kernel_vsnprintf(instance=0x00007ffdf0000b70, var_p0=1076035552, var_p1=1076035573, var_p2=305142, var_p3=1076035636) at lin.c:1581617
frame #3: 0x0000000001234523 runner`w2c_kernel_vprintk_store(instance=0x00007ffdf0000b70, var_p0=0, var_p1=1, var_p2=0, var_p3=305121, var_p4=1076035632) at lin.c:1577055
frame #4: 0x0000000001235d5d runner`w2c_kernel_vprintk_emit(instance=0x00007ffdf0000b70, var_p0=0, var_p1=0, var_p2=0, var_p3=305121, var_p4=1076035632) at lin.c:1577652
frame #5: 0x0000000001237eb2 runner`w2c_kernel_vprintk_default(instance=0x00007ffdf0000b70, var_p0=305121, var_p1=1076035632) at lin.c:1578369
frame #6: 0x00000000011a9d8b runner`w2c_kernel_vprintk(instance=0x00007ffdf0000b70, var_p0=305121, var_p1=1076035632) at lin.c:1522525
frame #7: 0x000000000122a66e runner`w2c_kernel_0x5Fprintk(instance=0x00007ffdf0000b70, var_p0=305121, var_p1=1076035632) at lin.c:1573232
frame #8: 0x0000000001195e36 runner`w2c_kernel_free_area_init_node(instance=0x00007ffdf0000b70, var_p0=0) at lin.c:1514591
frame #9: 0x0000000001194ae1 runner`w2c_kernel_free_area_init(instance=0x00007ffdf0000b70, var_p0=1076035784) at lin.c:1514124
frame #10: 0x000000000042a4e7 runner`w2c_kernel_bootmem_init(instance=0x00007ffdf0000b70, var_p0=3406) at lin.c:193940:3
frame #11: 0x0000000001297f31 runner`w2c_kernel_setup_arch(instance=0x00007ffdf0000b70, var_p0=1076035836) at lin.c:1618053
frame #12: 0x00000000010ff71d runner`w2c_kernel_start_kernel(instance=0x00007ffdf0000b70) at lin.c:1454998
frame #13: 0x0000000001298dd2 runner`w2c_kernel_lkl_run_kernel(instance=0x00007ffdf0000b70, var_p0=0) at lin.c:1618263
スタックポインタがおかしい。つまりスタックポインタの初期値が狂ってるな。。?
(lldb) p *instance
(w2c_kernel) $13 = {
w2c_env_instance = NULL
w2c_0x5F_stack_pointer = 1076035440
w2c_memory = (data = "", pages = 16429, max_pages = 65536, size = 1076690944, is64 = false)
syscallが成功しない
-22
を返却する。これは EINVAL
で引数が正常に渡っていないようだ。
[ 0.564232] ------------[ cut here ]------------
[ 0.564312] WARNING: CPU: 0 PID: 1 at arch/lkl/kernel/threads.c:129 0xdeadcafe
[ 0.564455] CPU: 0 PID: 1 Comm: host0 Tainted: G W 6.1.0+ #
[ 0.564514] Call Trace:
[ 0.564572] #00 [<0x000000000020a290>] 0x20a290
[ 0.564725] #01 [<0x0000000000000000>] 0x0
[ 0.564926] ---[ end trace 0000000000000000 ]---
Ret: -22, 0, 0
WARNINGはココ。
こういうときに自由にブレークポイント張れるのがwasm2cでデバッグする意義だよな。。
(lldb) b w2c_kernel_switch_to_host_task
Breakpoint 1: where = runner`w2c_kernel_switch_to_host_task + 15 at lin.c:1554882, address = 0x00000000011fad0f
周りのコードを読むと、どうもTLS関連がおかしいようだ。
とりあえず、実際に pipe2
がエラーを返却したのは確かで、
(lldb)
Process 134410 stopped
* thread #1, name = 'runner', stop reason = step over
frame #0: 0x00000000005bb48b runner`w2c_kernel_0x5F_do_pipe_flags(instance=0x000000000292c200, var_p0=2292624, var_p1=2292632, var_p2=2292640) at lin.c:342639:10
342636 var_l3 = var_i0;
342637 var_i0 = var_p2;
342638 var_i1 = 4294424447u;
-> 342639 var_i0 &= var_i1;
342640 if (var_i0) {goto var_B0;}
342641 var_i0 = var_p1;
342642 var_i1 = var_p2;
このとき flags
= 2292640 。ゴミが渡っている。。
syscallディスパッチが正しくない
lklは関数ポインタ経由でsyscallのディスパッチをしているが、ここで関数を可変長引数の関数にキャストしてしまっている:
これはC言語的には未定義のはずで、これが正しく実施できるアーキテクチャは限られている(常識的なABIは殆どセーフだけど)。実際これはWasm的には正しくないシーケンスになる ...がwasm2cはこれを弾けない。
frame #2: 0x000000000129656d runner`w2c_kernel_lkl_syscall(instance=0x000000000292c200, var_p0=59, var_p1=1059512320) at lin.c:1617613
1617610 var_i0 = var_l3;
1617611 var_i1 = var_l2;
1617612 var_i2 = var_l4;
-> 1617613 var_i0 = CALL_INDIRECT(instance->w2c_T0, u32 (*)(void*, u32, u32), w2c_kernel_t4, var_i2, instance->w2c_T0.data[var_i2].module_instance, var_i0, var_i1);
1617614 var_l4 = var_i0;
1617615 w2c_kernel_task_work_run(instance);
1617616 var_i0 = var_p0;
lkl_syscall
に引数カウントを渡して、再キャストして呼ぶしかないな。。
引数の数を渡すようにした
diff --git a/arch/lkl/include/asm/syscalls.h b/arch/lkl/include/asm/syscalls.h
index 4116e8a0cc6fc1..07d65ebfbb2e59 100644
--- a/arch/lkl/include/asm/syscalls.h
+++ b/arch/lkl/include/asm/syscalls.h
@@ -3,7 +3,11 @@
int syscalls_init(void);
void syscalls_cleanup(void);
+#ifndef __wasm__
long lkl_syscall(long no, long *params);
+#else
+long lkl_syscall(long no, int nargs, long *params);
+#endif
void wakeup_idle_host_task(void);
#define sys_mmap sys_mmap_pgoff
まぁ仕方ないね。。
If a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined.
Cはcompatibleでない関数ポインタにconvertして呼んだ場合の挙動はundefinedとしている。(なので厳密な禁止ではない)
For two function types to be compatible, both shall specify compatible return types. Moreover, the parameter type lists, if both are present, shall agree in the number of parameters and in use of the ellipsis terminator; corresponding parameters shall have compatible types.
関数ポインタがcompatibleであるためには、 ...
(ellipsis) の使用有無も一致しなければならない。今回のケースでは、
long syscall_XX(long a, long b);
を、
long syscall_XX(long a, ...);
にキャストして呼んでしまっているのでcompatibleでない。ただし、常識的なABIでは6個以下の可変長引数関数に整数のみを積んで呼ぶときのABIは通常の関数呼出しとかわらないのでLKLのような使い方でも大抵のアーキテクチャで動作する。(が、WebAssemblyはそうではない)