dusk_wasmtime/runtime/
instantiate.rs

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
6use crate::{code_memory::CodeMemory, profiling_agent::ProfilingAgent};
7use anyhow::Result;
8use std::str;
9use std::sync::Arc;
10use wasmtime_environ::{
11    CompiledFunctionInfo, CompiledModuleInfo, DefinedFuncIndex, FuncIndex, FunctionLoc,
12    FunctionName, Metadata, Module, ModuleInternedTypeIndex, PrimaryMap, StackMapInformation,
13    WasmFunctionInfo,
14};
15use wasmtime_runtime::{CompiledModuleId, CompiledModuleIdAllocator, MmapVec};
16
17/// A compiled wasm module, ready to be instantiated.
18pub struct CompiledModule {
19    module: Arc<Module>,
20    funcs: PrimaryMap<DefinedFuncIndex, CompiledFunctionInfo>,
21    wasm_to_native_trampolines: Vec<(ModuleInternedTypeIndex, FunctionLoc)>,
22    meta: Metadata,
23    code_memory: Arc<CodeMemory>,
24    #[cfg(feature = "debug-builtins")]
25    dbg_jit_registration: Option<wasmtime_runtime::GdbJitImageRegistration>,
26    /// A unique ID used to register this module with the engine.
27    unique_id: CompiledModuleId,
28    func_names: Vec<FunctionName>,
29}
30
31impl CompiledModule {
32    /// Creates `CompiledModule` directly from a precompiled artifact.
33    ///
34    /// The `code_memory` argument is expected to be the result of a previous
35    /// call to `ObjectBuilder::finish` above. This is an ELF image, at this
36    /// time, which contains all necessary information to create a
37    /// `CompiledModule` from a compilation.
38    ///
39    /// This method also takes `info`, an optionally-provided deserialization
40    /// of the artifacts' compilation metadata section. If this information is
41    /// not provided then the information will be
42    /// deserialized from the image of the compilation artifacts. Otherwise it
43    /// will be assumed to be what would otherwise happen if the section were
44    /// to be deserialized.
45    ///
46    /// The `profiler` argument here is used to inform JIT profiling runtimes
47    /// about new code that is loaded.
48    pub fn from_artifacts(
49        code_memory: Arc<CodeMemory>,
50        info: CompiledModuleInfo,
51        profiler: &dyn ProfilingAgent,
52        id_allocator: &CompiledModuleIdAllocator,
53    ) -> Result<Self> {
54        let mut ret = Self {
55            module: Arc::new(info.module),
56            funcs: info.funcs,
57            wasm_to_native_trampolines: info.wasm_to_native_trampolines,
58            #[cfg(feature = "debug-builtins")]
59            dbg_jit_registration: None,
60            code_memory,
61            meta: info.meta,
62            unique_id: id_allocator.alloc(),
63            func_names: info.func_names,
64        };
65        ret.register_debug_and_profiling(profiler)?;
66
67        Ok(ret)
68    }
69
70    fn register_debug_and_profiling(&mut self, profiler: &dyn ProfilingAgent) -> Result<()> {
71        #[cfg(feature = "debug-builtins")]
72        if self.meta.native_debug_info_present {
73            use anyhow::Context;
74
75            let text = self.text();
76            let bytes = crate::debug::create_gdbjit_image(
77                self.mmap().to_vec(),
78                (text.as_ptr(), text.len()),
79            )
80            .context("failed to create jit image for gdb")?;
81            let reg = wasmtime_runtime::GdbJitImageRegistration::register(bytes);
82            self.dbg_jit_registration = Some(reg);
83        }
84        profiler.register_module(&self.code_memory.mmap()[..], &|addr| {
85            let (idx, _) = self.func_by_text_offset(addr)?;
86            let idx = self.module.func_index(idx);
87            let name = self.func_name(idx)?;
88            let mut demangled = String::new();
89            wasmtime_environ::demangle_function_name(&mut demangled, name).unwrap();
90            Some(demangled)
91        });
92        Ok(())
93    }
94
95    /// Get this module's unique ID. It is unique with respect to a
96    /// single allocator (which is ordinarily held on a Wasm engine).
97    pub fn unique_id(&self) -> CompiledModuleId {
98        self.unique_id
99    }
100
101    /// Returns the underlying memory which contains the compiled module's
102    /// image.
103    pub fn mmap(&self) -> &MmapVec {
104        self.code_memory.mmap()
105    }
106
107    /// Returns the underlying owned mmap of this compiled image.
108    pub fn code_memory(&self) -> &Arc<CodeMemory> {
109        &self.code_memory
110    }
111
112    /// Returns the text section of the ELF image for this compiled module.
113    ///
114    /// This memory should have the read/execute permissions.
115    #[inline]
116    pub fn text(&self) -> &[u8] {
117        self.code_memory.text()
118    }
119
120    /// Return a reference-counting pointer to a module.
121    pub fn module(&self) -> &Arc<Module> {
122        &self.module
123    }
124
125    /// Looks up the `name` section name for the function index `idx`, if one
126    /// was specified in the original wasm module.
127    pub fn func_name(&self, idx: FuncIndex) -> Option<&str> {
128        // Find entry for `idx`, if present.
129        let i = self.func_names.binary_search_by_key(&idx, |n| n.idx).ok()?;
130        let name = &self.func_names[i];
131
132        // Here we `unwrap` the `from_utf8` but this can theoretically be a
133        // `from_utf8_unchecked` if we really wanted since this section is
134        // guaranteed to only have valid utf-8 data. Until it's a problem it's
135        // probably best to double-check this though.
136        let data = self.code_memory().func_name_data();
137        Some(str::from_utf8(&data[name.offset as usize..][..name.len as usize]).unwrap())
138    }
139
140    /// Return a reference to a mutable module (if possible).
141    pub fn module_mut(&mut self) -> Option<&mut Module> {
142        Arc::get_mut(&mut self.module)
143    }
144
145    /// Returns an iterator over all functions defined within this module with
146    /// their index and their body in memory.
147    #[inline]
148    pub fn finished_functions(
149        &self,
150    ) -> impl ExactSizeIterator<Item = (DefinedFuncIndex, &[u8])> + '_ {
151        self.funcs
152            .iter()
153            .map(move |(i, _)| (i, self.finished_function(i)))
154    }
155
156    /// Returns the body of the function that `index` points to.
157    #[inline]
158    pub fn finished_function(&self, index: DefinedFuncIndex) -> &[u8] {
159        let loc = self.funcs[index].wasm_func_loc;
160        &self.text()[loc.start as usize..][..loc.length as usize]
161    }
162
163    /// Get the array-to-Wasm trampoline for the function `index` points to.
164    ///
165    /// If the function `index` points to does not escape, then `None` is
166    /// returned.
167    ///
168    /// These trampolines are used for array callers (e.g. `Func::new`)
169    /// calling Wasm callees.
170    pub fn array_to_wasm_trampoline(&self, index: DefinedFuncIndex) -> Option<&[u8]> {
171        let loc = self.funcs[index].array_to_wasm_trampoline?;
172        Some(&self.text()[loc.start as usize..][..loc.length as usize])
173    }
174
175    /// Get the native-to-Wasm trampoline for the function `index` points to.
176    ///
177    /// If the function `index` points to does not escape, then `None` is
178    /// returned.
179    ///
180    /// These trampolines are used for native callers (e.g. `Func::wrap`)
181    /// calling Wasm callees.
182    #[inline]
183    pub fn native_to_wasm_trampoline(&self, index: DefinedFuncIndex) -> Option<&[u8]> {
184        let loc = self.funcs[index].native_to_wasm_trampoline?;
185        Some(&self.text()[loc.start as usize..][..loc.length as usize])
186    }
187
188    /// Get the Wasm-to-native trampoline for the given signature.
189    ///
190    /// These trampolines are used for filling in
191    /// `VMFuncRef::wasm_call` for `Func::wrap`-style host funcrefs
192    /// that don't have access to a compiler when created.
193    pub fn wasm_to_native_trampoline(&self, signature: ModuleInternedTypeIndex) -> &[u8] {
194        let idx = self
195            .wasm_to_native_trampolines
196            .binary_search_by_key(&signature, |entry| entry.0)
197            .expect("should have a Wasm-to-native trampline for all signatures");
198        let (_, loc) = self.wasm_to_native_trampolines[idx];
199        &self.text()[loc.start as usize..][..loc.length as usize]
200    }
201
202    /// Returns the stack map information for all functions defined in this
203    /// module.
204    ///
205    /// The iterator returned iterates over the span of the compiled function in
206    /// memory with the stack maps associated with those bytes.
207    pub fn stack_maps(&self) -> impl Iterator<Item = (&[u8], &[StackMapInformation])> {
208        self.finished_functions().map(|(_, f)| f).zip(
209            self.funcs
210                .values()
211                .map(|f| &f.wasm_func_info.stack_maps[..]),
212        )
213    }
214
215    /// Lookups a defined function by a program counter value.
216    ///
217    /// Returns the defined function index and the relative address of
218    /// `text_offset` within the function itself.
219    pub fn func_by_text_offset(&self, text_offset: usize) -> Option<(DefinedFuncIndex, u32)> {
220        let text_offset = u32::try_from(text_offset).unwrap();
221
222        let index = match self.funcs.binary_search_values_by_key(&text_offset, |e| {
223            debug_assert!(e.wasm_func_loc.length > 0);
224            // Return the inclusive "end" of the function
225            e.wasm_func_loc.start + e.wasm_func_loc.length - 1
226        }) {
227            Ok(k) => {
228                // Exact match, pc is at the end of this function
229                k
230            }
231            Err(k) => {
232                // Not an exact match, k is where `pc` would be "inserted"
233                // Since we key based on the end, function `k` might contain `pc`,
234                // so we'll validate on the range check below
235                k
236            }
237        };
238
239        let CompiledFunctionInfo { wasm_func_loc, .. } = self.funcs.get(index)?;
240        let start = wasm_func_loc.start;
241        let end = wasm_func_loc.start + wasm_func_loc.length;
242
243        if text_offset < start || end < text_offset {
244            return None;
245        }
246
247        Some((index, text_offset - wasm_func_loc.start))
248    }
249
250    /// Gets the function location information for a given function index.
251    pub fn func_loc(&self, index: DefinedFuncIndex) -> &FunctionLoc {
252        &self
253            .funcs
254            .get(index)
255            .expect("defined function should be present")
256            .wasm_func_loc
257    }
258
259    /// Gets the function information for a given function index.
260    pub fn wasm_func_info(&self, index: DefinedFuncIndex) -> &WasmFunctionInfo {
261        &self
262            .funcs
263            .get(index)
264            .expect("defined function should be present")
265            .wasm_func_info
266    }
267
268    /// Creates a new symbolication context which can be used to further
269    /// symbolicate stack traces.
270    ///
271    /// Basically this makes a thing which parses debuginfo and can tell you
272    /// what filename and line number a wasm pc comes from.
273    #[cfg(feature = "addr2line")]
274    pub fn symbolize_context(&self) -> Result<Option<SymbolizeContext<'_>>> {
275        use anyhow::Context;
276        use gimli::EndianSlice;
277        if !self.meta.has_wasm_debuginfo {
278            return Ok(None);
279        }
280        let dwarf = gimli::Dwarf::load(|id| -> Result<_> {
281            // Lookup the `id` in the `dwarf` array prepared for this module
282            // during module serialization where it's sorted by the `id` key. If
283            // found this is a range within the general module's concatenated
284            // dwarf section which is extracted here, otherwise it's just an
285            // empty list to represent that it's not present.
286            let data = self
287                .meta
288                .dwarf
289                .binary_search_by_key(&(id as u8), |(id, _)| *id)
290                .map(|i| {
291                    let (_, range) = &self.meta.dwarf[i];
292                    &self.code_memory().dwarf()[range.start as usize..range.end as usize]
293                })
294                .unwrap_or(&[]);
295            Ok(EndianSlice::new(data, gimli::LittleEndian))
296        })?;
297        let cx = addr2line::Context::from_dwarf(dwarf)
298            .context("failed to create addr2line dwarf mapping context")?;
299        Ok(Some(SymbolizeContext {
300            inner: cx,
301            code_section_offset: self.meta.code_section_offset,
302        }))
303    }
304
305    /// Returns whether the original wasm module had unparsed debug information
306    /// based on the tunables configuration.
307    pub fn has_unparsed_debuginfo(&self) -> bool {
308        self.meta.has_unparsed_debuginfo
309    }
310
311    /// Indicates whether this module came with n address map such that lookups
312    /// via `wasmtime_environ::lookup_file_pos` will succeed.
313    ///
314    /// If this function returns `false` then `lookup_file_pos` will always
315    /// return `None`.
316    pub fn has_address_map(&self) -> bool {
317        !self.code_memory.address_map_data().is_empty()
318    }
319}
320
321#[cfg(feature = "addr2line")]
322type Addr2LineContext<'a> = addr2line::Context<gimli::EndianSlice<'a, gimli::LittleEndian>>;
323
324/// A context which contains dwarf debug information to translate program
325/// counters back to filenames and line numbers.
326#[cfg(feature = "addr2line")]
327pub struct SymbolizeContext<'a> {
328    inner: Addr2LineContext<'a>,
329    code_section_offset: u64,
330}
331
332#[cfg(feature = "addr2line")]
333impl<'a> SymbolizeContext<'a> {
334    /// Returns access to the [`addr2line::Context`] which can be used to query
335    /// frame information with.
336    pub fn addr2line(&self) -> &Addr2LineContext<'a> {
337        &self.inner
338    }
339
340    /// Returns the offset of the code section in the original wasm file, used
341    /// to calculate lookup values into the DWARF.
342    pub fn code_section_offset(&self) -> u64 {
343        self.code_section_offset
344    }
345}