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