Skip to main content

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