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