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