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