wasmtime_cranelift/
debug.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
//! Debug utils for WebAssembly using Cranelift.

// FIXME: this whole crate opts-in to these two noisier-than-default lints, but
// this module has lots of hits on this warning which aren't the easiest to
// resolve. Ideally all warnings would be resolved here though.
#![expect(
    clippy::cast_possible_truncation,
    clippy::cast_sign_loss,
    reason = "haven't had a chance to fix these yet"
)]

use crate::CompiledFunctionMetadata;
use core::fmt;
use cranelift_codegen::isa::TargetIsa;
use object::write::SymbolId;
use std::collections::HashMap;
use wasmtime_environ::{
    DefinedFuncIndex, DefinedMemoryIndex, EntityRef, MemoryIndex, ModuleTranslation,
    OwnedMemoryIndex, PrimaryMap, PtrSize, StaticModuleIndex, Tunables, VMOffsets,
};

/// Memory definition offset in the VMContext structure.
#[derive(Debug, Clone)]
pub enum ModuleMemoryOffset {
    /// Not available.
    None,
    /// Offset to the defined memory.
    Defined(u32),
    /// This memory is imported.
    Imported {
        /// Offset, in bytes, to the `*mut VMMemoryDefinition` structure within
        /// `VMContext`.
        offset_to_vm_memory_definition: u32,
        /// Offset, in bytes within `VMMemoryDefinition` where the `base` field
        /// lies.
        offset_to_memory_base: u32,
    },
}

type Reader<'input> = gimli::EndianSlice<'input, gimli::LittleEndian>;

/// "Package structure" to collect together various artifacts/results of a
/// compilation.
///
/// This structure is threaded through a number of top-level functions of DWARF
/// processing within in this submodule to pass along all the bits-and-pieces of
/// the compilation context.
pub struct Compilation<'a> {
    /// All module translations which were present in this compilation.
    ///
    /// This map has one entry for core wasm modules and may have multiple (or
    /// zero) for components.
    translations: &'a PrimaryMap<StaticModuleIndex, ModuleTranslation<'a>>,

    /// Accessor of a particular compiled function for a module.
    ///
    /// This returns the `object`-based-symbol for the function as well as the
    /// `&CompiledFunction`.
    get_func:
        &'a dyn Fn(StaticModuleIndex, DefinedFuncIndex) -> (SymbolId, &'a CompiledFunctionMetadata),

    /// Optionally-specified `*.dwp` file, currently only supported for core
    /// wasm modules.
    dwarf_package_bytes: Option<&'a [u8]>,

    /// Compilation settings used when producing functions.
    tunables: &'a Tunables,

    /// Translation between `SymbolId` and a `usize`-based symbol which gimli
    /// uses.
    symbol_index_to_id: Vec<SymbolId>,
    symbol_id_to_index: HashMap<SymbolId, (usize, StaticModuleIndex, DefinedFuncIndex)>,

    /// The `ModuleMemoryOffset` for each module within `translations`.
    ///
    /// Note that this doesn't support multi-memory at this time.
    module_memory_offsets: PrimaryMap<StaticModuleIndex, ModuleMemoryOffset>,
}

