1 #![no_std] 2 3 #[macro_use] 4 extern crate alloc; 5 6 use alloc::string::ToString; 7 use anyhow::{Result, ensure}; 8 use core::ptr; 9 use wasmtime::{Config, Engine, Instance, Linker, Module, Store}; 10 11 mod allocator; 12 mod panic; 13 14 #[cfg(feature = "wasi")] 15 mod 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)] 23 pub 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 54 fn 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 67 fn 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 existed 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 108 fn 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 118 fn 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 131 fn 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 146 fn 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 159 fn 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 } 194