1 //! Define the `instantiate` function, which takes a byte array containing an 2 //! encoded wasm module and returns a live wasm instance. Also, define 3 //! `CompiledModule` to allow compiling and instantiating to be done as separate 4 //! steps. 5 6 use crate::code::EngineCode; 7 use crate::prelude::*; 8 use crate::profiling_agent::ProfilingAgent; 9 use crate::runtime::vm::CompiledModuleId; 10 use alloc::sync::Arc; 11 use core::ops::Range; 12 use core::str; 13 use wasmtime_environ::{ 14 CompiledFunctionsTable, CompiledModuleInfo, DefinedFuncIndex, EntityRef, FilePos, FuncIndex, 15 FuncKey, FunctionLoc, FunctionName, Metadata, Module, ModuleInternedTypeIndex, 16 StaticModuleIndex, 17 }; 18 19 /// A compiled wasm module, ready to be instantiated. 20 pub struct CompiledModule { 21 /// A unique ID used to register this module with the engine. 22 unique_id: CompiledModuleId, 23 engine_code: Arc<EngineCode>, 24 module: Arc<Module>, 25 meta: Metadata, 26 index: Arc<CompiledFunctionsTable>, 27 /// Sorted list, by function index, of names we have for this module. 28 func_names: Vec<FunctionName>, 29 } 30 31 impl CompiledModule { 32 /// Creates `CompiledModule` directly from a precompiled artifact. 33 /// 34 /// The `engine_code` argument is expected to be an EngineCode 35 /// wrapper around a CodeMemory containing the result of a 36 /// previous call to `ObjectBuilder::finish` above. This is an ELF 37 /// image, at this time, which contains all necessary information 38 /// to create a `CompiledModule` from a compilation. 39 /// 40 /// This method also takes `info`, an optionally-provided deserialization 41 /// of the artifacts' compilation metadata section. If this information is 42 /// not provided then the information will be 43 /// deserialized from the image of the compilation artifacts. Otherwise it 44 /// will be assumed to be what would otherwise happen if the section were 45 /// to be deserialized. 46 /// 47 /// The `profiler` argument here is used to inform JIT profiling runtimes 48 /// about new code that is loaded. from_artifacts( engine_code: Arc<EngineCode>, info: CompiledModuleInfo, index: Arc<CompiledFunctionsTable>, profiler: &dyn ProfilingAgent, ) -> Result<Self>49 pub fn from_artifacts( 50 engine_code: Arc<EngineCode>, 51 info: CompiledModuleInfo, 52 index: Arc<CompiledFunctionsTable>, 53 profiler: &dyn ProfilingAgent, 54 ) -> Result<Self> { 55 let mut ret = Self { 56 unique_id: CompiledModuleId::new(), 57 engine_code, 58 module: try_new::<Arc<_>>(info.module)?, 59 meta: info.meta, 60 index, 61 func_names: info.func_names, 62 }; 63 ret.register_profiling(profiler)?; 64 65 Ok(ret) 66 } 67 register_profiling(&mut self, profiler: &dyn ProfilingAgent) -> Result<()>68 fn register_profiling(&mut self, profiler: &dyn ProfilingAgent) -> Result<()> { 69 // TODO-Bug?: "code_memory" is not exclusive for this module in the case of components, 70 // so we may be registering the same code range multiple times here. 71 72 profiler.register_module(self.engine_code.image(), &|addr| { 73 let idx = self.func_by_text_offset(addr)?; 74 let idx = self.module.func_index(idx); 75 let name = self.func_name(idx)?; 76 let mut demangled = String::new(); 77 wasmtime_environ::demangle_function_name(&mut demangled, name).unwrap(); 78 Some(demangled) 79 }); 80 Ok(()) 81 } 82 83 /// Get this module's unique ID. It is unique with respect to a 84 /// single allocator (which is ordinarily held on a Wasm engine). unique_id(&self) -> CompiledModuleId85 pub fn unique_id(&self) -> CompiledModuleId { 86 self.unique_id 87 } 88 89 /// Return a reference-counting pointer to a module. module(&self) -> &Arc<Module>90 pub fn module(&self) -> &Arc<Module> { 91 &self.module 92 } 93 module_index(&self) -> StaticModuleIndex94 fn module_index(&self) -> StaticModuleIndex { 95 self.module.module_index 96 } 97 98 /// Looks up the `name` section name for the function index `idx`, if one 99 /// was specified in the original wasm module. func_name(&self, idx: FuncIndex) -> Option<&str>100 pub fn func_name(&self, idx: FuncIndex) -> Option<&str> { 101 // Find entry for `idx`, if present. 102 let i = self.func_names.binary_search_by_key(&idx, |n| n.idx).ok()?; 103 let name = &self.func_names[i]; 104 105 // Here we `unwrap` the `from_utf8` but this can theoretically be a 106 // `from_utf8_unchecked` if we really wanted since this section is 107 // guaranteed to only have valid utf-8 data. Until it's a problem it's 108 // probably best to double-check this though. 109 let data = self.engine_code.func_name_data(); 110 Some(str::from_utf8(&data[name.offset as usize..][..name.len as usize]).unwrap()) 111 } 112 113 /// Returns an iterator over all functions defined within this module with 114 /// their index and their offset in the underlying code image. 115 #[inline] finished_function_ranges( &self, ) -> impl ExactSizeIterator<Item = (DefinedFuncIndex, Range<usize>)> + '_116 pub fn finished_function_ranges( 117 &self, 118 ) -> impl ExactSizeIterator<Item = (DefinedFuncIndex, Range<usize>)> + '_ { 119 self.module 120 .defined_func_indices() 121 .map(|i| (i, self.finished_function_range(i))) 122 } 123 124 /// Returns the offset in the text section of the function that `index` points to. 125 #[inline] finished_function_range(&self, def_func_index: DefinedFuncIndex) -> Range<usize>126 pub fn finished_function_range(&self, def_func_index: DefinedFuncIndex) -> Range<usize> { 127 let loc = self.func_loc(def_func_index); 128 let start = usize::try_from(loc.start).unwrap(); 129 let end = usize::try_from(loc.start + loc.length).unwrap(); 130 start..end 131 } 132 133 /// Get the array-to-Wasm trampoline for the function `index` 134 /// points to, as a range in the text segment. 135 /// 136 /// If the function `index` points to does not escape, then `None` is 137 /// returned. 138 /// 139 /// These trampolines are used for array callers (e.g. `Func::new`) 140 /// calling Wasm callees. array_to_wasm_trampoline_range( &self, def_func_index: DefinedFuncIndex, ) -> Option<Range<usize>>141 pub fn array_to_wasm_trampoline_range( 142 &self, 143 def_func_index: DefinedFuncIndex, 144 ) -> Option<Range<usize>> { 145 assert!(def_func_index.index() < self.module.num_defined_funcs()); 146 let key = FuncKey::ArrayToWasmTrampoline(self.module_index(), def_func_index); 147 let loc = self.index.func_loc(key)?; 148 let start = usize::try_from(loc.start).unwrap(); 149 let end = usize::try_from(loc.start + loc.length).unwrap(); 150 Some(start..end) 151 } 152 153 /// Get the Wasm-to-array trampoline for the given signature, as a 154 /// range in the text segment. 155 /// 156 /// These trampolines are used for filling in 157 /// `VMFuncRef::wasm_call` for `Func::wrap`-style host funcrefs 158 /// that don't have access to a compiler when created. wasm_to_array_trampoline(&self, signature: ModuleInternedTypeIndex) -> Option<&[u8]>159 pub fn wasm_to_array_trampoline(&self, signature: ModuleInternedTypeIndex) -> Option<&[u8]> { 160 let key = FuncKey::WasmToArrayTrampoline(signature); 161 let loc = self.index.func_loc(key)?; 162 let start = usize::try_from(loc.start).unwrap(); 163 let end = usize::try_from(loc.start + loc.length).unwrap(); 164 Some( 165 self.engine_code 166 .raw_wasm_to_array_trampoline_data(start..end), 167 ) 168 } 169 170 /// Lookups a defined function by a program counter value. 171 /// 172 /// Returns the defined function index and the relative address of 173 /// `text_offset` within the function itself. func_by_text_offset(&self, text_offset: usize) -> Option<DefinedFuncIndex>174 pub fn func_by_text_offset(&self, text_offset: usize) -> Option<DefinedFuncIndex> { 175 let text_offset = u32::try_from(text_offset).unwrap(); 176 let key = self.index.func_by_text_offset(text_offset)?; 177 match key { 178 FuncKey::DefinedWasmFunction(module, def_func_index) => { 179 // If this function is for `self` then pass it through, 180 // otherwise it's for some other module in this image so 181 // there's no `DefinedFuncIndex` for this offset. 182 if module == self.module_index() { 183 Some(def_func_index) 184 } else { 185 None 186 } 187 } 188 _ => None, 189 } 190 } 191 192 /// Gets the function location information for a given function index. func_loc(&self, def_func_index: DefinedFuncIndex) -> &FunctionLoc193 pub fn func_loc(&self, def_func_index: DefinedFuncIndex) -> &FunctionLoc { 194 assert!(def_func_index.index() < self.module.num_defined_funcs()); 195 let key = FuncKey::DefinedWasmFunction(self.module_index(), def_func_index); 196 self.index 197 .func_loc(key) 198 .expect("defined function should be present") 199 } 200 201 /// Returns the original binary offset in the file that `index` was defined 202 /// at. func_start_srcloc(&self, def_func_index: DefinedFuncIndex) -> FilePos203 pub fn func_start_srcloc(&self, def_func_index: DefinedFuncIndex) -> FilePos { 204 assert!(def_func_index.index() < self.module.num_defined_funcs()); 205 let key = FuncKey::DefinedWasmFunction(self.module_index(), def_func_index); 206 self.index 207 .src_loc(key) 208 .expect("defined function should be present") 209 } 210 211 /// Creates a new symbolication context which can be used to further 212 /// symbolicate stack traces. 213 /// 214 /// Basically this makes a thing which parses debuginfo and can tell you 215 /// what filename and line number a wasm pc comes from. 216 #[cfg(feature = "addr2line")] symbolize_context(&self) -> Result<Option<SymbolizeContext<'_>>>217 pub fn symbolize_context(&self) -> Result<Option<SymbolizeContext<'_>>> { 218 use gimli::EndianSlice; 219 if !self.meta.has_wasm_debuginfo { 220 return Ok(None); 221 } 222 let dwarf = gimli::Dwarf::load(|id| -> Result<_> { 223 // Lookup the `id` in the `dwarf` array prepared for this module 224 // during module serialization where it's sorted by the `id` key. If 225 // found this is a range within the general module's concatenated 226 // dwarf section which is extracted here, otherwise it's just an 227 // empty list to represent that it's not present. 228 let data = self 229 .meta 230 .dwarf 231 .binary_search_by_key(&(id as u8), |(id, _)| *id) 232 .ok() 233 .and_then(|i| { 234 let (_, range) = &self.meta.dwarf[i]; 235 let start = range.start.try_into().ok()?; 236 let end = range.end.try_into().ok()?; 237 self.engine_code.wasm_dwarf().get(start..end) 238 }) 239 .unwrap_or(&[]); 240 Ok(EndianSlice::new(data, gimli::LittleEndian)) 241 })?; 242 let cx = addr2line::Context::from_dwarf(dwarf) 243 .context("failed to create addr2line dwarf mapping context")?; 244 Ok(Some(SymbolizeContext { 245 inner: cx, 246 code_section_offset: self.meta.code_section_offset, 247 })) 248 } 249 250 /// Returns whether the original wasm module had unparsed debug information 251 /// based on the tunables configuration. has_unparsed_debuginfo(&self) -> bool252 pub fn has_unparsed_debuginfo(&self) -> bool { 253 self.meta.has_unparsed_debuginfo 254 } 255 256 /// Indicates whether this module came with n address map such that lookups 257 /// via `wasmtime_environ::lookup_file_pos` will succeed. 258 /// 259 /// If this function returns `false` then `lookup_file_pos` will always 260 /// return `None`. has_address_map(&self) -> bool261 pub fn has_address_map(&self) -> bool { 262 !self.engine_code.address_map_data().is_empty() 263 } 264 265 /// Returns the original Wasm bytecode for this module, if it is available. bytecode(&self) -> Option<&[u8]>266 pub fn bytecode(&self) -> Option<&[u8]> { 267 self.engine_code 268 .wasm_bytecode_for_module(self.module.module_index) 269 } 270 } 271 272 #[cfg(feature = "addr2line")] 273 type Addr2LineContext<'a> = addr2line::Context<gimli::EndianSlice<'a, gimli::LittleEndian>>; 274 275 /// A context which contains dwarf debug information to translate program 276 /// counters back to filenames and line numbers. 277 #[cfg(feature = "addr2line")] 278 pub struct SymbolizeContext<'a> { 279 inner: Addr2LineContext<'a>, 280 code_section_offset: u64, 281 } 282 283 #[cfg(feature = "addr2line")] 284 impl<'a> SymbolizeContext<'a> { 285 /// Returns access to the [`addr2line::Context`] which can be used to query 286 /// frame information with. addr2line(&self) -> &Addr2LineContext<'a>287 pub fn addr2line(&self) -> &Addr2LineContext<'a> { 288 &self.inner 289 } 290 291 /// Returns the offset of the code section in the original wasm file, used 292 /// to calculate lookup values into the DWARF. code_section_offset(&self) -> u64293 pub fn code_section_offset(&self) -> u64 { 294 self.code_section_offset 295 } 296 } 297