Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Embedding WebAssembly in Rust

Embedding WebAssembly in Rust

下町.rs の資料です。
https://shitamachi.connpass.com/event/173972/

Avatar for Taiga Merlin

Taiga Merlin

April 28, 2020
Tweet

More Decks by Taiga Merlin

Other Decks in Technology

Transcript

  1. Agenda • Introduction • WebAssembly overview • WebAssembly ABI •

    WebAssembly toolchain • Embedding WebAssembly into Rust @TaigaMerlin
  2. I like binaries 00000000 00 61 73 6d 01 00

    00 00 01 6a 10 60 02 7f 7f 01 |.asm.....j.`....| 00000010 7f 60 02 7f 7f 00 60 01 7f 00 60 03 7f 7f 7f 01 |.`....`...`.....| 00000020 7f 60 03 7f 7f 7f 00 60 01 7f 01 7f 60 04 7f 7f |.`.....`....`...| 00000030 7f 7f 00 60 00 00 60 01 7f 01 7e 60 00 01 7f 60 |...`..`...~`...`| 00000040 04 7f 7f 7f 7f 01 7f 60 05 7f 7f 7f 7f 7f 00 60 |.......`.......`| 00000050 05 7f 7f 7f 7f 7f 01 7f 60 06 7f 7f 7f 7f 7f 7f |........`.......| 00000060 01 7f 60 07 7f 7f 7f 7f 7f 7f 7f 01 7f 60 03 7e |..`..........`.~| 00000070 7f 7f 01 7f 02 eb 01 06 16 77 61 73 69 5f 73 6e |.........wasi_sn| 00000080 61 70 73 68 6f 74 5f 70 72 65 76 69 65 77 31 09 |apshot_preview1.| 00000090 70 72 6f 63 5f 65 78 69 74 00 02 16 77 61 73 69 |proc_exit...wasi| 000000a0 5f 73 6e 61 70 73 68 6f 74 5f 70 72 65 76 69 65 |_snapshot_previe| 000000b0 77 31 08 66 64 5f 77 72 69 74 65 00 0a 16 77 61 |w1.fd_write...wa| 000000c0 73 69 5f 73 6e 61 70 73 68 6f 74 5f 70 72 65 76 |si_snapshot_prev| 000000d0 69 65 77 31 0e 66 64 5f 70 72 65 73 74 61 74 5f |iew1.fd_prestat_| 000000e0 67 65 74 00 00 16 77 61 73 69 5f 73 6e 61 70 73 |get...wasi_snaps| 000000f0 68 6f 74 5f 70 72 65 76 69 65 77 31 13 66 64 5f |hot_preview1.fd_| 00000100 70 72 65 73 74 61 74 5f 64 69 72 5f 6e 61 6d 65 |prestat_dir_name| 00000110 00 03 16 77 61 73 69 5f 73 6e 61 70 73 68 6f 74 |...wasi_snapshot| @TaigaMerlin
  3. WebAssembly ABIs • ABI: Application Binary Interface • What functions

    does the host provide? (what can be imported?) • What functions should the module expose? (what should be exported?) • WASM modules are sandboxed ◦ do not have access to stdout ◦ nothing runs until called upon • Commonly used ◦ Emscripten (POSIX-like) => wasm32-unknown-emscripten ◦ WASI (WebAssembly System Interface, capability based) => wasm32-wasi ▪ Officially supported since Rust 1.36
  4. WASI Imports and Exports $ wasm-objdump -x target/wasm32-wasi/release/shitamachi-rs.rustc.wasm [...] Import[6]:

    - func[0] sig=1 <__wasi_proc_exit> <- wasi_snapshot_preview1.proc_exit - func[1] sig=9 <_ZN4wasi13lib_generated22wasi_snapshot_preview18fd_write17h62539b3299a4581fE> <- wasi_snapshot_preview1.fd_write - func[2] sig=3 <__wasi_fd_prestat_get> <- wasi_snapshot_preview1.fd_prestat_get - func[3] sig=8 <__wasi_fd_prestat_dir_name> <- wasi_snapshot_preview1.fd_prestat_dir_name - func[4] sig=3 <__wasi_environ_sizes_get> <- wasi_snapshot_preview1.environ_sizes_get - func[5] sig=3 <__wasi_environ_get> <- wasi_snapshot_preview1.environ_get Export[6]: - memory[0] -> "memory" - func[163] <_start> -> "_start" - func[150] <__original_main> -> "__original_main" - func[190] <main> -> "main" - global[1] -> "__data_end" - global[2] -> "__heap_base" [...]
  5. Let’s make it more interesting • Wasmer is an embeddable

    WASM runtime • We can write our own Rust program that will run WASM code fn main() -> error::Result<()> { let wasm_bytes = include_bytes!("my-module.wasm"); let import_object = imports! {}; let instance = instantiate(wasm_bytes, &import_object)?; instance.call("my_function", &[])?; Ok(()) }
  6. Let’s fake implement the required WASI functions! let wasm_bytes =

    include_bytes!("print.wasm"); let import_object = imports! { "wasi_snapshot_preview1" => { "proc_exit" => func!(wasi_proc_exit), "fd_write" => func!(wasi_fd_write), "fd_prestat_get" => func!(wasi_fd_prestat_get), "fd_prestat_dir_name" => func!(wasi_fd_prestat_dir_name), "environ_sizes_get" => func!(wasi_environ_sizes_get), "environ_get" => func!(wasi_environ_get), }, }; let instance = instantiate(wasm_bytes, &import_object)?; instance.call("_start", &[])?;
  7. With some hacks like this... fn wasi_fd_prestat_get(_ctx: &mut Ctx, _fd:

    u32, _ptr: WasmPtr<__wasi_prestat_t>) -> u16 { println!("fd_prestat_get"); return __WASI_EBADF; } fn wasi_fd_prestat_dir_name( _ctx: &mut Ctx, _fd: u32, _path: WasmPtr<u8, Array>, _path_len: u32, ) -> u16 { println!("fd_prestat_dir_name"); return __WASI_EBADF; }
  8. And more late night hacks... fn wasi_fd_write(ctx: &mut Ctx, fd:

    u32, iovs: WasmPtr<__wasi_ciovec_t, Array>, iovs_len: u32, nwritten: WasmPtr<u32> ) -> u32 { let memory = ctx.memory(0); let iovs_ptr = iovs.deref(memory, 0, iovs_len).unwrap(); let nwritten_ptr = nwritten.deref(memory).unwrap(); let total_len = iovs_ptr.iter().map(|iov| iov.get().buf_len).fold(0, |acc, x| acc + x); let data_arr = iovs_ptr.iter().map(|iov| get_array(&*ctx, iov.get())) .flat_map(|s| s.into_iter()).collect::<Vec<u8>>(); let data_str = std::str::from_utf8(data_arr.as_slice()).unwrap(); println!("wasi_fd_write fd={} byte_len={} bytes={}", fd, total_len, data_str); nwritten_ptr.set(total_len); return 0; }
  9. And we can see the WASI syscalls! $ cargo run

    Compiling wasmer-test v0.1.0 (/code/wasmer-test) Finished dev [unoptimized + debuginfo] target(s) in 2.60s Running `target/debug/wasmer-test` environ_sizes_get fd_prestat_get wasi_fd_write fd=1 byte_len=11 bytes= hello world • It’s printing to stdout via a call into a host-provided fd_write() function • WASI syscalls allow modules to access restricted resources
  10. • We are going to run JavaScript in the browser!

    • Via a JavaScript interpreter compiled to WASM • Running on top of the V8 engine • https://webassembly.sh/?run-command=quickjs Let’s take a look at QuickJS on webassembly.sh
  11. Wrapping up • WASI is still highly experimental and not

    stable • Rust has full WASI support built into the toolchain • The inner workings of WASM/WASI are quite simple • Rust x WASI has many interesting cross-platform use cases @TaigaMerlin