The introduction to WASI, the practical example, and the key benefits of using WASI with WebAssembly.
2024-09-29T00:00:00+0100
The introduction to WASI, the practical example, and the key benefits of using WASI with WebAssembly.
Some functionalities are more difficult to achieve in WebAssembly due to imposed security goals. The language does not rely on the security mechanisms of the host system, as is the case with most others. Therefore, access to the file system, memory, and other resources must be explicitly allowed. This excludes many unrecognized attack vectors.
WebAssembly was also developed to make code portable, and thanks to WASI, it becomes a portable application.
In the browser, there is a standardized environment, and APIs can always be passed to WebAssembly modules in the same way for internal use. On the operating system, certain functionality is expected from the module itself, provided the permission is available. An additional layer is needed, which was created by WASI.
Create a new project:
$ cargo new --bin hello-wasi
Created binary (application) `hello-wasi` package
The main.rs
file in the src
directory looks
something like this:
fn main() {
println!("Hello, WASI!");
}
Build:
$ cd hello-wasi/
$ cargo build --release
Compiling hello-wasi v0.1.0 (...)
Finished release [optimized] target(s) in 0.42s
Run:
$ cargo run --release
Finished release [optimized] target(s) in 0.00s
Running `target/release/hello-wasi`
Hello, WASI!
Because Rust is LLVM-based, a binary for the current operating system has now been created. However, this binary doesn’t work on another operating system.
Try to run a WebAssembly module directly. We’ll use wasm3, but the same can be done with other outside-the-browser WebAssembly runtimes (e.g., wasmtime, wasmer, …).
If Rust in WebAssembly is new to you or you’re more interested in the topic, I recommend consulting my older posts: * Simple Rust Program in WebAssembly: Simple guide on how to use Rust in a web application
Build:
$ cargo build --target wasm32-unknown-unknown --release
Compiling hello-wasi v0.1.0 (...)
Finished release [optimized] target(s) in 0.25s
Run:
$ wasm3 target/wasm32-unknown-unknown/release/hello-wasi.wasm
Error: function lookup failed ('_start')
The WebAssembly module cannot be executed directly. It lacks the
correct structure, such as the _start
function here, to be
executed by the runtime.
Build with WASI backend:
$ cargo build --target wasm32-wasi --release
Compiling hello-wasi v0.1.0 (...)
Finished release [optimized] target(s) in 0.16s
Run:
$ wasm3 target/wasm32-wasi/release/hello-wasi.wasm
Hello, WASI!
Analyze:
$ wasm-objdump -x target/wasm32-wasi/release/hello-wasi.wasm
hello-wasi.wasm: file format wasm 0x1
Section Details:
Type[18]:
- type[0] () -> nil
- type[1] (i32) -> nil
- type[2] (i32, i32) -> nil
- type[3] (i32) -> i32
- type[4] (i32, i32) -> i32
....
Import[4]:
- func[0] sig=7 <_ZN4wasi13lib_generated22wasi_snapshot_preview18fd_write17haeb8e9d471da6707E> <- wasi_snapshot_preview1.fd_write
- func[1] sig=4 <__imported_wasi_snapshot_preview1_environ_get> <- wasi_snapshot_preview1.environ_get
- func[2] sig=4 <__imported_wasi_snapshot_preview1_environ_sizes_get> <- wasi_snapshot_preview1.environ_sizes_get
- func[3] sig=1 <__imported_wasi_snapshot_preview1_proc_exit> <- wasi_snapshot_preview1.proc_exit
Function[204]:
- func[4] sig=0 <__wasm_call_ctors>
- func[5] sig=0 <_start>
- func[6] sig=1 <_ZN3std10sys_common9backtrace28__rust_begin_short_backtrace17h90275821610c977eE>
- func[7] sig=3 <_ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17h077ef646d308a78dE>
- func[8] sig=3 <_ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17h7be2897f107f95f6E>
- func[9] sig=1 <_ZN4core3ptr85drop_in_place$LT$std..rt..lang_start$LT$$LP$$RP$$GT$..$u7b$$u7b$closure$u7d$$u7d$$GT$17hd02ca6a3ba2a3b9bE>
- func[10] sig=0 <_ZN10hello_wasi4main17h913399cd9566c9a2E>
- func[11] sig=8 <__main_void>
- func[12] sig=4 <__rust_alloc>
- func[13] sig=5 <__rust_dealloc>
- func[14] sig=7 <__rust_realloc>
- func[15] sig=2 <__rust_alloc_error_handler>
...
Table[1]:
- table[0] type=funcref initial=75 max=75
Memory[1]:
- memory[0] pages: initial=17
Global[1]:
- global[0] i32 mutable=1 <__stack_pointer> - init i32=1048576
Export[3]:
- memory[0] -> "memory"
- func[5] <_start> -> "_start"
- func[11] <__main_void> -> "__main_void"
Elem[1]:
- segment[0] flags=0 table=0 count=74 - init i32=1
- elem[1] = func[10] <_ZN10hello_wasi4main17h913399cd9566c9a2E>
...
Code[204]:
- func[4] size=2 <__wasm_call_ctors>
- func[5] size=68 <_start>
- func[6] size=11 <_ZN3std10sys_common9backtrace28__rust_begin_short_backtrace17h90275821610c977eE>
- func[7] size=15 <_ZN3std2rt10lang_start28_$u7b$$u7b$closure$u7d$$u7d$17h077ef646d308a78dE>
- func[8] size=15 <_ZN4core3ops8function6FnOnce40call_once$u7b$$u7b$vtable.shim$u7d$$u7d$17h7be2897f107f95f6E>
- func[9] size=2 <_ZN4core3ptr85drop_in_place$LT$std..rt..lang_start$LT$$LP$$RP$$GT$..$u7b$$u7b$closure$u7d$$u7d$$GT$17hd02ca6a3ba2a3b9bE>
- func[10] size=82 <_ZN10hello_wasi4main17h913399cd9566c9a2E>
- func[11] size=70 <__main_void>
- func[12] size=19 <__rust_alloc>
- func[13] size=15 <__rust_dealloc>
- func[14] size=23 <__rust_realloc>
...
Data[2]:
- segment[0] <.rodata> memory=0 size=7192 - init i32=1048576
- 0100000: 0200 0000 0400 0000 0400 0000 0300 0000 ................
- 0100010: 0400 0000 0400 0000 4865 6c6c 6f2c 2057 ........Hello, W
- 0100020: 4153 4921 0a00 0000 1800 1000 0d00 0000 ASI!............
- 0100030: 2829 0000 0f00 0000 0400 0000 0400 0000 ()..............
...
Custom:
- name: ".debug_abbrev"
...
Without going too deep, you can now see the desired _start function in the export. In addition, the WASI compiler provides a standard for WebAssembly modules to import functionality that the module itself wants to use. More details on this in the WASI Tutorial by wasmtime.
As a tip, Rust-based WASI applications can be developed directly with cargo
Install:
$ cargo install cargo-wasi
Updating crates.io index
Downloaded cargo-wasi v0.1.28
Downloaded 1 crate (13.9 KB) in 1.03s
Installing cargo-wasi v0.1.28
Updating crates.io index
Downloaded cargo-wasi-exe-x86_64-unknown-linux-musl v0.1.28
Downloaded 1 crate (5.4 MB) in 3.12s
Compiling cfg-if v1.0.0
Compiling cargo-wasi-exe-x86_64-unknown-linux-musl v0.1.28
Compiling cargo-wasi v0.1.28
Finished release [optimized] target(s) in 9.85s
Installing /home/progressio/.cargo/bin/cargo-wasi
Installed package `cargo-wasi v0.1.28` (executable `cargo-wasi`)
Run:
$ cargo wasi run
Compiling hello-wasi v0.1.0 (/home/progressio/Nextcloud/marco-private/Git/Forschung/public_doc/essays/14_hello_wasi/hello-wasi)
Finished dev [unoptimized + debuginfo] target(s) in 0.06s
Running `/home/progressio/.cargo/bin/cargo-wasi target/wasm32-wasi/debug/hello-wasi.wasm`
Running `target/wasm32-wasi/debug/hello-wasi.wasm`
Hello, WASI!
Compilation to WASI code is offered for various programming languages. As an example for C/C++, wasienv.
WASI applications can be run on various platforms, including Node.js, Deno, Browser, etc.
I am happy to clarify, expand, or correct the article. Write feedback or contact me directly.
Created by Marco Kuoni, September 2024