Coverage Report

Created: 2026-08-15 07:39

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/wasmtime/crates/cranelift/src/debug.rs
Line
Count
Source
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
12
use crate::CompiledFunctionMetadata;
13
use core::fmt;
14
use cranelift_codegen::isa::TargetIsa;
15
use object::write::SymbolId;
16
use std::collections::HashMap;
17
use 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)]
24
pub 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
40
type 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.
48
pub 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: &'a dyn Fn(
60
        StaticModuleIndex,
61
        DefinedFuncIndex,
62
    ) -> (Option<SymbolId>, &'a CompiledFunctionMetadata),
63
64
    /// Optionally-specified `*.dwp` file, currently only supported for core
65
    /// wasm modules.
66
    dwarf_package_bytes: Option<&'a [u8]>,
67
68
    /// Compilation settings used when producing functions.
69
    tunables: &'a Tunables,
70
71
    /// Translation between `SymbolId` and a `usize`-based symbol which gimli
72
    /// uses.
73
    symbol_index_to_id: Vec<Option<SymbolId>>,
74
    symbol_id_to_index: HashMap<SymbolId, (usize, StaticModuleIndex, DefinedFuncIndex)>,
75
76
    /// The `ModuleMemoryOffset` for each module within `translations`.
77
    ///
78
    /// Note that this doesn't support multi-memory at this time.
79
    module_memory_offsets: PrimaryMap<StaticModuleIndex, ModuleMemoryOffset>,
80
}
81
82
impl<'a> Compilation<'a> {
83
0
    pub fn new(
84
0
        isa: &dyn TargetIsa,
85
0
        translations: &'a PrimaryMap<StaticModuleIndex, ModuleTranslation<'a>>,
86
0
        get_func: &'a dyn Fn(
87
0
            StaticModuleIndex,
88
0
            DefinedFuncIndex,
89
0
        ) -> (Option<SymbolId>, &'a CompiledFunctionMetadata),
90
0
        dwarf_package_bytes: Option<&'a [u8]>,
91
0
        tunables: &'a Tunables,
92
0
    ) -> Compilation<'a> {
93
        // Build the `module_memory_offsets` map based on the modules in
94
        // `translations`.
95
0
        let mut module_memory_offsets = PrimaryMap::new();
96
0
        for (i, translation) in translations {
97
0
            let ofs = VMOffsets::new(
98
0
                isa.triple().architecture.pointer_width().unwrap().bytes(),
99
0
                &translation.module,
100
            );
101
102
0
            let memory_offset = if ofs.num_imported_memories > 0 {
103
0
                let index = MemoryIndex::new(0);
104
0
                ModuleMemoryOffset::Imported {
105
0
                    offset_to_vm_memory_definition: ofs.imported_memories().at(index)
106
0
                        + u32::from(ofs.ptr.vm_memory_import().from()),
107
0
                    offset_to_memory_base: ofs.ptr.vm_memory_definition().base().into(),
108
0
                }
109
0
            } else if ofs.num_owned_memories > 0 {
110
0
                let index = OwnedMemoryIndex::new(0);
111
0
                ModuleMemoryOffset::Defined(
112
0
                    ofs.owned_memories().at(index)
113
0
                        + u32::from(ofs.ptr.vm_memory_definition().base()),
114
0
                )
115
0
            } else if ofs.num_defined_memories > 0 {
116
0
                let index = DefinedMemoryIndex::new(0);
117
0
                ModuleMemoryOffset::Imported {
118
0
                    offset_to_vm_memory_definition: ofs.memories().at(index),
119
0
                    offset_to_memory_base: ofs.ptr.vm_memory_definition().base().into(),
120
0
                }
121
            } else {
122
0
                ModuleMemoryOffset::None
123
            };
124
0
            let j = module_memory_offsets.push(memory_offset);
125
0
            assert_eq!(i, j);
126
        }
127
128
        // Build the `symbol <=> usize` mappings
129
0
        let mut symbol_index_to_id = Vec::new();
130
0
        let mut symbol_id_to_index = HashMap::new();
131
132
0
        for (module, translation) in translations {
133
0
            for func in translation.module.defined_func_indices() {
134
0
                let (sym, _func) = get_func(module, func);
135
0
                if let Some(sym) = sym {
136
0
                    symbol_id_to_index.insert(sym, (symbol_index_to_id.len(), module, func));
137
0
                }
138
0
                symbol_index_to_id.push(sym);
139
            }
140
        }
141
142
0
        Compilation {
143
0
            translations,
144
0
            get_func,
145
0
            dwarf_package_bytes,
146
0
            tunables,
147
0
            symbol_index_to_id,
148
0
            symbol_id_to_index,
149
0
            module_memory_offsets,
150
0
        }
151
0
    }
152
153
    /// Returns an iterator over all function indexes present in this
154
    /// compilation.
155
    ///
156
    /// Each function is additionally accompanied with its module index.
157
0
    fn indexes(&self) -> impl Iterator<Item = (StaticModuleIndex, DefinedFuncIndex)> + use<'_> {
158
0
        self.translations
159
0
            .iter()
160
0
            .flat_map(|(i, t)| t.module.defined_func_indices().map(move |j| (i, j)))
161
0
    }
162
163
    /// Returns an iterator of all functions with their module, symbol, and
164
    /// function metadata that were produced during compilation.
165
0
    fn functions(
166
0
        &self,
167
0
    ) -> impl Iterator<
168
0
        Item = (
169
0
            StaticModuleIndex,
170
0
            Option<usize>,
171
0
            &'a CompiledFunctionMetadata,
172
0
        ),
173
0
    > + '_ {
174
0
        self.indexes().map(move |(module, func)| {
175
0
            let (sym, func) = self.function(module, func);
176
0
            (module, sym, func)
177
0
        })
178
0
    }
179
180
    /// Returns the symbol and metadata associated with a specific function.
181
0
    fn function(
182
0
        &self,
183
0
        module: StaticModuleIndex,
184
0
        func: DefinedFuncIndex,
185
0
    ) -> (Option<usize>, &'a CompiledFunctionMetadata) {
186
0
        let (sym, func) = (self.get_func)(module, func);
187
0
        (sym.map(|sym| self.symbol_id_to_index[&sym].0), func)
188
0
    }
189
190
    /// Maps a `usize`-based symbol used by gimli to the object-based
191
    /// `SymbolId`.
192
0
    pub fn symbol_id(&self, sym: usize) -> Option<SymbolId> {
193
0
        self.symbol_index_to_id[sym]
194
0
    }
195
}
196
197
impl<'a> fmt::Debug for Compilation<'a> {
198
    // Sample output: '[#0: OneModule, #1: TwoModule, #3]'.
199
0
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
200
0
        write!(f, "[")?;
201
0
        let mut is_first_module = true;
202
0
        for (i, translation) in self.translations {
203
0
            if !is_first_module {
204
0
                write!(f, ", ")?;
205
0
            } else {
206
0
                is_first_module = false;
207
0
            }
208
0
            write!(f, "#{}", i.as_u32())?;
209
0
            if let Some(name) = translation.debuginfo.name_section.module_name {
210
0
                write!(f, ": {name}")?;
211
0
            }
212
        }
213
0
        write!(f, "]")
214
0
    }
215
}
216
217
pub use write_debuginfo::{DwarfSectionRelocTarget, emit_dwarf};
218
219
mod gc;
220
mod transform;
221
mod write_debuginfo;