rustwebassemblyrust-wasmwasmtime

Can't link to preview1 functions in rust wasmtime


I'm trying to write some WASM that can read from the local filesystem using WASI. (using wasmtime) But every time I try, I get a runtime error that says:

Error: unknown import: wasi_snapshot_preview1::fd_filestat_get has not been defined

Both the WASM and the wasmtime code are in rust:

use std::fs;

#[unsafe(no_mangle)]
pub fn get_file() -> u32 {
    let message = fs::read_to_string("test.txt");
    return match message {
        Ok(_content) => 0,
        Err(_e) => 1,
    };
}

This should try to read in the test.txt file, and return an integer to the outer code with whether it succeeded or not.

This is compiled into WASM with:

cargo build --target wasm32-wasip1

And then converted to WAT:

wasm2wat wasmcode.wasm > watcode.wat

I'm using wasmtime as my WASM runtime. It looks like:

use std::fs;
use anyhow::Result;
use wasmtime::Linker;
use wasmtime::*;
use wasmtime_wasi::p2::WasiCtxBuilder;
use wasmtime_wasi::preview1::WasiP1Ctx;
use wasmtime_wasi::{DirPerms, FilePerms};

fn main() -> Result<()> {
    let wat = fs::read_to_string("watcode.wat");
    let wasi_ctx: WasiP1Ctx = WasiCtxBuilder::new()
        .preopened_dir(".", "/", DirPerms::READ, FilePerms::READ)?
        .build_p1();
    let config = Config::new();
    let engine: Engine = Engine::new(&config)?;
    let mut store = Store::new(&engine, wasi_ctx);
    let mut linker = Linker::new(&engine);
    let module: Module = Module::new(&engine, wat)?;
    let instance = linker.instantiate(&mut store, &module)?;
    wasmtime_wasi::preview1::add_to_linker_sync(&mut linker, |s| s)?;

    let get_file = instance.get_typed_func::<(), u32>(&mut store, "get_file")?;
    let error_code = get_file.call(&mut store, ())?;
    println!("get_file returned: {}", error_code);

    Ok(())
}

The error clearly is telling me that the WASM code is trying to link to fd_filestat_get, and it can't be found. But why not? I'm building the linker with build_p1 and then linking it with add_to_linker_sync. I'm new to Rust, but not sure what I'm missing here. Thanks!


Solution

  • The add_to_linker_sync call is in the wrong place. It has to come before the linker is instantiated and the module is created. So the working code looks like:

    use std::fs;
    use anyhow::Result;
    use wasmtime::Linker;
    use wasmtime::*;
    use wasmtime_wasi::p2::WasiCtxBuilder;
    use wasmtime_wasi::preview1::WasiP1Ctx;
    use wasmtime_wasi::{DirPerms, FilePerms};
    
    fn main() -> Result<()> {
        let wat = fs::read_to_string("watcode.wat");
        let wasi_ctx: WasiP1Ctx = WasiCtxBuilder::new()
            .preopened_dir(".", "/", DirPerms::READ, FilePerms::READ)?
            .build_p1();
        let config = Config::new();
        let engine: Engine = Engine::new(&config)?;
        let mut store = Store::new(&engine, wasi_ctx);
        let mut linker = Linker::new(&engine);
        wasmtime_wasi::preview1::add_to_linker_sync(&mut linker, |s| s)?; // This line has moved
        let module: Module = Module::new(&engine, wat)?;
        let instance = linker.instantiate(&mut store, &module)?;
    
        let get_file = instance.get_typed_func::<(), u32>(&mut store, "get_file")?;
        let error_code = get_file.call(&mut store, ())?;
        println!("get_file returned: {}", error_code);
    
        Ok(())
    }
    

    Annoying that it just silently fails in this instance.