wasmtime_cranelift/
debug.rs

1//! Debug utils for WebAssembly using Cranelift.
2
3// FIXME: this whole crate opts-in to these two noisier-than-default lints, but
4// this module has lots of hits on this warning which aren't the easiest to
5// resolve. Ideally all warnings would be resolved here though.
6#![expect(
7    clippy::cast_possible_truncation,
8    clippy::cast_sign_loss,
9    reason = "haven't had a chance to fix these yet"
10)]
11
12use crate::CompiledFunctionMetadata;
13use core::fmt;
14use cranelift_codegen::isa::TargetIsa;
15use object::write::SymbolId;
16use std::collections::HashMap;
17use wasmtime_environ::{
18    DefinedFuncIndex, DefinedMemoryIndex, EntityRef, MemoryIndex, ModuleTranslation,
19    OwnedMemoryIndex, PrimaryMap, PtrSize, StaticModuleIndex, Tunables, VMOffsets,
20};
21
22/// Memory definition offset in the VMContext structure.
23#[derive(Debug, Clone)]
24pub enum ModuleMemoryOffset {
25    /// Not available.
26    None,
27    /// Offset to the defined memory.
28    Defined(u32),
29    /// This memory is imported.
30    Imported {
31        /// Offset, in bytes, to the `*mut VMMemoryDefinition` structure within
32        /// `VMContext`.
33        offset_to_vm_memory_definition: u32,
34        /// Offset, in bytes within `VMMemoryDefinition` where the `base` field
35        /// lies.
36        offset_to_memory_base: u32,
37    },
38}
39
40type Reader<'input> = gimli::EndianSlice<'input, gimli::LittleEndian>;
41
42/// "Package structure" to collect together various artifacts/results of a
43/// compilation.
44///
45/// This structure is threaded through a number of top-level functions of DWARF
46/// processing within in this submodule to pass along all the bits-and-pieces of
47/// the compilation context.
48pub struct Compilation<'a> {
49    /// All module translations which were present in this compilation.
50    ///
51    /// This map has one entry for core wasm modules and may have multiple (or
52    /// zero) for components.
53    translations: &'a PrimaryMap<StaticModuleIndex, ModuleTranslation<'a>>,
54
55    /// Accessor of a particular compiled function for a module.
56    ///
57    /// This returns the `object`-based-symbol for the function as well as the
58    /// `&CompiledFunction`.
59    get_func:
60        &'a dyn Fn(StaticModuleIndex, DefinedFuncIndex) -> (SymbolId, &'a CompiledFunctionMetadata),
61
62    /// Optionally-specified `*.dwp` file, currently only supported for core
63    /// wasm modules.
64    dwarf_package_bytes: Option<&'a [u8]>,
65
66    /// Compilation settings used when producing functions.
67    tunables: &'a Tunables,
68
69    /// Translation between `SymbolId` and a `usize`-based symbol which gimli
70    /// uses.
71    symbol_index_to_id: Vec<SymbolId>,
72    symbol_id_to_index: HashMap<SymbolId, (usize, StaticModuleIndex, DefinedFuncIndex)>,
73
74    /// The `ModuleMemoryOffset` for each module within `translations`.
75    ///
76    /// Note that this doesn't support multi-memory at this time.
77    module_memory_offsets: PrimaryMap<StaticModuleIndex, ModuleMemoryOffset>,
78}
79
80impl<'a> Compilation<'a> {
81    pub fn new(
82        isa: &dyn TargetIsa,
83        translations: &'a PrimaryMap<StaticModuleIndex, ModuleTranslation<'a>>,
84        get_func: &'a dyn Fn(
85            StaticModuleIndex,
86            DefinedFuncIndex,
87        ) -> (SymbolId, &'a CompiledFunctionMetadata),
88        dwarf_package_bytes: Option<&'a [u8]>,
89        tunables: &'a Tunables,
90    ) -> Compilation<'a> {
91        // Build the `module_memory_offsets` map based on the modules in
92        // `translations`.
93        let mut module_memory_offsets = PrimaryMap::new();
94        for (i, translation) in translations {
95            let ofs = VMOffsets::new(
96                isa.triple().architecture.pointer_width().unwrap().bytes(),
97                &translation.module,
98            );
99
100            let memory_offset = if ofs.num_imported_memories > 0 {
101                let index = MemoryIndex::new(0);
102                ModuleMemoryOffset::Imported {
103                    offset_to_vm_memory_definition: ofs.vmctx_vmmemory_import(index)
104                        + u32::from(ofs.vmmemory_import_from()),
105                    offset_to_memory_base: ofs.ptr.vmmemory_definition_base().into(),
106                }
107            } else if ofs.num_owned_memories > 0 {
108                let index = OwnedMemoryIndex::new(0);
109                ModuleMemoryOffset::Defined(ofs.vmctx_vmmemory_definition_base(index))
110            } else if ofs.num_defined_memories > 0 {
111                let index = DefinedMemoryIndex::new(0);
112                ModuleMemoryOffset::Imported {
113                    offset_to_vm_memory_definition: ofs.vmctx_vmmemory_pointer(index),
114                    offset_to_memory_base: ofs.ptr.vmmemory_definition_base().into(),
115                }
116            } else {
117                ModuleMemoryOffset::None
118            };
119            let j = module_memory_offsets.push(memory_offset);
120            assert_eq!(i, j);
121        }
122
123        // Build the `symbol <=> usize` mappings
124        let mut symbol_index_to_id = Vec::new();
125        let mut symbol_id_to_index = HashMap::new();
126
127        for (module, translation) in translations {
128            for func in translation.module.defined_func_indices() {
129                let (sym, _func) = get_func(module, func);
130                symbol_id_to_index.insert(sym, (symbol_index_to_id.len(), module, func));
131                symbol_index_to_id.push(sym);
132            }
133        }
134
135        Compilation {
136            translations,
137            get_func,
138            dwarf_package_bytes,
139            tunables,
140            symbol_index_to_id,
141            symbol_id_to_index,
142            module_memory_offsets,
143        }
144    }
145
146    /// Returns an iterator over all function indexes present in this
147    /// compilation.
148    ///
149    /// Each function is additionally accompanied with its module index.
150    fn indexes(&self) -> impl Iterator<Item = (StaticModuleIndex, DefinedFuncIndex)> + use<'_> {
151        self.translations
152            .iter()
153            .flat_map(|(i, t)| t.module.defined_func_indices().map(move |j| (i, j)))
154    }
155
156    /// Returns an iterator of all functions with their module, symbol, and
157    /// function metadata that were produced during compilation.
158    fn functions(
159        &self,
160    ) -> impl Iterator<Item = (StaticModuleIndex, usize, &'a CompiledFunctionMetadata)> + '_ {
161        self.indexes().map(move |(module, func)| {
162            let (sym, func) = self.function(module, func);
163            (module, sym, func)
164        })
165    }
166
167    /// Returns the symbol and metadata associated with a specific function.
168    fn function(
169        &self,
170        module: StaticModuleIndex,
171        func: DefinedFuncIndex,
172    ) -> (usize, &'a CompiledFunctionMetadata) {
173        let (sym, func) = (self.get_func)(module, func);
174        (self.symbol_id_to_index[&sym].0, func)
175    }
176
177    /// Maps a `usize`-based symbol used by gimli to the object-based
178    /// `SymbolId`.
179    pub fn symbol_id(&self, sym: usize) -> SymbolId {
180        self.symbol_index_to_id[sym]
181    }
182}
183
184impl<'a> fmt::Debug for Compilation<'a> {
185    // Sample output: '[#0: OneModule, #1: TwoModule, #3]'.
186    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
187        write!(f, "[")?;
188        let mut is_first_module = true;
189        for (i, translation) in self.translations {
190            if !is_first_module {
191                write!(f, ", ")?;
192            } else {
193                is_first_module = false;
194            }
195            write!(f, "#{}", i.as_u32())?;
196            if let Some(name) = translation.debuginfo.name_section.module_name {
197                write!(f, ": {name}")?;
198            }
199        }
200        write!(f, "]")
201    }
202}
203
204pub use write_debuginfo::{emit_dwarf, DwarfSectionRelocTarget};
205
206mod gc;
207mod transform;
208mod write_debuginfo;