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