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