Skip to main content

wasmtime_environ/
module_artifacts.rs

1//! Definitions of runtime structures and metadata which are serialized into ELF
2//! with `bincode` as part of a module's compilation process.
3
4use crate::prelude::*;
5use crate::{
6    EntityRef, FilePos, FuncIndex, FuncKey, FuncKeyIndex, FuncKeyKind, FuncKeyNamespace, Module,
7    PanicOnOom as _,
8};
9use core::ops::Range;
10use core::{fmt, u32};
11use core::{iter, str};
12use serde_derive::{Deserialize, Serialize};
13#[cfg(feature = "rr")]
14use 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)]
19pub 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
27impl FunctionLoc {
28    /// Is this an empty function location?
29    #[inline]
30    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)]
40pub struct WasmChecksum([u8; 32]);
41
42impl 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")]
47    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"))]
58    pub fn from_binary(_: &[u8], _: bool) -> WasmChecksum {
59        WasmChecksum::default()
60    }
61}
62
63impl core::ops::Deref for WasmChecksum {
64    type Target = [u8; 32];
65
66    fn deref(&self) -> &Self::Target {
67        &self.0
68    }
69}
70
71/// A builder for a `CompiledFunctionsTable`.
72pub struct CompiledFunctionsTableBuilder {
73    inner: CompiledFunctionsTable,
74}
75
76impl CompiledFunctionsTableBuilder {
77    /// Create a new builder.
78    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
92    fn last_namespace(&self) -> Option<FuncKeyNamespace> {
93        let (_, &ns) = self.inner.namespaces.last()?;
94        Some(ns)
95    }
96
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
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.
125    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`.
234    pub fn finish(self) -> CompiledFunctionsTable {
235        self.inner
236    }
237}
238
239#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
240struct NamespaceIndex(u32);
241cranelift_entity::entity_impl!(NamespaceIndex);
242
243#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
244struct FuncLocIndex(u32);
245cranelift_entity::entity_impl!(FuncLocIndex);
246
247#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
248struct SparseIndex(u32);
249cranelift_entity::entity_impl!(SparseIndex);
250
251#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
252struct SrcLocIndex(u32);
253cranelift_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)]
287pub 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
351impl CompiledFunctionsTable {
352    #[inline]
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]
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
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
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]
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]
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
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.
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.
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.
547    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
556            | FuncKeyKind::ModuleStartup => false,
557
558            // `FactInlineIntrinsic` never names a compiled function, so it is
559            // never classified here; group it with the other intrinsics.
560            #[cfg(feature = "component-model")]
561            FuncKeyKind::ComponentTrampoline
562            | FuncKeyKind::ResourceDropTrampoline
563            | FuncKeyKind::UnsafeIntrinsic => true,
564        }
565    }
566
567    /// Whether the given function kind has source locations or not.
568    fn has_src_locs(kind: FuncKeyKind) -> bool {
569        match kind {
570            FuncKeyKind::DefinedWasmFunction => true,
571            FuncKeyKind::ArrayToWasmTrampoline
572            | FuncKeyKind::WasmToArrayTrampoline
573            | FuncKeyKind::WasmToBuiltinTrampoline
574            | FuncKeyKind::PatchableToBuiltinTrampoline
575            | FuncKeyKind::PulleyHostCall
576            | FuncKeyKind::ModuleStartup => false,
577            #[cfg(feature = "component-model")]
578            FuncKeyKind::ComponentTrampoline
579            | FuncKeyKind::ResourceDropTrampoline
580            | FuncKeyKind::UnsafeIntrinsic => false,
581        }
582    }
583}
584
585/// Secondary in-memory results of module compilation.
586///
587/// This opaque structure can be optionally passed back to
588/// `CompiledModule::from_artifacts` to avoid decoding extra information there.
589#[derive(Serialize, Deserialize)]
590pub struct CompiledModuleInfo {
591    /// Type information about the compiled WebAssembly module.
592    pub module: Module,
593
594    /// General compilation metadata.
595    pub meta: Metadata,
596
597    /// Sorted list, by function index, of names we have for this module.
598    pub func_names: Vec<FunctionName>,
599
600    /// Checksum of the source Wasm binary from which this module was compiled.
601    pub checksum: WasmChecksum,
602}
603
604/// The name of a function stored in the
605/// [`ELF_NAME_DATA`](crate::obj::ELF_NAME_DATA) section.
606#[derive(Serialize, Deserialize)]
607pub struct FunctionName {
608    /// The Wasm function index of this function.
609    pub idx: FuncIndex,
610    /// The offset of the name in the
611    /// [`ELF_NAME_DATA`](crate::obj::ELF_NAME_DATA) section.
612    pub offset: u32,
613    /// The length of the name in bytes.
614    pub len: u32,
615}
616
617/// Metadata associated with a compiled ELF artifact.
618#[derive(Serialize, Deserialize)]
619pub struct Metadata {
620    /// Whether or not the original wasm module contained debug information that
621    /// we skipped and did not parse.
622    pub has_unparsed_debuginfo: bool,
623
624    /// Offset in the original wasm file to the code section.
625    pub code_section_offset: u64,
626
627    /// Whether or not custom wasm-specific dwarf sections were inserted into
628    /// the ELF image.
629    ///
630    /// Note that even if this flag is `true` sections may be missing if they
631    /// weren't found in the original wasm module itself.
632    pub has_wasm_debuginfo: bool,
633
634    /// Dwarf sections and the offsets at which they're stored in the
635    /// ELF_WASMTIME_DWARF
636    pub dwarf: Vec<(u8, Range<u64>)>,
637}
638
639/// Value of a configured setting for a [`Compiler`](crate::Compiler)
640#[derive(Serialize, Deserialize, Hash, Eq, PartialEq, Debug)]
641pub enum FlagValue<'a> {
642    /// Name of the value that has been configured for this setting.
643    Enum(&'a str),
644    /// The numerical value of the configured settings.
645    Num(u8),
646    /// Whether the setting is on or off.
647    Bool(bool),
648}
649
650impl fmt::Display for FlagValue<'_> {
651    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
652        match self {
653            Self::Enum(v) => v.fmt(f),
654            Self::Num(v) => v.fmt(f),
655            Self::Bool(v) => v.fmt(f),
656        }
657    }
658}
659
660/// Types of objects that can be created by `Compiler::object`
661pub enum ObjectKind {
662    /// A core wasm compilation artifact
663    Module,
664    /// A component compilation artifact
665    Component,
666}
667
668#[cfg(test)]
669mod tests {
670    use super::*;
671    use crate::{DefinedFuncIndex, StaticModuleIndex};
672
673    fn func_loc(range: Range<u32>) -> FunctionLoc {
674        FunctionLoc {
675            start: range.start,
676            length: range.end - range.start,
677        }
678    }
679
680    fn def_func_key(m: u32, f: u32) -> FuncKey {
681        FuncKey::DefinedWasmFunction(
682            StaticModuleIndex::from_u32(m),
683            DefinedFuncIndex::from_u32(f),
684        )
685    }
686
687    fn array_to_wasm_tramp_key(m: u32, f: u32) -> FuncKey {
688        FuncKey::ArrayToWasmTrampoline(
689            StaticModuleIndex::from_u32(m),
690            DefinedFuncIndex::from_u32(f),
691        )
692    }
693
694    fn make_test_table() -> CompiledFunctionsTable {
695        let mut builder = CompiledFunctionsTableBuilder::new();
696
697        builder
698            // ========= Dense =========
699            .push_func(def_func_key(0, 0), func_loc(0..10), FilePos::new(111))
700            .push_func(def_func_key(0, 1), func_loc(10..20), FilePos::new(222))
701            .push_func(def_func_key(0, 2), func_loc(20..30), FilePos::none())
702            // Gap in dense keys!
703            .push_func(def_func_key(0, 5), func_loc(30..40), FilePos::new(333))
704            // ========= Sparse =========
705            .push_func(
706                array_to_wasm_tramp_key(0, 1),
707                func_loc(100..110),
708                FilePos::none(),
709            )
710            .push_func(
711                array_to_wasm_tramp_key(0, 2),
712                func_loc(110..120),
713                FilePos::none(),
714            )
715            .push_func(
716                array_to_wasm_tramp_key(0, 5),
717                func_loc(120..130),
718                FilePos::none(),
719            );
720
721        builder.finish()
722    }
723
724    #[test]
725    fn src_locs() {
726        let table = make_test_table();
727
728        for (key, expected) in [
729            (def_func_key(0, 0), Some(FilePos::new(111))),
730            (def_func_key(0, 1), Some(FilePos::new(222))),
731            (def_func_key(0, 2), None),
732            (def_func_key(0, 3), None),
733            (def_func_key(0, 4), None),
734            (def_func_key(0, 5), Some(FilePos::new(333))),
735            (array_to_wasm_tramp_key(0, 0), None),
736            (array_to_wasm_tramp_key(0, 1), None),
737            (array_to_wasm_tramp_key(0, 2), None),
738            (array_to_wasm_tramp_key(0, 3), None),
739            (array_to_wasm_tramp_key(0, 4), None),
740            (array_to_wasm_tramp_key(0, 5), None),
741        ] {
742            eprintln!("Checking key {key:?}");
743            let actual = table.src_loc(key);
744            assert_eq!(expected, actual);
745        }
746    }
747
748    #[test]
749    fn func_locs() {
750        let table = make_test_table();
751
752        for (key, expected) in [
753            (def_func_key(0, 0), Some(0)),
754            (def_func_key(0, 1), Some(10)),
755            (def_func_key(0, 2), Some(20)),
756            (def_func_key(0, 3), None),
757            (def_func_key(0, 4), None),
758            (def_func_key(0, 5), Some(30)),
759            (array_to_wasm_tramp_key(0, 0), None),
760            (array_to_wasm_tramp_key(0, 1), Some(100)),
761            (array_to_wasm_tramp_key(0, 2), Some(110)),
762            (array_to_wasm_tramp_key(0, 3), None),
763            (array_to_wasm_tramp_key(0, 4), None),
764            (array_to_wasm_tramp_key(0, 5), Some(120)),
765        ] {
766            let actual = table.func_loc(key);
767            match (expected, actual) {
768                (None, None) => {}
769                (Some(expected), Some(actual)) => assert_eq!(expected, actual.start),
770                (None, Some(actual)) => {
771                    panic!("expected no function location for {key:?}, got {actual:?}")
772                }
773                (Some(_), None) => {
774                    panic!("expected a function location for {key:?}, but got nothing")
775                }
776            }
777        }
778    }
779
780    #[test]
781    fn reverse_func_locs() {
782        let table = make_test_table();
783
784        for (range, expected) in [
785            (0..10, Some(def_func_key(0, 0))),
786            (10..20, Some(def_func_key(0, 1))),
787            (20..30, Some(def_func_key(0, 2))),
788            (30..40, Some(def_func_key(0, 5))),
789            (40..100, None),
790            (100..110, Some(array_to_wasm_tramp_key(0, 1))),
791            (110..120, Some(array_to_wasm_tramp_key(0, 2))),
792            (120..130, Some(array_to_wasm_tramp_key(0, 5))),
793            (140..150, None),
794        ] {
795            for i in range {
796                eprintln!("Checking offset {i}");
797                let actual = table.func_by_text_offset(i);
798                assert_eq!(expected, actual);
799            }
800        }
801    }
802
803    #[test]
804    fn reverse_lookups() {
805        use arbitrary::{Result, Unstructured};
806
807        arbtest::arbtest(|u| run(u)).budget_ms(1_000);
808
809        fn run(u: &mut Unstructured<'_>) -> Result<()> {
810            let mut funcs = Vec::new();
811
812            // Build up a random set of functions with random indices.
813            for _ in 0..u.int_in_range(1..=200)? {
814                #[cfg(feature = "component-model")]
815                let choice = u.int_in_range(0..=6)?;
816                #[cfg(not(feature = "component-model"))]
817                let choice = u.int_in_range(0..=4)?;
818
819                let key = match choice {
820                    0 => FuncKey::DefinedWasmFunction(idx(u, 10)?, idx(u, 200)?),
821                    1 => FuncKey::ArrayToWasmTrampoline(idx(u, 10)?, idx(u, 200)?),
822                    2 => FuncKey::WasmToArrayTrampoline(idx(u, 100)?),
823                    3 => FuncKey::WasmToBuiltinTrampoline(u.arbitrary()?),
824                    4 => FuncKey::PulleyHostCall(u.arbitrary()?),
825                    #[cfg(feature = "component-model")]
826                    5 => FuncKey::ComponentTrampoline(u.arbitrary()?, idx(u, 50)?),
827                    #[cfg(feature = "component-model")]
828                    6 => FuncKey::ResourceDropTrampoline,
829                    _ => unreachable!(),
830                };
831                funcs.push(key);
832            }
833
834            // Sort/dedup our list of `funcs` to satisfy the requirement of
835            // `CompiledFunctionsTableBuilder::push_func`.
836            funcs.sort();
837            funcs.dedup();
838
839            let mut builder = CompiledFunctionsTableBuilder::new();
840            let mut size = 0;
841            let mut expected = Vec::new();
842            for key in funcs {
843                let length = u.int_in_range(1..=10)?;
844                for _ in 0..length {
845                    expected.push(key);
846                }
847                // println!("push {key:?} - {length}");
848                builder.push_func(
849                    key,
850                    FunctionLoc {
851                        start: size,
852                        length,
853                    },
854                    FilePos::none(),
855                );
856                size += length;
857            }
858            let index = builder.finish();
859
860            let mut expected = expected.iter();
861            for i in 0..size {
862                // println!("lookup {i}");
863                let actual = index.func_by_text_offset(i).unwrap();
864                assert_eq!(Some(&actual), expected.next());
865            }
866
867            Ok(())
868        }
869
870        fn idx<T>(u: &mut Unstructured<'_>, max: usize) -> Result<T>
871        where
872            T: EntityRef,
873        {
874            Ok(T::new(u.int_in_range(0..=max - 1)?))
875        }
876    }
877}