/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; |