Wastime: `#[!no_std]` Support

在 v21.0.0 的 Release 版本中,wasmtime 包允许在 #![no_std] 的平台构建,但是只支持编译过程中的 Cargo 特性,目前支持的 Cargo 特性有:runtime, gc, component-model

官方提供了 Example 来表明如何构建这样的 wasmtime

Example 分析

min_platform/embedding

embedding 目录下 wasmtime-platfrom.c 具体实现了如何进行 std 操作,这些符号以 wasmtime_ 开头以防止符号冲突,包括 memmove, memset, memcmp, bcmp, __tls_get_addr

通过 build.sh 下的

cbindgen ../../crates/wasmtime/src/runtime/vm/sys/custom/capi.rs \
    --config embedding/cbindgen.toml > embedding/wasmtime-platform.h
clang -shared -O2 -o libwasmtime-platform.so ./embedding/wasmtime-platform.c \
  -D_GNU_SOURCE

将其生成为一个动态链接库。

srclib.rs 中暴露出了一个 run 符号,分别接受各个参数的地址和长度,接收后通过 core::slice::from_raw_parts 恢复数组,然后通过 run_result 进行传参和执行,可以看见每个函数都接受一个AOT编译后生成的 &[u8]cwasm代码 :

#[no_mangle]
pub unsafe extern "C" fn run(
    error_buf: *mut u8,
    error_size: usize,
    smoke_module: *const u8,
    smoke_size: usize,
    simple_add_module: *const u8,
    simple_add_size: usize,
    simple_host_fn_module: *const u8,
    simple_host_fn_size: usize,
) -> usize {
    let buf = core::slice::from_raw_parts_mut(error_buf, error_size);
    let smoke = core::slice::from_raw_parts(smoke_module, smoke_size);
    let simple_add = core::slice::from_raw_parts(simple_add_module, simple_add_size);
    let simple_host_fn = core::slice::from_raw_parts(simple_host_fn_module, simple_host_fn_size);
    match run_result(smoke, simple_add, simple_host_fn) {
        Ok(()) => 0,
        Err(e) => {
            let msg = format!("{e:?}");
            let len = buf.len().min(msg.len());
            buf[..len].copy_from_slice(&msg.as_bytes()[..len]);
            len
        }
    }
}

fn run_result(
    smoke_module: &[u8],
    simple_add_module: &[u8],
    simple_host_fn_module: &[u8],
) -> Result<()> {
    smoke(smoke_module)?;
    simple_add(simple_add_module)?;
    simple_host_fn(simple_host_fn_module)?;
    Ok(())
}

fn smoke(module: &[u8]) -> Result<()> {
    let engine = Engine::default();
    let module = unsafe { Module::deserialize(&engine, module)? };
    Instance::new(&mut Store::new(&engine, ()), &module, &[])?;
    Ok(())
}

fn simple_add(module: &[u8]) -> Result<()> {
    let engine = Engine::default();
    let module = unsafe { Module::deserialize(&engine, module)? };
    let mut store = Store::new(&engine, ());
    let instance = Linker::new(&engine).instantiate(&mut store, &module)?;
    let func = instance.get_typed_func::<(u32, u32), u32>(&mut store, "add")?;
    assert_eq!(func.call(&mut store, (2, 3))?, 5);
    Ok(())
}

然后通过 build.sh 下的:

cargo build \
  --manifest-path embedding/Cargo.toml \
  --target $target \
  --release
cc \
  -Wl,--gc-sections \
  -Wl,--whole-archive \
  ../../target/$target/release/libembedding.a \
  -Wl,--no-whole-archive \
  -shared \
  -o libembedding.so

把用户的函数实现生成了动态连接。

min_platform/src

main.rs 中展示了如何加载用户函数、传递AOT代码、加载platform符号:

let lib = format!("./libembedding.so");
/* Compile to AOT ... */
let _platform_symbols =
            Library::open(Some("./libwasmtime-platform.so"), RTLD_NOW | RTLD_GLOBAL)?;

let lib = Library::new(&lib)?;

let run: Symbol<
            extern "C" fn(
                *mut u8,
                usize,
                *const u8,
                usize,
                *const u8,
                usize,
                *const u8,
                usize,
            ) -> usize,
        > = lib.get(b"run")?;

let mut error_buf = Vec::with_capacity(1024);
let len = run(
    error_buf.as_mut_ptr(),
    error_buf.capacity(),
    smoke.as_ptr(),
    smoke.len(),
    simple_add.as_ptr(),
    simple_add.len(),
    simple_host_fn.as_ptr(),
    simple_host_fn.len(),
);
println!("len: {}", len);
error_buf.set_len(len);

std::io::stderr().write_all(&error_buf).unwrap();

Usage

首先需要安装 v21.0.0 版本的 wasmtime 否则会出现不兼容问题:

wget https://wasmtime.dev/install.sh
./install.sh --version v21.0.0

error occurred: Command “/opt/wasi-sdk/bin/clang” “–sysroot=/opt/wasi-sdk/share/wasi-sysroot” “-O0” “-ffunction-sections” “-fdata-sections” “-fPIC” “-gdwarf-4” “-fno-omit-frame-pointer” “-m64” “–target=x86_64-unknown-linux-gnu” “-Wall” “-Wextra” “-DCFG_TARGET_OS_linux” “-DCFG_TARGET_ARCH_x86_64” “-DVERSIONED_SUFFIX=_21_0_1” “-o” “/home/cyc/Projects/alloystack/user/wasmtime_helloworld/target/debug/build/wasmtime-7d89a82a5840f40a/out/07c0bb04dd2aaef1-helpers.o” “-c” “src/runtime/vm/helpers.c” with args clang did not execute successfully (status code exit status: 1).


Last modified on 2024-10-03