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