impl<'a> Compilation<'a> {
    pub fn new(
        isa: &dyn TargetIsa,
        translations: &'a PrimaryMap<StaticModuleIndex, ModuleTranslation<'a>>,
        get_func: &'a dyn Fn(
            StaticModuleIndex,
            DefinedFuncIndex,
        ) -> (SymbolId, &'a CompiledFunctionMetadata),
        dwarf_package_bytes: Option<&'a [u8]>,
        tunables: &'a Tunables,
    ) -> Compilation<'a> {
        // Build the `module_memory_offsets` map based on the modules in
        // `translations`.
        let mut module_memory_offsets = PrimaryMap::new();
        for (i, translation) in translations {
            let ofs = VMOffsets::new(
                isa.triple().architecture.pointer_width().unwrap().bytes(),
                &translation.module,
            );

            let memory_offset = if ofs.num_imported_memories > 0 {
                let index = MemoryIndex::new(0);
                ModuleMemoryOffset::Imported {
                    offset_to_vm_memory_definition: ofs.vmctx_vmmemory_import(index)
                        + u32::from(ofs.vmmemory_import_from()),
                    offset_to_memory_base: ofs.ptr.vmmemory_definition_base().into(),
                }
            } else if ofs.num_owned_memories > 0 {
                let index = OwnedMemoryIndex::new(0);
                ModuleMemoryOffset::Defined(ofs.vmctx_vmmemory_definition_base(index))
            } else if ofs.num_defined_memories > 0 {
                let index = DefinedMemoryIndex::new(0);
                ModuleMemoryOffset::Imported {
                    offset_to_vm_memory_definition: ofs.vmctx_vmmemory_pointer(index),
                    offset_to_memory_base: ofs.ptr.vmmemory_definition_base().into(),
                }
            } else {
                ModuleMemoryOffset::None
            };
            let j = module_memory_offsets.push(memory_offset);
            assert_eq!(i, j);
        }

        // Build the `symbol <=> usize` mappings
        let mut symbol_index_to_id = Vec::new();
        let mut symbol_id_to_index = HashMap::new();

        for (module, translation) in translations {
            for func in translation.module.defined_func_indices() {
                let (sym, _func) = get_func(module, func);
                symbol_id_to_index.insert(sym, (symbol_index_to_id.len(), module, func));
                symbol_index_to_id.push(sym);
            }
        }

        Compilation {
            translations,
            get_func,
            dwarf_package_bytes,
            tunables,
            symbol_index_to_id,
            symbol_id_to_index,
            module_memory_offsets,
        }
    }

    /// Returns an iterator over all function indexes present in this
    /// compilation.
    ///
    /// Each function is additionally accompanied with its module index.
    fn indexes(&self) -> impl Iterator<Item = (StaticModuleIndex, DefinedFuncIndex)> + use<'_> {
        self.translations
            .iter()
            .flat_map(|(i, t)| t.module.defined_func_indices().map(move |j| (i, j)))
    }

    /// Returns an iterator of all functions with their module, symbol, and
    /// function metadata that were produced during compilation.
    fn functions(
        &self,
    ) -> impl Iterator<Item = (StaticModuleIndex, usize, &'a CompiledFunctionMetadata)> + '_ {
        self.indexes().map(move |(module, func)| {
            let (sym, func) = self.function(module, func);
            (module, sym, func)
        })
    }

    /// Returns the symbol and metadata associated with a specific function.
    fn function(
        &self,
        module: StaticModuleIndex,
        func: DefinedFuncIndex,
    ) -> (usize, &'a CompiledFunctionMetadata) {
        let (sym, func) = (self.get_func)(module, func);
        (self.symbol_id_to_index[&sym].0, func)
    }

    /// Maps a `usize`-based symbol used by gimli to the object-based
    /// `SymbolId`.
    pub fn symbol_id(&self, sym: usize) -> SymbolId {
        self.symbol_index_to_id[sym]
    }
}

impl<'a> fmt::Debug for Compilation<'a> {
    // Sample output: '[#0: OneModule, #1: TwoModule, #3]'.
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "[")?;
        let mut is_first_module = true;
        for (i, translation) in self.translations {
            if !is_first_module {
                write!(f, ", ")?;
            } else {
                is_first_module = false;
            }
            write!(f, "#{}", i.as_u32())?;
            if let Some(name) = translation.debuginfo.name_section.module_name {
                write!(f, ": {name}")?;
            }
        }
        write!(f, "]")
    }
}

pub use write_debuginfo::{emit_dwarf, DwarfSectionRelocTarget};

mod gc;
mod transform;
mod write_debuginfo;