Write and Run

it's a simple way, but the only way.

WASIを叩くWASMを手書きしてLucetでHello, world!する

WASI がなんだとか WASM がなんだとかは詳しく説明しません。各位やって。

まず Lucet を入れる。

github.com

はい

Hello, world! するには fd_write があればよさそう。

wasmtime/WASI-api.md at master · CraneStation/wasmtime · GitHub

引数はだいたい見りゃわかる。が、__wasi_ciovec_t っつーのが引っかかる。

とはいえこれも見りゃわかる。

wasmtime/WASI-api.md at master · CraneStation/wasmtime · GitHub

バッファへのポインタとバッファの長さを持ってる構造体。

で、fd_write にはそいつのポインタを渡してやればいいということがわかる。

というところまでわかったら WAT を書く。S 式。

(module
  (import "wasi_unstable" "fd_write" (func $__wasi_fd_write (param i32 i32 i32 i32) (result i32)))
  (func $_start
    i32.const 1
    i32.const 16
    i32.const 13
    i32.const 24
    call $__wasi_fd_write
    drop)
  (memory 1)
  (data (i32.const 0) "Hello, world!\00\00\00\00\00\00\00\0d\00\00\00")
  (export "_start" (func $_start)))

import ってやつでリンクをやってて、data でメモリの初期値を埋めてる。export はエントリポイントの公開。

で、data に埋まってる初期値が大切で、ここに

"Hello, world!"(13bytes) + padding[0x00 0x00 0x00](3bytes) + buf address[0x00 0x00 0x00 0x00] (4bytes = i32) + buf length[0x0d 0x00 0x00 0x00](4bytes = i32)

が埋まっている。

buf address と buf length ってのが __wasi_ciovec_t の中身。

ので、スタックに

1  ;; stdout の fd
16 ;; buf address へのアドレス
13 ;; "Hello, world!" のバイト数。buf length とは別にこれも必要らしい
24 ;; `nwritten` へのアドレスっぽい。戻り値はスタックに返ってくるんじゃねーのかよ(スタックにも返ってくるんだよな)

というかんじで積んでやって、call $__wasi_fd_write してやる。

最後、スタックに nwritten と思しき値が残ってしまうのでそいつを drop で消し飛ばせば完了。

hello.wat みたいな名前で保存してやって、以下のように実行できる。

lucetc-wasi -o hello.so hello.wat
lucet-wasi hello.so

楽しいね