在 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
将其生成为一个动态链接库。
在 src
下 lib.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