1 //! Definitions of runtime structures and metadata which are serialized into ELF
2 //! with `bincode` as part of a module's compilation process.
3 
4 use crate::prelude::*;
5 use crate::{
6     EntityRef, FilePos, FuncIndex, FuncKey, FuncKeyIndex, FuncKeyKind, FuncKeyNamespace, Module,
7     PanicOnOom as _,
8 };
9 use core::ops::Range;
10 use core::{fmt, u32};
11 use core::{iter, str};
12 use serde_derive::{Deserialize, Serialize};
13 #[cfg(feature = "rr")]
14 use sha2::{Digest, Sha256};
15 
16 /// Description of where a function is located in the text section of a
17 /// compiled image.
18 #[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
19 pub struct FunctionLoc {
20     /// The byte offset from the start of the text section where this
21     /// function starts.
22     pub start: u32,
23     /// The byte length of this function's function body.
24     pub length: u32,
25 }
26 
27 impl FunctionLoc {
28     /// Is this an empty function location?
29     #[inline]
is_empty(&self) -> bool30     pub fn is_empty(&self) -> bool {
31         self.length == 0
32     }
33 }
34 
35 /// The checksum of a Wasm binary.
36 ///
37 /// Allows for features requiring the exact same Wasm Module (e.g. deterministic replay)
38 /// to verify that the binary used matches the one originally compiled.
39 #[derive(Copy, Clone, Default, PartialEq, Eq, Ord, PartialOrd, Debug, Serialize, Deserialize)]
40 pub struct WasmChecksum([u8; 32]);
41 
42 impl WasmChecksum {
43     /// Construct a [`WasmChecksum`] from the given wasm binary, used primarily for integrity
44     /// checks on compiled modules when recording configs are enabled. The checksum is not
45     /// computed when recording is disabled to prevent pessimization of non-recorded compilations.
46     #[cfg(feature = "rr")]
from_binary(bin: &[u8], recording: bool) -> WasmChecksum47     pub fn from_binary(bin: &[u8], recording: bool) -> WasmChecksum {
48         if recording {
49             WasmChecksum(Sha256::digest(bin).into())
50         } else {
51             WasmChecksum::default()
52         }
53     }
54 
55     /// This method requires the `rr` feature to actual compute a checksum. Since the `rr`
56     /// feature is disabled, this only returns a default checksum value of all zeros.
57     #[cfg(not(feature = "rr"))]
from_binary(_: &[u8], _: bool) -> WasmChecksum58     pub fn from_binary(_: &[u8], _: bool) -> WasmChecksum {
59         WasmChecksum::default()
60     }
61 }
62 
63 impl core::ops::Deref for WasmChecksum {
64     type Target = [u8; 32];
65 
deref(&self) -> &Self::Target66     fn deref(&self) -> &Self::Target {
67         &self.0
68     }
69 }
70 
71 /// A builder for a `CompiledFunctionsTable`.
72 pub struct CompiledFunctionsTableBuilder {
73     inner: CompiledFunctionsTable,
74 }
75 
76 impl CompiledFunctionsTableBuilder {
77     /// Create a new builder.
new() -> Self78     pub fn new() -> Self {
79         Self {
80             inner: CompiledFunctionsTable {
81                 namespaces: TryPrimaryMap::new(),
82                 func_loc_starts: TryPrimaryMap::new(),
83                 sparse_starts: TryPrimaryMap::new(),
84                 src_loc_starts: TryPrimaryMap::new(),
85                 sparse_indices: TryPrimaryMap::new(),
86                 func_locs: TryPrimaryMap::new(),
87                 src_locs: TryPrimaryMap::new(),
88             },
89         }
90     }
91 
last_namespace(&self) -> Option<FuncKeyNamespace>92     fn last_namespace(&self) -> Option<FuncKeyNamespace> {
93         let (_, &ns) = self.inner.namespaces.last()?;
94         Some(ns)
95     }
96 
last_key_index(&self) -> Option<FuncKeyIndex>97     fn last_key_index(&self) -> Option<FuncKeyIndex> {
98         let (ns_idx, ns) = self.inner.namespaces.last()?;
99         let start = self.inner.func_loc_starts[ns_idx];
100         if CompiledFunctionsTable::is_dense(ns.kind()) {
101             let len = self.inner.func_locs.len();
102             let len = u32::try_from(len).unwrap();
103             let key_index = len - start.as_u32();
104             let key_index = FuncKeyIndex::from_raw(key_index);
105             Some(key_index)
106         } else {
107             let sparse_start = self.inner.sparse_starts[ns_idx];
108             if self.inner.sparse_indices.len() > sparse_start.index() {
109                 let (_, &key_index) = self.inner.sparse_indices.last().unwrap();
110                 Some(key_index)
111             } else {
112                 None
113             }
114         }
115     }
116 
last_func_loc(&self) -> Option<FunctionLoc>117     fn last_func_loc(&self) -> Option<FunctionLoc> {
118         let (_, &loc) = self.inner.func_locs.last()?;
119         Some(loc)
120     }
121 
122     /// Push a new entry into this builder.
123     ///
124     /// Panics if the key or function location is out of order.
push_func( &mut self, key: FuncKey, func_loc: FunctionLoc, src_loc: FilePos, ) -> &mut Self125     pub fn push_func(
126         &mut self,
127         key: FuncKey,
128         func_loc: FunctionLoc,
129         src_loc: FilePos,
130     ) -> &mut Self {
131         let (key_ns, key_index) = key.into_parts();
132 
133         assert!(
134             self.last_namespace().is_none_or(|ns| ns <= key_ns),
135             "`FuncKey`s pushed out of order"
136         );
137         assert!(
138             self.last_key_index().is_none_or(
139                 |i| i <= key_index || self.last_namespace().is_some_and(|ns| ns != key_ns)
140             ),
141             "`FuncKey`s pushed out of order"
142         );
143         assert!(
144             self.last_func_loc()
145                 .is_none_or(|l| l.start + l.length <= func_loc.start),
146             "`FunctionLoc`s pushed out of order"
147         );
148 
149         // Make sure that there is a `kind` entry for this key's kind.
150         let kind_start_index = self
151             .inner
152             .namespaces
153             .last()
154             .and_then(|(ns_idx, ns)| {
155                 if *ns == key_ns {
156                     Some(self.inner.func_loc_starts[ns_idx])
157                 } else {
158                     None
159                 }
160             })
161             .unwrap_or_else(|| {
162                 let start = self.inner.func_locs.next_key();
163                 let ns_idx = self.inner.namespaces.push(key_ns).panic_on_oom();
164                 let ns_idx2 = self.inner.func_loc_starts.push(start).panic_on_oom();
165                 let ns_idx3 = self
166                     .inner
167                     .sparse_starts
168                     .push(self.inner.sparse_indices.next_key())
169                     .panic_on_oom();
170                 let ns_idx4 = self
171                     .inner
172                     .src_loc_starts
173                     .push(self.inner.src_locs.next_key())
174                     .panic_on_oom();
175                 debug_assert_eq!(ns_idx, ns_idx2);
176                 debug_assert_eq!(ns_idx, ns_idx3);
177                 debug_assert_eq!(ns_idx, ns_idx4);
178                 start
179             });
180 
181         if CompiledFunctionsTable::is_dense(key.kind()) {
182             // Figure out the index within `func_locs` for this key's entry.
183             let index = kind_start_index.as_u32() + key_index.into_raw();
184             let index = FuncLocIndex::from_u32(index);
185             debug_assert!(self.inner.func_locs.get(index).is_none());
186 
187             // Fill in null entries for any key indices that have been omitted.
188             //
189             // Note that we need a null `FunctionLoc`, but we also need
190             // `func_locs` to be sorted so that we support reverse
191             // lookups. Therefore, we take care to create an empty function
192             // location that starts at the text offset that the previous one (if
193             // any) ends at, and use that as our null entry.
194             let null_func_loc = FunctionLoc {
195                 start: self
196                     .last_func_loc()
197                     .map(|l| l.start + l.length)
198                     .unwrap_or_default(),
199                 length: 0,
200             };
201             let gap = index.index() - self.inner.func_locs.len();
202             self.inner
203                 .func_locs
204                 .try_extend(iter::repeat(null_func_loc).take(gap))
205                 .panic_on_oom();
206             debug_assert_eq!(index, self.inner.func_locs.next_key());
207 
208             if CompiledFunctionsTable::has_src_locs(key_ns.kind()) {
209                 self.inner
210                     .src_locs
211                     .try_extend(iter::repeat(FilePos::none()).take(gap))
212                     .panic_on_oom();
213             }
214         } else {
215             debug_assert!(
216                 src_loc.is_none(),
217                 "sparse keys do not have source locations"
218             );
219             self.inner.sparse_indices.push(key_index).panic_on_oom();
220         }
221 
222         // And finally, we push this entry.
223         self.inner.func_locs.push(func_loc).panic_on_oom();
224         if CompiledFunctionsTable::has_src_locs(key_ns.kind()) {
225             self.inner.src_locs.push(src_loc).panic_on_oom();
226         } else {
227             debug_assert!(src_loc.is_none());
228         }
229 
230         self
231     }
232 
233     /// Finish construction of the `CompiledFunctionsTable`.
finish(self) -> CompiledFunctionsTable234     pub fn finish(self) -> CompiledFunctionsTable {
235         self.inner
236     }
237 }
238 
239 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
240 struct NamespaceIndex(u32);
241 cranelift_entity::entity_impl!(NamespaceIndex);
242 
243 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
244 struct FuncLocIndex(u32);
245 cranelift_entity::entity_impl!(FuncLocIndex);
246 
247 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
248 struct SparseIndex(u32);
249 cranelift_entity::entity_impl!(SparseIndex);
250 
251 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
252 struct SrcLocIndex(u32);
253 cranelift_entity::entity_impl!(SrcLocIndex);
254 
255 /// A table describing the set of functions compiled into an artifact, their
256 /// locations within the text section, and etc...
257 ///
258 /// Logically, this type is a map from a `FuncKey` to the associated function's
259 ///
260 /// * location within the associated text section, and
261 /// * optional source location.
262 ///
263 /// How this map is *actually* implemented is with a series of lookup and binary
264 /// search tables, split out in a data-oriented, struct-of-arrays style. We
265 /// organize the data in this way is service of three goals:
266 ///
267 /// 1. Provide fast look ups: We need to look up the metadata for a function by
268 ///    its key at runtime. During instantiation, for example, we need to create
269 ///    `VMFuncRef`s for escaping functions and this requires looking up the
270 ///    locations of those Wasm functions and their associated array-to-Wasm
271 ///    trampolines.
272 ///
273 /// 2. Keep memory overheads low and code size small: This type is serialized
274 ///    into all of our ELF artifacts and deserialized into all `Module`s and
275 ///    `Component`s at runtime.
276 ///
277 /// 3. Be generic over any kind of function (whether defined Wasm function,
278 ///    trampoline, or etc...) that we compile: Adding a new kind of trampoline,
279 ///    for example, should not require updating this structure to add a new
280 ///    table of the function locations for just trampolines of that new kind. We
281 ///    should be able to store and query all kinds of functions uniformly.
282 //
283 // TODO: This structure could be directly encoded as raw ELF sections, instead
284 // of a `struct` containing a bunch of `PrimaryMap`s, which would allow us to
285 // avoid the serialize/deserialize runtime costs.
286 #[derive(Debug, Serialize, Deserialize)]
287 pub struct CompiledFunctionsTable {
288     /// A binary-search index for this table, mapping raw `FuncKeyNamespace`s to
289     /// their associated `NamespaceIndex`. That `NamespaceIndex` can then be
290     /// used to find the range of other entity indices that are specific to that
291     /// namespace.
292     namespaces: TryPrimaryMap<NamespaceIndex, FuncKeyNamespace>,
293 
294     /// `self.func_loc_starts[i]..self.func_loc_starts[i+1]` describes the range
295     /// within `self.func_locs` whose entries are associated with the namespace
296     /// `self.index[i]`.
297     ///
298     /// When `self.func_loc_starts[i+1]` is out of bounds, then the range is to
299     /// the end of `self.func_locs`.
300     func_loc_starts: TryPrimaryMap<NamespaceIndex, FuncLocIndex>,
301 
302     /// `self.sparse_starts[i]..self.sparse_starts[i+1]` describes the range
303     /// within `self.sparse_indices` whose entries are associated with the
304     /// namespace `self.index[i]`.
305     ///
306     /// When `self.sparse_starts[i+1]` is out of bounds, then the range is to
307     /// the end of `self.sparse_indices`.
308     ///
309     /// Entries are only valid for sparse, non-dense namespaces.
310     sparse_starts: TryPrimaryMap<NamespaceIndex, SparseIndex>,
311 
312     /// `self.src_loc_starts[i]..self.src_loc_starts[i+1]` describes the range
313     /// within `self.src_loc_indices` whose entries are associated with the
314     /// namespace `self.index[i]`.
315     ///
316     /// When `self.src_loc_starts[i+1]` is out of bounds, then the range is to
317     /// the end of `self.src_locs`.
318     ///
319     /// Entries are only valid for namespaces whose functions have source
320     /// locations.
321     src_loc_starts: TryPrimaryMap<NamespaceIndex, SrcLocIndex>,
322 
323     /// `self.sparse_indices[i]` contains the index part of
324     /// `FuncKey::from_parts(ns, index)` where `ns` is determined by
325     /// `self.sparse_starts` and is a sparse, non-dense key kind. (Note that for
326     /// dense keys, this information is implicitly encoded in their offset from
327     /// the namespace's start index.)
328     ///
329     /// This is sorted to allow for binary searches.
330     sparse_indices: TryPrimaryMap<SparseIndex, FuncKeyIndex>,
331 
332     /// `self.func_locs[i]` contains the location within the text section of
333     /// `FuncKey::from_parts(self.namespaces[ns], i - start)`'s function, where
334     /// `ns` and `start` are determined by `self.func_loc_starts`.
335     ///
336     /// Values are sorted by function location to support reverse queries from
337     /// function location back to `FuncKey`.
338     ///
339     /// The absence of a function location (for gaps in dense namespaces) is
340     /// represented with `FunctionLoc::none()`.
341     func_locs: TryPrimaryMap<FuncLocIndex, FunctionLoc>,
342 
343     /// `self.src_locs[i]` contains the initial source location of
344     /// `FuncKey::from_parts(self.namespaces[ns], i - start)`'s function, where
345     /// `ns` and `start` are determined by `self.src_loc_starts`.
346     ///
347     /// The absence of a source location is represented by `FilePos::none()`.
348     src_locs: TryPrimaryMap<SrcLocIndex, FilePos>,
349 }
350 
351 impl CompiledFunctionsTable {
352     #[inline]
namespace_index(&self, namespace: FuncKeyNamespace) -> Option<NamespaceIndex>353     fn namespace_index(&self, namespace: FuncKeyNamespace) -> Option<NamespaceIndex> {
354         const LINEAR_SEARCH_LIMIT: usize = 32;
355         if self.namespaces.len() <= LINEAR_SEARCH_LIMIT {
356             self.namespaces
357                 .iter()
358                 .find_map(|(idx, ns)| if *ns == namespace { Some(idx) } else { None })
359         } else {
360             self.namespaces
361                 .binary_search_values_by_key(&namespace, |ns| *ns)
362                 .ok()
363         }
364     }
365 
366     #[inline]
func_loc_range(&self, ns_idx: NamespaceIndex) -> Range<FuncLocIndex>367     fn func_loc_range(&self, ns_idx: NamespaceIndex) -> Range<FuncLocIndex> {
368         let start = self.func_loc_starts[ns_idx];
369         let next_ns_idx = NamespaceIndex::from_u32(ns_idx.as_u32() + 1);
370         let end = self
371             .func_loc_starts
372             .get(next_ns_idx)
373             .copied()
374             .unwrap_or_else(|| self.func_locs.next_key());
375         start..end
376     }
377 
sparse_range(&self, ns_idx: NamespaceIndex) -> Range<SparseIndex>378     fn sparse_range(&self, ns_idx: NamespaceIndex) -> Range<SparseIndex> {
379         debug_assert!(!Self::is_dense(self.namespaces[ns_idx].kind()));
380         let start = self.sparse_starts[ns_idx];
381         let next_ns_idx = NamespaceIndex::from_u32(ns_idx.as_u32() + 1);
382         let end = self
383             .sparse_starts
384             .get(next_ns_idx)
385             .copied()
386             .unwrap_or_else(|| self.sparse_indices.next_key());
387         start..end
388     }
389 
src_loc_range(&self, ns_idx: NamespaceIndex) -> Range<SrcLocIndex>390     fn src_loc_range(&self, ns_idx: NamespaceIndex) -> Range<SrcLocIndex> {
391         debug_assert!(Self::has_src_locs(self.namespaces[ns_idx].kind()));
392         let start = self.src_loc_starts[ns_idx];
393         let next_ns_idx = NamespaceIndex::from_u32(ns_idx.as_u32() + 1);
394         let end = self
395             .src_loc_starts
396             .get(next_ns_idx)
397             .copied()
398             .unwrap_or_else(|| self.src_locs.next_key());
399         start..end
400     }
401 
402     /// Get the index within `self.{func_locs,src_locs}` that is associated with
403     /// the given `key`, if any.
404     #[inline]
func_loc_index(&self, key: FuncKey) -> Option<FuncLocIndex>405     fn func_loc_index(&self, key: FuncKey) -> Option<FuncLocIndex> {
406         let (key_ns, key_index) = key.into_parts();
407         let ns_idx = self.namespace_index(key_ns)?;
408         let Range { start, end } = self.func_loc_range(ns_idx);
409 
410         let index = if Self::is_dense(key.kind()) {
411             let index = start.as_u32().checked_add(key_index.into_raw())?;
412             FuncLocIndex::from_u32(index)
413         } else {
414             let sparse_range = self.sparse_range(ns_idx);
415             let sparse_subslice = self.sparse_indices.get_range(sparse_range).unwrap();
416             match sparse_subslice.binary_search(&key_index) {
417                 Ok(i) => FuncLocIndex::new(start.index() + i),
418                 Err(_) => return None,
419             }
420         };
421 
422         if index < end { Some(index) } else { None }
423     }
424 
425     /// Get the location of the function associated with the given `key` inside
426     /// the text section, if any.
427     #[inline]
func_loc(&self, key: FuncKey) -> Option<&FunctionLoc>428     pub fn func_loc(&self, key: FuncKey) -> Option<&FunctionLoc> {
429         let index = self.func_loc_index(key)?;
430         let loc = &self.func_locs[index];
431         if loc.is_empty() { None } else { Some(loc) }
432     }
433 
src_loc_index(&self, key: FuncKey) -> Option<SrcLocIndex>434     fn src_loc_index(&self, key: FuncKey) -> Option<SrcLocIndex> {
435         let (key_ns, key_index) = key.into_parts();
436         if !Self::has_src_locs(key_ns.kind()) {
437             return None;
438         }
439 
440         let ns_idx = self.namespace_index(key_ns)?;
441         let Range { start, end } = self.src_loc_range(ns_idx);
442 
443         debug_assert!(Self::is_dense(key_ns.kind()));
444         let index = start.as_u32().checked_add(key_index.into_raw())?;
445         let index = SrcLocIndex::from_u32(index);
446         if index >= end {
447             return None;
448         }
449 
450         Some(index)
451     }
452 
453     /// Get the initial source location of the function associated with the
454     /// given `key`, if any.
src_loc(&self, key: FuncKey) -> Option<FilePos>455     pub fn src_loc(&self, key: FuncKey) -> Option<FilePos> {
456         let index = self.src_loc_index(key)?;
457         let loc = self.src_locs[index];
458         if loc.is_none() { None } else { Some(loc) }
459     }
460 
461     /// Given an offset into the text section, get the key for its associated
462     /// function and its offset within that function.
func_by_text_offset(&self, text_offset: u32) -> Option<FuncKey>463     pub fn func_by_text_offset(&self, text_offset: u32) -> Option<FuncKey> {
464         let index = match self.func_locs.as_values_slice().binary_search_by(|loc| {
465             if loc.is_empty() {
466                 loc.start
467                     .cmp(&text_offset)
468                     .then_with(|| core::cmp::Ordering::Less)
469             } else {
470                 if loc.start > text_offset {
471                     core::cmp::Ordering::Greater
472                 } else if loc.start + loc.length <= text_offset {
473                     core::cmp::Ordering::Less
474                 } else {
475                     debug_assert!(loc.start <= text_offset);
476                     debug_assert!(text_offset < loc.start + loc.length);
477                     core::cmp::Ordering::Equal
478                 }
479             }
480         }) {
481             // Exact match, the offset is at the end of this function.
482             Ok(k) => k,
483             // Not an exact match: `k` is where the offset would be
484             // "inserted". Since we key based on the end, function `k` might
485             // contain the offset, so we'll validate on the range check
486             // below.
487             Err(k) => k,
488         };
489         let index = FuncLocIndex::new(index);
490 
491         // Make sure that the text offset is actually within this function.
492         // Non-exact binary search results can either be because we have a text
493         // offset within a function but not exactly at its inclusive end, or
494         // because the text offset is not within any of our functions. We filter
495         // that latter case out with this check.
496         let loc = self.func_locs.get(index)?;
497         let start = loc.start;
498         let end = start + loc.length;
499         if text_offset < start || end < text_offset {
500             return None;
501         }
502 
503         let ns_idx = match self
504             .func_loc_starts
505             .binary_search_values_by_key(&index, |s| *s)
506         {
507             // Exact match: `i` is the entry's index.
508             Ok(i) => i,
509             // Not an exact match: the index, if it were the start of a
510             // namespace's range, would be at `i`. Therefore, our namespace
511             // entry is actually at index `i - 1`.
512             Err(i) => {
513                 let i = i.as_u32();
514                 assert_ne!(i, 0);
515                 NamespaceIndex::from_u32(i - 1)
516             }
517         };
518         let key_ns = self.namespaces[ns_idx];
519         let start = self.func_loc_starts[ns_idx];
520 
521         let key_index = if Self::is_dense(key_ns.kind()) {
522             let key_index = index.as_u32() - start.as_u32();
523             FuncKeyIndex::from_raw(key_index)
524         } else {
525             let sparse_offset = index.as_u32() - start.as_u32();
526             let sparse_start = self.sparse_starts[ns_idx];
527             let sparse_index = SparseIndex::from_u32(sparse_start.as_u32() + sparse_offset);
528             debug_assert!(
529                 {
530                     let range = self.sparse_range(ns_idx);
531                     range.start <= sparse_index && sparse_index < range.end
532                 },
533                 "{sparse_index:?} is not within {:?}",
534                 self.sparse_range(ns_idx)
535             );
536             self.sparse_indices[sparse_index]
537         };
538         let key = FuncKey::from_parts(key_ns, key_index);
539 
540         Some(key)
541     }
542 
543     /// Whether the given kind's index space is (generally) densely populated
544     /// and therefore we should densely pack them in the table for `O(1)`
545     /// lookups; otherwise, we should avoid code size bloat by using the sparse
546     /// table indirection and `O(log n)` binary search lookups.
is_dense(kind: FuncKeyKind) -> bool547     fn is_dense(kind: FuncKeyKind) -> bool {
548         match kind {
549             FuncKeyKind::DefinedWasmFunction
550             | FuncKeyKind::WasmToArrayTrampoline
551             | FuncKeyKind::PulleyHostCall => true,
552 
553             FuncKeyKind::ArrayToWasmTrampoline
554             | FuncKeyKind::WasmToBuiltinTrampoline
555             | FuncKeyKind::PatchableToBuiltinTrampoline => false,
556 
557             #[cfg(feature = "component-model")]
558             FuncKeyKind::ComponentTrampoline
559             | FuncKeyKind::ResourceDropTrampoline
560             | FuncKeyKind::UnsafeIntrinsic => true,
561         }
562     }
563 
564     /// Whether the given function kind has source locations or not.
has_src_locs(kind: FuncKeyKind) -> bool565     fn has_src_locs(kind: FuncKeyKind) -> bool {
566         match kind {
567             FuncKeyKind::DefinedWasmFunction => true,
568             FuncKeyKind::ArrayToWasmTrampoline
569             | FuncKeyKind::WasmToArrayTrampoline
570             | FuncKeyKind::WasmToBuiltinTrampoline
571             | FuncKeyKind::PatchableToBuiltinTrampoline
572             | FuncKeyKind::PulleyHostCall => false,
573             #[cfg(feature = "component-model")]
574             FuncKeyKind::ComponentTrampoline
575             | FuncKeyKind::ResourceDropTrampoline
576             | FuncKeyKind::UnsafeIntrinsic => false,
577         }
578     }
579 }
580 
581 /// Secondary in-memory results of module compilation.
582 ///
583 /// This opaque structure can be optionally passed back to
584 /// `CompiledModule::from_artifacts` to avoid decoding extra information there.
585 #[derive(Serialize, Deserialize)]
586 pub struct CompiledModuleInfo {
587     /// Type information about the compiled WebAssembly module.
588     pub module: Module,
589 
590     /// General compilation metadata.
591     pub meta: Metadata,
592 
593     /// Sorted list, by function index, of names we have for this module.
594     pub func_names: Vec<FunctionName>,
595 
596     /// Checksum of the source Wasm binary from which this module was compiled.
597     pub checksum: WasmChecksum,
598 }
599 
600 /// The name of a function stored in the
601 /// [`ELF_NAME_DATA`](crate::obj::ELF_NAME_DATA) section.
602 #[derive(Serialize, Deserialize)]
603 pub struct FunctionName {
604     /// The Wasm function index of this function.
605     pub idx: FuncIndex,
606     /// The offset of the name in the
607     /// [`ELF_NAME_DATA`](crate::obj::ELF_NAME_DATA) section.
608     pub offset: u32,
609     /// The length of the name in bytes.
610     pub len: u32,
611 }
612 
613 /// Metadata associated with a compiled ELF artifact.
614 #[derive(Serialize, Deserialize)]
615 pub struct Metadata {
616     /// Whether or not the original wasm module contained debug information that
617     /// we skipped and did not parse.
618     pub has_unparsed_debuginfo: bool,
619 
620     /// Offset in the original wasm file to the code section.
621     pub code_section_offset: u64,
622 
623     /// Whether or not custom wasm-specific dwarf sections were inserted into
624     /// the ELF image.
625     ///
626     /// Note that even if this flag is `true` sections may be missing if they
627     /// weren't found in the original wasm module itself.
628     pub has_wasm_debuginfo: bool,
629 
630     /// Dwarf sections and the offsets at which they're stored in the
631     /// ELF_WASMTIME_DWARF
632     pub dwarf: Vec<(u8, Range<u64>)>,
633 }
634 
635 /// Value of a configured setting for a [`Compiler`](crate::Compiler)
636 #[derive(Serialize, Deserialize, Hash, Eq, PartialEq, Debug)]
637 pub enum FlagValue<'a> {
638     /// Name of the value that has been configured for this setting.
639     Enum(&'a str),
640     /// The numerical value of the configured settings.
641     Num(u8),
642     /// Whether the setting is on or off.
643     Bool(bool),
644 }
645 
646 impl fmt::Display for FlagValue<'_> {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result647     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
648         match self {
649             Self::Enum(v) => v.fmt(f),
650             Self::Num(v) => v.fmt(f),
651             Self::Bool(v) => v.fmt(f),
652         }
653     }
654 }
655 
656 /// Types of objects that can be created by `Compiler::object`
657 pub enum ObjectKind {
658     /// A core wasm compilation artifact
659     Module,
660     /// A component compilation artifact
661     Component,
662 }
663 
664 #[cfg(test)]
665 mod tests {
666     use super::*;
667     use crate::{DefinedFuncIndex, StaticModuleIndex};
668 
func_loc(range: Range<u32>) -> FunctionLoc669     fn func_loc(range: Range<u32>) -> FunctionLoc {
670         FunctionLoc {
671             start: range.start,
672             length: range.end - range.start,
673         }
674     }
675 
def_func_key(m: u32, f: u32) -> FuncKey676     fn def_func_key(m: u32, f: u32) -> FuncKey {
677         FuncKey::DefinedWasmFunction(
678             StaticModuleIndex::from_u32(m),
679             DefinedFuncIndex::from_u32(f),
680         )
681     }
682 
array_to_wasm_tramp_key(m: u32, f: u32) -> FuncKey683     fn array_to_wasm_tramp_key(m: u32, f: u32) -> FuncKey {
684         FuncKey::ArrayToWasmTrampoline(
685             StaticModuleIndex::from_u32(m),
686             DefinedFuncIndex::from_u32(f),
687         )
688     }
689 
make_test_table() -> CompiledFunctionsTable690     fn make_test_table() -> CompiledFunctionsTable {
691         let mut builder = CompiledFunctionsTableBuilder::new();
692 
693         builder
694             // ========= Dense =========
695             .push_func(def_func_key(0, 0), func_loc(0..10), FilePos::new(111))
696             .push_func(def_func_key(0, 1), func_loc(10..20), FilePos::new(222))
697             .push_func(def_func_key(0, 2), func_loc(20..30), FilePos::none())
698             // Gap in dense keys!
699             .push_func(def_func_key(0, 5), func_loc(30..40), FilePos::new(333))
700             // ========= Sparse =========
701             .push_func(
702                 array_to_wasm_tramp_key(0, 1),
703                 func_loc(100..110),
704                 FilePos::none(),
705             )
706             .push_func(
707                 array_to_wasm_tramp_key(0, 2),
708                 func_loc(110..120),
709                 FilePos::none(),
710             )
711             .push_func(
712                 array_to_wasm_tramp_key(0, 5),
713                 func_loc(120..130),
714                 FilePos::none(),
715             );
716 
717         builder.finish()
718     }
719 
720     #[test]
src_locs()721     fn src_locs() {
722         let table = make_test_table();
723 
724         for (key, expected) in [
725             (def_func_key(0, 0), Some(FilePos::new(111))),
726             (def_func_key(0, 1), Some(FilePos::new(222))),
727             (def_func_key(0, 2), None),
728             (def_func_key(0, 3), None),
729             (def_func_key(0, 4), None),
730             (def_func_key(0, 5), Some(FilePos::new(333))),
731             (array_to_wasm_tramp_key(0, 0), None),
732             (array_to_wasm_tramp_key(0, 1), None),
733             (array_to_wasm_tramp_key(0, 2), None),
734             (array_to_wasm_tramp_key(0, 3), None),
735             (array_to_wasm_tramp_key(0, 4), None),
736             (array_to_wasm_tramp_key(0, 5), None),
737         ] {
738             eprintln!("Checking key {key:?}");
739             let actual = table.src_loc(key);
740             assert_eq!(expected, actual);
741         }
742     }
743 
744     #[test]
func_locs()745     fn func_locs() {
746         let table = make_test_table();
747 
748         for (key, expected) in [
749             (def_func_key(0, 0), Some(0)),
750             (def_func_key(0, 1), Some(10)),
751             (def_func_key(0, 2), Some(20)),
752             (def_func_key(0, 3), None),
753             (def_func_key(0, 4), None),
754             (def_func_key(0, 5), Some(30)),
755             (array_to_wasm_tramp_key(0, 0), None),
756             (array_to_wasm_tramp_key(0, 1), Some(100)),
757             (array_to_wasm_tramp_key(0, 2), Some(110)),
758             (array_to_wasm_tramp_key(0, 3), None),
759             (array_to_wasm_tramp_key(0, 4), None),
760             (array_to_wasm_tramp_key(0, 5), Some(120)),
761         ] {
762             let actual = table.func_loc(key);
763             match (expected, actual) {
764                 (None, None) => {}
765                 (Some(expected), Some(actual)) => assert_eq!(expected, actual.start),
766                 (None, Some(actual)) => {
767                     panic!("expected no function location for {key:?}, got {actual:?}")
768                 }
769                 (Some(_), None) => {
770                     panic!("expected a function location for {key:?}, but got nothing")
771                 }
772             }
773         }
774     }
775 
776     #[test]
reverse_func_locs()777     fn reverse_func_locs() {
778         let table = make_test_table();
779 
780         for (range, expected) in [
781             (0..10, Some(def_func_key(0, 0))),
782             (10..20, Some(def_func_key(0, 1))),
783             (20..30, Some(def_func_key(0, 2))),
784             (30..40, Some(def_func_key(0, 5))),
785             (40..100, None),
786             (100..110, Some(array_to_wasm_tramp_key(0, 1))),
787             (110..120, Some(array_to_wasm_tramp_key(0, 2))),
788             (120..130, Some(array_to_wasm_tramp_key(0, 5))),
789             (140..150, None),
790         ] {
791             for i in range {
792                 eprintln!("Checking offset {i}");
793                 let actual = table.func_by_text_offset(i);
794                 assert_eq!(expected, actual);
795             }
796         }
797     }
798 
799     #[test]
reverse_lookups()800     fn reverse_lookups() {
801         use arbitrary::{Result, Unstructured};
802 
803         arbtest::arbtest(|u| run(u)).budget_ms(1_000);
804 
805         fn run(u: &mut Unstructured<'_>) -> Result<()> {
806             let mut funcs = Vec::new();
807 
808             // Build up a random set of functions with random indices.
809             for _ in 0..u.int_in_range(1..=200)? {
810                 let key = match u.int_in_range(0..=6)? {
811                     0 => FuncKey::DefinedWasmFunction(idx(u, 10)?, idx(u, 200)?),
812                     1 => FuncKey::ArrayToWasmTrampoline(idx(u, 10)?, idx(u, 200)?),
813                     2 => FuncKey::WasmToArrayTrampoline(idx(u, 100)?),
814                     3 => FuncKey::WasmToBuiltinTrampoline(u.arbitrary()?),
815                     4 => FuncKey::PulleyHostCall(u.arbitrary()?),
816                     5 => FuncKey::ComponentTrampoline(u.arbitrary()?, idx(u, 50)?),
817                     6 => FuncKey::ResourceDropTrampoline,
818                     _ => unreachable!(),
819                 };
820                 funcs.push(key);
821             }
822 
823             // Sort/dedup our list of `funcs` to satisfy the requirement of
824             // `CompiledFunctionsTableBuilder::push_func`.
825             funcs.sort();
826             funcs.dedup();
827 
828             let mut builder = CompiledFunctionsTableBuilder::new();
829             let mut size = 0;
830             let mut expected = Vec::new();
831             for key in funcs {
832                 let length = u.int_in_range(1..=10)?;
833                 for _ in 0..length {
834                     expected.push(key);
835                 }
836                 // println!("push {key:?} - {length}");
837                 builder.push_func(
838                     key,
839                     FunctionLoc {
840                         start: size,
841                         length,
842                     },
843                     FilePos::none(),
844                 );
845                 size += length;
846             }
847             let index = builder.finish();
848 
849             let mut expected = expected.iter();
850             for i in 0..size {
851                 // println!("lookup {i}");
852                 let actual = index.func_by_text_offset(i).unwrap();
853                 assert_eq!(Some(&actual), expected.next());
854             }
855 
856             Ok(())
857         }
858 
859         fn idx<T>(u: &mut Unstructured<'_>, max: usize) -> Result<T>
860         where
861             T: EntityRef,
862         {
863             Ok(T::new(u.int_in_range(0..=max - 1)?))
864         }
865     }
866 }
867