Open4
WasmLinux: MUSL libcユーザランドの事前調査
とりあえずHello, worldに必要なものだけ先に用意して、あとはprintfデバッグしつつpthreadやTLS、シグナルを用意するのが良いかな。いわゆるPOSIXのsingle process環境に近いところを目指す。
純粋なHello, worldで呼ばれるsyscallを列挙する
#include <stdio.h>
int
__original_main(int ac, char** av){
printf("Hello, world!\n");
return 0;
}
をコンパイルして wasm-decompile
に掛ける:
$ wasm-decompile --enable-threads user.wasm | grep syscall
import function wasmlinux_syscall32(a:int, b:int, c:int):int;
f = wasmlinux_syscall32(222, 6, b);
c = wasmlinux_syscall32(96, 1, b);
if (wasmlinux_syscall32(73, 5, c + 208) > -1) goto B_k;
if (wasmlinux_syscall32(56, 3, c + 208) > -1) goto B_l;
if (wasmlinux_syscall32(56, 3, c + 208) > -1) goto B_m;
if (wasmlinux_syscall32(56, 3, c + 208) > -1) goto B_n;
wasmlinux_syscall32(94, 1, b);
wasmlinux_syscall32(93, 1, b);
function syscall_ret(a:int):int {
a = syscall_ret(wasmlinux_syscall32(57, 1, b));
b = syscall_ret(wasmlinux_syscall32(66, 3, d + 16));
f != (b = syscall_ret(wasmlinux_syscall32(66, 3, d + 16)))) continue L_c;
if (eqz(wasmlinux_syscall32(29, 3, d + 16))) goto B_a;
if (wasmlinux_syscall32(98, 4, b) != -38) goto B_c;
wasmlinux_syscall32(98, 4, b);
if (wasmlinux_syscall32(98, 3, b) != -38) goto B_b;
wasmlinux_syscall32(98, 3, b);
if (wasmlinux_syscall32(98, 4, b) != -38) goto B_g;
wasmlinux_syscall32(98, 4, b);
c = syscall_ret(wasmlinux_syscall32(62, 3, d));
#define __NR3264_mmap 222
#define __NR_set_tid_address 96
#define __NR_ppoll 73
#define __NR_openat 56
#define __NR_exit 93
#define __NR_exit_group 94
#define __NR_close 57
#define __NR_writev 66
#define __NR_ioctl 29
#define __NR_futex 98
#define __NR3264_lseek 62
なかなかキツそうなのが揃ってんな。。一旦libcじゃなくて直接syscallを発行するスタイルに戻して動作確認するのが良いかな。
futexとmmapはエミュレーションしないといけないかもしれない。NOMMUのドキュメントにはかなり複雑な実装状況が書かれている。
Hello, World 直後にクラッシュする
... 最初 dup
したfdを閉じたら pid = 1 の方でも閉じた判定になってて焦ったけど、cloneに余計なフラグを渡してたのが問題だった。
とりあえず、ここまででMUSLをリンクしたWasm moduleから printf("Hello, world!\n");
できるようになったが、直後でクラッシュする:
* thread #28, name = 'runner', stop reason = signal SIGABRT
* frame #0: 0x00007ffff7ab0884 libc.so.6`__pthread_kill_implementation + 276
frame #1: 0x00007ffff7a5fafe libc.so.6`raise + 30
frame #2: 0x00007ffff7a4887f libc.so.6`abort + 223
frame #3: 0x00000000004024e5 runner`wasm_rt_trap(code=WASM_RT_TRAP_CALL_INDIRECT) at wasm-rt-impl.c:75:3
frame #4: 0x0000000001350e15 runner`w2c_kernel_lkl_syscall(instance=0x00007ffd78000b70, var_p0=98, var_p1=4, var_p2=1042546656) at lin.c:1692154
frame #5: 0x0000000001373388 runner`w2c_kernel_syscall_0(instance=0x00007ffd78000b70, var_p0=98, var_p1=4, var_p2=1042546656) at lin.c:1704131
frame #6: 0x0000000000403f29 runner`w2c_kernel_syscall(instance=0x00007ffd78000b70, var_p0=98, var_p1=4, var_p2=1042546656) at lin.c:191102:10
frame #7: 0x000000000269be71 runner`runsyscall32(no=98, nargs=4, in=1042546656) at runner.cpp:201:30
frame #8: 0x000000000269c00e runner`w2c_env_wasmlinux_syscall32(env=0x0000000000000000, argc=4, no=98, args=1042546656) at runner.cpp:274:24
frame #9: 0x00000000026844f2 runner`w2c_user_0x5F_lockfile(instance=0x00000000029a5260, var_p0=1868963937) at user.c:3136:14
frame #10: 0x00000000026852a8 runner`w2c_user_0x5F_stdio_exit(instance=0x00000000029a5260) at user.c:3378:12
frame #11: 0x000000000268336f runner`w2c_user_exit(instance=0x00000000029a5260, var_p0=0) at user.c:2679:3
frame #12: 0x0000000002682f83 runner`w2c_user_libc_start_main_stage2(instance=0x00000000029a5260, var_p0=2, var_p1=1, var_p2=1059577860) at user.c:2588:3
frame #13: 0x0000000002682e14 runner`w2c_user_0x5F_libc_start_main(instance=0x00000000029a5260, var_p0=2, var_p1=1, var_p2=1059577860, var_p3=1, var_p4=0, var_p5=0) at user.c:2568:12
frame #14: 0x0000000002680a5e runner`w2c_user_0x5Fstart_c_0(instance=0x00000000029a5260, var_p0=1059577856) at user.c:1723:12
frame #15: 0x000000000267eec3 runner`w2c_user_0x5Fstart_c(instance=0x00000000029a5260, var_p0=1059577856) at user.c:1361:3
frame #16: 0x000000000269c34e runner`thr_user(procctx=948439040) at runner.cpp:359:25
frame #17: 0x00000000026a0516 runner`void std::__invoke_impl<void, void (*)(unsigned int), unsigned int>((null)=__invoke_other @ 0x00007ffd94ff8b10, __f=0x0000000002a00cd0, (null)=0x0000000002a00cc8) at invoke.h:61:36
frame #18: 0x00000000026a02f5 runner`std::__invoke_result<void (*)(unsigned int), unsigned int>::type std::__invoke<void (*)(unsigned int), unsigned int>(__fn=0x0000000002a00cd0, (null)=0x0000000002a00cc8) at invoke.h:96:40
frame #19: 0x00000000026a0079 runner`void std::thread::_Invoker<std::tuple<void (*)(unsigned int), unsigned int>>::_M_invoke<0ul, 1ul>(this=0x0000000002a00cc8, (null)=_Index_tuple<0, 1> @ 0x00007ffd94ff8b70) at std_thread.h:292:26
frame #20: 0x000000000269ff34 runner`std::thread::_Invoker<std::tuple<void (*)(unsigned int), unsigned int>>::operator()(this=0x0000000002a00cc8) at std_thread.h:299:20
frame #21: 0x000000000269fec6 runner`std::thread::_State_impl<std::thread::_Invoker<std::tuple<void (*)(unsigned int), unsigned int>>>::_M_run(this=0x0000000002a00cc0) at std_thread.h:244:20
frame #22: 0x00007ffff7ce31b3 libstdc++.so.6`execute_native_thread_routine + 19
frame #23: 0x00007ffff7aae947 libc.so.6`start_thread + 759
frame #24: 0x00007ffff7b34860 libc.so.6`__clone3 + 48
呼ぼうとしているsyscallは、 w2c_kernel_0x5F_se_sys_futex_time32
、
(lldb) p instance->w2c_T0.data[var_i4]
(wasm_rt_funcref_t) $0 = {
func_type = 0x00000000026a1470 "\xe3\xe1\xd3u_A\x96\xa4\xe0\xe2P\x997O\xf5^\xe1\x88\xd0X\xd13۳\xac\xd6\U0000001a#\xbf\xd4\xc01"
func = 0x00000000010e9606 (runner`w2c_kernel_0x5F_se_sys_futex_time32 at lin.c:1450451)
module_instance = 0x00007ffd78000b70
}
args = 4 は明かに少い。。
MUSL側の __futexwait
は SYS_futex
で確かにこれを 98
番にアサインしている。
この引数の違いがどこから来たのか調査する必要があるな。。
time_t
用じゃん
というかこれ 32bit
ウチのABIはレガシーフリーで行こうぜ。。