embedding/
lib.rs

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