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