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