Path: blob/main/examples/min-platform/embedding/src/lib.rs
2450 views
#![no_std]12#[macro_use]3extern crate alloc;45use alloc::string::ToString;6use anyhow::{Result, ensure};7use core::ptr;8use wasmtime::{Config, Engine, Instance, Linker, Module, Store};910mod allocator;11mod panic;1213#[cfg(feature = "wasi")]14mod wasi;1516/// Entrypoint of this embedding.17///18/// This takes a number of parameters which are the precompiled module AOT19/// images that are run for each of the various tests below. The first parameter20/// is also where to put an error string, if any, if anything fails.21#[unsafe(no_mangle)]22pub unsafe extern "C" fn run(23error_buf: *mut u8,24error_size: usize,25smoke_module: *const u8,26smoke_size: usize,27simple_add_module: *const u8,28simple_add_size: usize,29simple_host_fn_module: *const u8,30simple_host_fn_size: usize,31simple_floats_module: *const u8,32simple_floats_size: usize,33) -> usize {34unsafe {35let buf = core::slice::from_raw_parts_mut(error_buf, error_size);36let smoke = core::slice::from_raw_parts(smoke_module, smoke_size);37let simple_add = core::slice::from_raw_parts(simple_add_module, simple_add_size);38let simple_host_fn =39core::slice::from_raw_parts(simple_host_fn_module, simple_host_fn_size);40let simple_floats = core::slice::from_raw_parts(simple_floats_module, simple_floats_size);41match run_result(smoke, simple_add, simple_host_fn, simple_floats) {42Ok(()) => 0,43Err(e) => {44let msg = format!("{e:?}");45let len = buf.len().min(msg.len());46buf[..len].copy_from_slice(&msg.as_bytes()[..len]);47len48}49}50}51}5253fn run_result(54smoke_module: &[u8],55simple_add_module: &[u8],56simple_host_fn_module: &[u8],57simple_floats_module: &[u8],58) -> Result<()> {59smoke(smoke_module)?;60simple_add(simple_add_module)?;61simple_host_fn(simple_host_fn_module)?;62simple_floats(simple_floats_module)?;63Ok(())64}6566fn config() -> Config {67let mut config = Config::new();68let _ = &mut config;6970#[cfg(target_arch = "x86_64")]71{72// This example runs in a Linux process where it's valid to use73// floating point registers. Additionally sufficient x86 features are74// enabled during compilation to avoid float-related libcalls. Thus75// despite the host being configured for "soft float" it should be76// valid to turn this on.77unsafe {78config.x86_float_abi_ok(true);79}8081// To make the float ABI above OK it requires CPU features above82// baseline to be enabled. Wasmtime needs to be able to check to ensure83// that the feature is actually supplied at runtime, but a default check84// isn't possible in no_std. For x86_64 we can use the cpuid instruction85// bound through an external crate.86//87// Note that CPU support for these features has existed since 201388// (Haswell) on Intel chips and 2012 (Piledriver) on AMD chips.89unsafe {90config.detect_host_feature(move |feature| {91let id = raw_cpuid::CpuId::new();92match feature {93"sse3" => Some(id.get_feature_info()?.has_sse3()),94"ssse3" => Some(id.get_feature_info()?.has_sse3()),95"sse4.1" => Some(id.get_feature_info()?.has_sse41()),96"sse4.2" => Some(id.get_feature_info()?.has_sse42()),97"fma" => Some(id.get_feature_info()?.has_fma()),98_ => None,99}100});101}102}103104config105}106107fn smoke(module: &[u8]) -> Result<()> {108let engine = Engine::new(&config())?;109let module = match deserialize(&engine, module)? {110Some(module) => module,111None => return Ok(()),112};113Instance::new(&mut Store::new(&engine, ()), &module, &[])?;114Ok(())115}116117fn simple_add(module: &[u8]) -> Result<()> {118let engine = Engine::new(&config())?;119let module = match deserialize(&engine, module)? {120Some(module) => module,121None => return Ok(()),122};123let mut store = Store::new(&engine, ());124let instance = Linker::new(&engine).instantiate(&mut store, &module)?;125let func = instance.get_typed_func::<(u32, u32), u32>(&mut store, "add")?;126ensure!(func.call(&mut store, (2, 3))? == 5);127Ok(())128}129130fn simple_host_fn(module: &[u8]) -> Result<()> {131let engine = Engine::new(&config())?;132let module = match deserialize(&engine, module)? {133Some(module) => module,134None => return Ok(()),135};136let mut linker = Linker::<()>::new(&engine);137linker.func_wrap("host", "multiply", |a: u32, b: u32| a.saturating_mul(b))?;138let mut store = Store::new(&engine, ());139let instance = linker.instantiate(&mut store, &module)?;140let func = instance.get_typed_func::<(u32, u32, u32), u32>(&mut store, "add_and_mul")?;141ensure!(func.call(&mut store, (2, 3, 4))? == 10);142Ok(())143}144145fn simple_floats(module: &[u8]) -> Result<()> {146let engine = Engine::new(&config())?;147let module = match deserialize(&engine, module)? {148Some(module) => module,149None => return Ok(()),150};151let mut store = Store::new(&engine, ());152let instance = Linker::new(&engine).instantiate(&mut store, &module)?;153let func = instance.get_typed_func::<(f32, f32), f32>(&mut store, "frob")?;154ensure!(func.call(&mut store, (1.4, 3.2))? == 5.);155Ok(())156}157158fn deserialize(engine: &Engine, module: &[u8]) -> Result<Option<Module>> {159let result = if cfg!(feature = "custom") {160// If a custom virtual memory system is in use use the raw `deserialize`161// API to let Wasmtime handle publishing the executable and such.162unsafe { Module::deserialize(engine, module) }163} else {164// NOTE: deserialize_raw avoids creating a copy of the module code. See165// the safety notes before using in your embedding.166//167// Also note that this will only work for native code with a custom code168// publisher which isn't configured in this example. Such custom code169// publisher will need to handle making this executable for example.170let memory_ptr = ptr::slice_from_raw_parts(module.as_ptr(), module.len());171let module_memory = ptr::NonNull::new(memory_ptr.cast_mut()).unwrap();172unsafe { Module::deserialize_raw(engine, module_memory) }173};174match result {175Ok(module) => Ok(Some(module)),176Err(e) => {177// Currently if custom signals/virtual memory are disabled then this178// example is expected to fail to load since loading native code179// requires virtual memory. In the future this will go away as when180// signals-based-traps is disabled then that means that the181// interpreter should be used which should work here.182if !cfg!(feature = "custom")183&& e.to_string()184.contains("requires virtual memory to be enabled")185{186Ok(None)187} else {188Err(e)189}190}191}192}193194195