Open3

WasmLinux: ユーザーランドバイナリを頑張って作る

okuokuokuoku

とりあえずカーネルヘッダのexportだけやって、その結果だけでsyscallを呼出し、色々やってみるのが良いだろうか。。カーネルとユーザーランドで共有する構造体とかマクロはLinuxのソースツリーからコピーして使うのがルールになっている。

https://docs.kernel.org/kbuild/headers_install.html

$ make ARCH=lkl INSTALL_HDR_PATH=/home/oku/repos/linux/_hostwasm/prefix headers_install
scripts/Makefile.asm-generic:25: redundant generic-y found in arch/lkl/include/uapi/asm/Kbuild: siginfo.h
okuokuokuoku

カーネルヘッダはlibcに依存している

In file included from /home/oku/repos/linux/_hostwasm/prefix/include/asm/syscalls.h:105:
/home/oku/repos/linux/_hostwasm/prefix/include/linux/shm.h:8:10: error: 'unistd.h' file not found with <angled> include; use "quotes" instead
    8 | #include <unistd.h>
      |          ^


In file included from /home/oku/repos/linux/_hostwasm/prefix/include/asm/syscalls.h:132:
/home/oku/repos/linux/_hostwasm/prefix/include/linux/if.h:28:10: fatal error: 'sys/socket.h' file not found
   28 | #include <sys/socket.h>                 /* for struct sockaddr.         */
      |          ^~~~~~~~~~~~~~

... 冷静に考えると当たり前だな。。いっそのことmuslを早めに移植してしまうか。。?

okuokuokuoku

wasm2cのABIを固定するのがむずい

GOT経由でimportしてきた関数を参照してしまうケースがあるようだ。

/* Import syscall I/F */
#include <stdint.h>
__attribute__((import_module("env"), import_name("wasmlinux_syscall32")))
int32_t wasmlinux_syscall32(uint32_t no, uint32_t* in);

//#include <asm/syscalls.h>

int
main(int ac, char** av){
    const char msg[] = "hello, world!\n";
    uint32_t args[6];
    args[0] = (uint32_t)msg;
    args[1] = sizeof(msg);
    (void)wasmlinux_syscall32(64 /* __NR_write */, args);
    return 0;
}

/* Added dummy to make ABI stable for wasm2c generated file */

typedef int (*__dummy_export_t)(void);
typedef int32_t (*__dummy_export2_t)(uint32_t, uint32_t*);


int dummy_export(void);
static __dummy_export_t implicit_export = dummy_export;
int
__attribute__((export_name ("__dummy_export")))
dummy_export(void){
    implicit_export();
    return (int32_t)main & (int32_t)wasmlinux_syscall32;
}

このように、 wasmlinux_syscall32 の関数ポインタを取得しようとすると、

typedef struct w2c_user {
  struct w2c_env* w2c_env_instance;
  u32 w2c_0x5F_stack_pointer;
  u32 w2c_GOT0x2Edata0x2Einternal0x2E_0x5Fmemory_base;
  u32 w2c_GOT0x2Edata0x2Einternal0x2E_0x5Ftable_base;
  u32 w2c_GOT0x2Efunc0x2Einternal0x2Ewasmlinux_syscall32; // ★ これが追加される
  wasm_rt_memory_t w2c_memory;
  wasm_rt_funcref_table_t w2c_T0;
} w2c_user;

GOT.func.internal.wasmlinux_syscall32 が(Wasm的な)グローバルに追加されてしまう。通常の呼出しはGOTを経由しないので油断してたらこういうケースがあるのを忘れていた。。

Wasm的には

 (import "env" "wasmlinux_syscall32" (func $wasmlinux_syscall32 (param i32 i32) (result i32)))
 (global $__stack_pointer (mut i32) (i32.const 66592))
 (global $GOT.data.internal.__memory_base i32 (i32.const 0))
 (global $GOT.data.internal.__table_base i32 (i32.const 1))
 (global $GOT.func.internal.wasmlinux_syscall32 i32 (i32.const 2))

のようになる。 import は普通に行われるので、通常の呼出しはGOTを経由する必要はない。このため、GOT経由の参照が生成されたら常に失敗させるのが簡単。。だと思う。一般には単にリンカが生成してきた .wasm をリライトして消し込みを行えば良いが、そもそも他所の共有ライブラリの関数ポインタを取得するというのがかなりレアケースなのではないだろうか。。