1 use self::debug_transform_logging::dbi_log;
2 use self::refs::DebugInfoRefsMap;
3 use self::simulate::generate_simulated_dwarf;
4 use self::unit::clone_unit;
5 use crate::debug::gc::build_dependencies;
6 use crate::debug::{Compilation, Reader};
7 use cranelift_codegen::isa::TargetIsa;
8 use gimli::{Dwarf, DwarfPackage, LittleEndian, Section, Unit, UnitRef, UnitSectionOffset, write};
9 use std::{collections::HashSet, fmt::Debug};
10 use synthetic::ModuleSyntheticUnit;
11 use thiserror::Error;
12 use wasmtime_environ::error::Error;
13 use wasmtime_environ::{
14     DefinedFuncIndex, ModuleTranslation, PrimaryMap, StaticModuleIndex, Tunables, prelude::*,
15 };
16 
17 pub use address_transform::AddressTransform;
18 
19 mod address_transform;
20 mod attr;
21 mod debug_transform_logging;
22 mod expression;
23 mod line_program;
24 mod range_info_builder;
25 mod refs;
26 mod simulate;
27 mod synthetic;
28 mod unit;
29 mod utils;
30 
31 impl<'a> Compilation<'a> {
32     fn function_frame_info(
33         &mut self,
34         module: StaticModuleIndex,
35         func: DefinedFuncIndex,
36     ) -> expression::FunctionFrameInfo<'a> {
37         let (_, func) = self.function(module, func);
38 
39         expression::FunctionFrameInfo {
40             value_ranges: &func.value_labels_ranges,
41             memory_offset: self.module_memory_offsets[module].clone(),
42         }
43     }
44 }
45 
46 #[derive(Error, Debug)]
47 #[error("Debug info transform error: {0}")]
48 pub struct TransformError(&'static str);
49 
50 pub(crate) struct DebugInputContext<'a> {
51     reachable: &'a HashSet<UnitSectionOffset>,
52 }
53 
54 fn load_dwp<'data>(
55     translation: ModuleTranslation<'data>,
56     buffer: &'data [u8],
57 ) -> Result<DwarfPackage<gimli::EndianSlice<'data, gimli::LittleEndian>>> {
58     let endian_slice = gimli::EndianSlice::new(buffer, LittleEndian);
59 
60     let dwarf_package = DwarfPackage::load(
61         |id| -> Result<_> {
62             let slice = match id {
63                 gimli::SectionId::DebugAbbrev => {
64                     translation.debuginfo.dwarf.debug_abbrev.reader().slice()
65                 }
66                 gimli::SectionId::DebugInfo => {
67                     translation.debuginfo.dwarf.debug_info.reader().slice()
68                 }
69                 gimli::SectionId::DebugLine => {
70                     translation.debuginfo.dwarf.debug_line.reader().slice()
71                 }
72                 gimli::SectionId::DebugStr => {
73                     translation.debuginfo.dwarf.debug_str.reader().slice()
74                 }
75                 gimli::SectionId::DebugStrOffsets => translation
76                     .debuginfo
77                     .dwarf
78                     .debug_str_offsets
79                     .reader()
80                     .slice(),
81                 gimli::SectionId::DebugLoc => translation.debuginfo.debug_loc.reader().slice(),
82                 gimli::SectionId::DebugLocLists => {
83                     translation.debuginfo.debug_loclists.reader().slice()
84                 }
85                 gimli::SectionId::DebugRngLists => {
86                     translation.debuginfo.debug_rnglists.reader().slice()
87                 }
88                 gimli::SectionId::DebugTypes => {
89                     translation.debuginfo.dwarf.debug_types.reader().slice()
90                 }
91                 gimli::SectionId::DebugCuIndex => {
92                     translation.debuginfo.debug_cu_index.reader().slice()
93                 }
94                 gimli::SectionId::DebugTuIndex => {
95                     translation.debuginfo.debug_tu_index.reader().slice()
96                 }
97                 _ => &buffer,
98             };
99 
100             Ok(gimli::EndianSlice::new(slice, gimli::LittleEndian))
101         },
102         endian_slice,
103     )?;
104 
105     Ok(dwarf_package)
106 }
107 
108 /// Attempts to load a DWARF package using the passed bytes.
109 fn read_dwarf_package_from_bytes<'data>(
110     dwp_bytes: &'data [u8],
111     buffer: &'data [u8],
112     tunables: &Tunables,
113 ) -> Option<DwarfPackage<gimli::EndianSlice<'data, gimli::LittleEndian>>> {
114     let mut validator = wasmparser::Validator::new();
115     let parser = wasmparser::Parser::new(0);
116     let mut types = wasmtime_environ::ModuleTypesBuilder::new(&validator);
117     let translation = match wasmtime_environ::ModuleEnvironment::new(
118         tunables,
119         &mut validator,
120         &mut types,
121         StaticModuleIndex::from_u32(0),
122     )
123     .translate(parser, dwp_bytes)
124     {
125         Ok(translation) => translation,
126         Err(e) => {
127             log::warn!("failed to parse wasm dwarf package: {e:?}");
128             return None;
129         }
130     };
131 
132     match load_dwp(translation, buffer) {
133         Ok(package) => Some(package),
134         Err(err) => {
135             log::warn!("Failed to load Dwarf package {err}");
136             None
137         }
138     }
139 }
140 
141 pub fn transform_dwarf(
142     isa: &dyn TargetIsa,
143     compilation: &mut Compilation<'_>,
144 ) -> Result<write::Dwarf, Error> {
145     dbi_log!("Commencing DWARF transform for {:?}", compilation);
146 
147     let mut transforms = PrimaryMap::new();
148     for (i, _) in compilation.translations.iter() {
149         transforms.push(AddressTransform::new(compilation, i));
150     }
151 
152     let buffer = Vec::new();
153 
154     let dwarf_package = compilation
155         .dwarf_package_bytes
156         .map(
157             |bytes| -> Option<DwarfPackage<gimli::EndianSlice<'_, gimli::LittleEndian>>> {
158                 read_dwarf_package_from_bytes(bytes, &buffer, compilation.tunables)
159             },
160         )
161         .flatten();
162 
163     let out_encoding = gimli::Encoding {
164         format: gimli::Format::Dwarf32,
165         version: 4, // TODO: this should be configurable
166         address_size: isa.pointer_bytes(),
167     };
168     let mut out_strings = write::StringTable::default();
169     let mut out_units = write::UnitTable::default();
170 
171     let out_line_strings = write::LineStringTable::default();
172     let mut pending_di_refs = Vec::new();
173     let mut di_ref_map = DebugInfoRefsMap::new();
174     let mut vmctx_ptr_die_refs = PrimaryMap::new();
175 
176     let mut translated = HashSet::new();
177 
178     for (module, translation) in compilation.translations.iter() {
179         dbi_log!("[== Transforming CUs for module #{} ==]", module.as_u32());
180 
181         let addr_tr = &transforms[module];
182         let di = &translation.debuginfo;
183         let reachable = build_dependencies(&di.dwarf, addr_tr)?.get_reachable();
184 
185         let out_module_synthetic_unit = ModuleSyntheticUnit::new(
186             module,
187             compilation,
188             out_encoding,
189             &mut out_units,
190             &mut out_strings,
191         );
192         // TODO-DebugInfo-Cleanup: move the simulation code to be per-module and delete this map.
193         vmctx_ptr_die_refs.push(out_module_synthetic_unit.vmctx_ptr_die_ref());
194 
195         let mut iter = di.dwarf.debug_info.units();
196         while let Some(header) = iter.next().unwrap_or(None) {
197             let unit = di.dwarf.unit(header)?;
198             let unit = unit.unit_ref(&di.dwarf);
199 
200             let mut split_unit = None;
201             let mut split_reachable = None;
202 
203             if unit.dwo_id.is_some() {
204                 if let Some(dwarf_package) = &dwarf_package {
205                     if let Some((fused, fused_dwarf)) =
206                         replace_unit_from_split_dwarf(unit, dwarf_package)
207                     {
208                         split_reachable =
209                             Some(build_dependencies(&fused_dwarf, addr_tr)?.get_reachable());
210                         split_unit = Some((fused, fused_dwarf));
211                     }
212                 }
213             }
214             let split_unit = split_unit
215                 .as_ref()
216                 .map(|(split_unit, split_dwarf)| split_unit.unit_ref(split_dwarf));
217             let context = DebugInputContext {
218                 reachable: split_reachable.as_ref().unwrap_or(&reachable),
219             };
220 
221             if let Some((id, ref_map, pending_refs)) = clone_unit(
222                 compilation,
223                 module,
224                 unit,
225                 split_unit,
226                 &context,
227                 &addr_tr,
228                 out_encoding,
229                 &out_module_synthetic_unit,
230                 &mut out_units,
231                 &mut out_strings,
232                 &mut translated,
233                 isa,
234             )? {
235                 di_ref_map.insert(&header, id, ref_map);
236                 pending_di_refs.push((id, pending_refs));
237             }
238         }
239     }
240     di_ref_map.patch(pending_di_refs.into_iter(), &mut out_units);
241 
242     generate_simulated_dwarf(
243         compilation,
244         &transforms,
245         &translated,
246         out_encoding,
247         &vmctx_ptr_die_refs,
248         &mut out_units,
249         &mut out_strings,
250         isa,
251     )?;
252 
253     Ok(write::Dwarf {
254         units: out_units,
255         line_programs: vec![],
256         line_strings: out_line_strings,
257         strings: out_strings,
258     })
259 }
260 
261 fn replace_unit_from_split_dwarf<'a>(
262     unit: UnitRef<Reader<'a>>,
263     dwp: &DwarfPackage<Reader<'a>>,
264 ) -> Option<(Unit<Reader<'a>>, Dwarf<Reader<'a>>)> {
265     let dwo_id = unit.dwo_id?;
266     let split_unit_dwarf = dwp.find_cu(dwo_id, unit.dwarf).ok()??;
267     let unit_header = split_unit_dwarf.debug_info.units().next().ok()??;
268     let mut split_unit = split_unit_dwarf.unit(unit_header).ok()?;
269     split_unit.copy_relocated_attributes(&unit);
270     Some((split_unit, split_unit_dwarf))
271 }
272