Next Generation WASM Microkernel Operating System
at main 208 lines 7.4 kB view raw
1// Copyright 2025 Jonas Kruckenberg 2// 3// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or 4// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or 5// http://opensource.org/licenses/MIT>, at your option. This file may not be 6// copied, modified, or distributed except according to those terms. 7 8use alloc::string::{String, ToString}; 9use alloc::sync::Arc; 10use core::mem; 11use core::ops::DerefMut; 12use core::ptr::NonNull; 13 14use wasmparser::{Validator, WasmFeatures}; 15 16use crate::wasm::Engine; 17use crate::wasm::code_registry::{register_code, unregister_code}; 18use crate::wasm::compile::{CompileInputs, CompiledFunctionInfo}; 19use crate::wasm::indices::{DefinedFuncIndex, EntityIndex, VMSharedTypeIndex}; 20use crate::wasm::translate::{Import, ModuleTranslator, TranslatedModule}; 21use crate::wasm::type_registry::RuntimeTypeCollection; 22use crate::wasm::utils::u8_size_of; 23use crate::wasm::vm::{CodeObject, MmapVec, VMArrayCallFunction, VMShape, VMWasmCallFunction}; 24 25/// A compiled WebAssembly module, ready to be instantiated. 26/// 27/// It holds all compiled code as well as the module's type information and other metadata (e.g. for 28/// trap handling and backtrace information). 29/// 30/// Currently, no form of dynamic tiering is implemented instead all functions are compiled synchronously 31/// when the module is created. This is expected to change though. 32#[derive(Debug, Clone)] 33pub struct Module(Arc<ModuleInner>); 34 35#[derive(Debug)] 36struct ModuleInner { 37 name: String, 38 engine: Engine, 39 translated_module: TranslatedModule, 40 required_features: WasmFeatures, 41 vmshape: VMShape, 42 code: Arc<CodeObject>, 43 type_collection: RuntimeTypeCollection, 44} 45 46impl Drop for ModuleInner { 47 fn drop(&mut self) { 48 tracing::warn!("Dropping wasm module {}", self.name); 49 unregister_code(&self.code); 50 } 51} 52 53impl Module { 54 pub fn from_bytes( 55 engine: &Engine, 56 validator: &mut Validator, 57 bytes: &[u8], 58 ) -> crate::Result<Self> { 59 let (mut translation, types) = ModuleTranslator::new(validator).translate(bytes)?; 60 61 tracing::debug!("Gathering compile inputs..."); 62 let function_body_data = mem::take(&mut translation.function_bodies); 63 let inputs = CompileInputs::from_module(&translation, &types, function_body_data); 64 65 tracing::debug!("Compiling inputs..."); 66 let unlinked_outputs = inputs.compile(engine.compiler())?; 67 68 let type_collection = engine.type_registry().register_module_types(engine, types); 69 70 let code = crate::mem::with_kernel_aspace(|aspace| -> crate::Result<_> { 71 tracing::debug!("Applying static relocations..."); 72 let mut code = 73 unlinked_outputs.link_and_finish(engine, &translation.module, |code| { 74 tracing::debug!("Allocating new memory map..."); 75 MmapVec::from_slice(aspace.clone(), &code) 76 })?; 77 78 code.publish(aspace.lock().deref_mut())?; 79 Ok(Arc::new(code)) 80 })?; 81 82 // register this code memory with the trap handler, so we can correctly unwind from traps 83 register_code(&code); 84 85 Ok(Self(Arc::new(ModuleInner { 86 name: translation 87 .module 88 .name 89 .clone() 90 .unwrap_or("<unnamed mystery module>".to_string()), 91 engine: engine.clone(), 92 vmshape: VMShape::for_module(u8_size_of::<*mut u8>(), &translation.module), 93 translated_module: translation.module, 94 required_features: translation.required_features, 95 code, 96 type_collection, 97 }))) 98 } 99 100 /// Returns the modules name if present. 101 pub fn name(&self) -> Option<&str> { 102 self.translated().name.as_deref() 103 } 104 105 /// Returns the modules imports. 106 pub fn imports(&self) -> impl ExactSizeIterator<Item = &Import> { 107 self.translated().imports.iter() 108 } 109 110 /// Returns the modules exports. 111 pub fn exports(&self) -> impl ExactSizeIterator<Item = (&str, EntityIndex)> + '_ { 112 self.translated() 113 .exports 114 .iter() 115 .map(|(name, index)| (name.as_str(), *index)) 116 } 117 118 pub(super) fn required_features(&self) -> WasmFeatures { 119 self.0.required_features 120 } 121 pub(super) fn engine(&self) -> &Engine { 122 &self.0.engine 123 } 124 pub(super) fn translated(&self) -> &TranslatedModule { 125 &self.0.translated_module 126 } 127 pub(super) fn vmshape(&self) -> &VMShape { 128 &self.0.vmshape 129 } 130 pub(crate) fn code(&self) -> &Arc<CodeObject> { 131 &self.0.code 132 } 133 pub(crate) fn type_collection(&self) -> &RuntimeTypeCollection { 134 &self.0.type_collection 135 } 136 pub(crate) fn type_ids(&self) -> &[VMSharedTypeIndex] { 137 self.0.type_collection.type_map().values().as_slice() 138 } 139 pub(crate) fn functions( 140 &self, 141 ) -> cranelift_entity::Iter<'_, DefinedFuncIndex, CompiledFunctionInfo> { 142 self.0.code.function_info().iter() 143 } 144 145 pub(super) fn array_to_wasm_trampoline( 146 &self, 147 index: DefinedFuncIndex, 148 ) -> Option<VMArrayCallFunction> { 149 let loc = self.0.code.function_info()[index].array_to_wasm_trampoline?; 150 let raw = self.code().resolve_function_loc(loc); 151 // Safety: TODO 152 Some(unsafe { mem::transmute::<usize, VMArrayCallFunction>(raw) }) 153 } 154 155 /// Return the address, in memory, of the trampoline that allows Wasm to 156 /// call a array function of the given signature. 157 pub(super) fn wasm_to_array_trampoline( 158 &self, 159 signature: VMSharedTypeIndex, 160 ) -> Option<NonNull<VMWasmCallFunction>> { 161 let trampoline_shared_ty = self.0.engine.type_registry().get_trampoline_type(signature); 162 let trampoline_module_ty = self 163 .0 164 .type_collection 165 .trampoline_type(trampoline_shared_ty)?; 166 167 debug_assert!( 168 self.0 169 .engine 170 .type_registry() 171 .borrow( 172 self.0 173 .type_collection 174 .lookup_shared_type(trampoline_module_ty) 175 .unwrap() 176 ) 177 .unwrap() 178 .unwrap_func() 179 .is_trampoline_type() 180 ); 181 182 let ptr = self.code().wasm_to_host_trampoline(trampoline_module_ty); 183 184 Some(ptr) 185 } 186 187 pub(super) fn function(&self, index: DefinedFuncIndex) -> NonNull<VMWasmCallFunction> { 188 let loc = self.0.code.function_info()[index].wasm_func_loc; 189 NonNull::new(self.code().resolve_function_loc(loc) as *mut VMWasmCallFunction).unwrap() 190 } 191 192 pub(super) fn same(&self, other: &Self) -> bool { 193 Arc::ptr_eq(&self.0, &other.0) 194 } 195 196 pub(super) fn new_stub(engine: Engine) -> Self { 197 let translated_module = TranslatedModule::default(); 198 Self(Arc::new(ModuleInner { 199 name: "<Stub>".to_string(), 200 engine: engine.clone(), 201 vmshape: VMShape::for_module(u8_size_of::<usize>(), &translated_module), 202 translated_module, 203 required_features: WasmFeatures::default(), 204 code: Arc::new(CodeObject::empty()), 205 type_collection: RuntimeTypeCollection::empty(engine), 206 })) 207 } 208}