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)] 22 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 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 66 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 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 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 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 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 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