wasmtime_cranelift/debug/transform/
mod.rs1use self::debug_transform_logging::dbi_log;
2use self::refs::DebugInfoRefsMap;
3use self::simulate::generate_simulated_dwarf;
4use self::unit::clone_unit;
5use crate::debug::gc::build_dependencies;
6use crate::debug::Compilation;
7use anyhow::Error;
8use cranelift_codegen::isa::TargetIsa;
9use gimli::{write, Dwarf, DwarfPackage, LittleEndian, Section, Unit, UnitSectionOffset};
10use std::{collections::HashSet, fmt::Debug};
11use synthetic::ModuleSyntheticUnit;
12use thiserror::Error;
13use wasmtime_environ::{
14 DefinedFuncIndex, ModuleTranslation, PrimaryMap, StaticModuleIndex, Tunables,
15};
16
17pub use address_transform::AddressTransform;
18
19mod address_transform;
20mod attr;
21mod debug_transform_logging;
22mod expression;
23mod line_program;
24mod range_info_builder;
25mod refs;
26mod simulate;
27mod synthetic;
28mod unit;
29mod utils;
30
31impl<'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
46pub(crate) trait Reader: gimli::Reader<Offset = usize> + Send + Sync {}
47
48impl<'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}")]
55pub struct TransformError(&'static str);
56
57pub(crate) struct DebugInputContext<'a> {
58 reachable: &'a HashSet<UnitSectionOffset>,
59}
60
61fn 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
115fn 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 =
125 match wasmtime_environ::ModuleEnvironment::new(tunables, &mut validator, &mut types)
126 .translate(parser, dwp_bytes)
127 {
128 Ok(translation) => translation,
129 Err(e) => {
130 log::warn!("failed to parse wasm dwarf package: {e:?}");
131 return None;
132 }
133 };
134
135 match load_dwp(translation, buffer) {
136 Ok(package) => Some(package),
137 Err(err) => {
138 log::warn!("Failed to load Dwarf package {}", err);
139 None
140 }
141 }
142}
143
144pub fn transform_dwarf(
145 isa: &dyn TargetIsa,
146 compilation: &mut Compilation<'_>,
147) -> Result<write::Dwarf, Error> {
148 dbi_log!("Commencing DWARF transform for {:?}", compilation);
149
150 let mut transforms = PrimaryMap::new();
151 for (i, _) in compilation.translations.iter() {
152 transforms.push(AddressTransform::new(compilation, i));
153 }
154
155 let buffer = Vec::new();
156
157 let dwarf_package = compilation
158 .dwarf_package_bytes
159 .map(
160 |bytes| -> Option<DwarfPackage<gimli::EndianSlice<'_, gimli::LittleEndian>>> {
161 read_dwarf_package_from_bytes(bytes, &buffer, compilation.tunables)
162 },
163 )
164 .flatten();
165
166 let reachable = build_dependencies(compilation, &dwarf_package, &transforms)?.get_reachable();
167
168 let out_encoding = gimli::Encoding {
169 format: gimli::Format::Dwarf32,
170 version: 4, address_size: isa.pointer_bytes(),
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 let mut vmctx_ptr_die_refs = PrimaryMap::new();
180
181 let mut translated = HashSet::new();
182
183 for (module, translation) in compilation.translations.iter() {
184 dbi_log!("[== Transforming CUs for module #{} ==]", module.as_u32());
185
186 let addr_tr = &transforms[module];
187 let di = &translation.debuginfo;
188 let context = DebugInputContext {
189 reachable: &reachable,
190 };
191 let out_module_synthetic_unit = ModuleSyntheticUnit::new(
192 module,
193 compilation,
194 out_encoding,
195 &mut out_units,
196 &mut out_strings,
197 );
198 vmctx_ptr_die_refs.push(out_module_synthetic_unit.vmctx_ptr_die_ref());
200
201 let mut iter = di.dwarf.debug_info.units();
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 unit.dwo_id.is_some() {
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 &out_module_synthetic_unit,
229 &mut out_units,
230 &mut out_strings,
231 &mut translated,
232 isa,
233 )? {
234 di_ref_map.insert(&header, id, ref_map);
235 pending_di_refs.push((id, pending_refs));
236 }
237 }
238 }
239 di_ref_map.patch(pending_di_refs.into_iter(), &mut out_units);
240
241 generate_simulated_dwarf(
242 compilation,
243 &transforms,
244 &translated,
245 out_encoding,
246 &vmctx_ptr_die_refs,
247 &mut out_units,
248 &mut out_strings,
249 isa,
250 )?;
251
252 Ok(write::Dwarf {
253 units: out_units,
254 line_programs: vec![],
255 line_strings: out_line_strings,
256 strings: out_strings,
257 })
258}
259
260fn replace_unit_from_split_dwarf<'a>(
261 unit: &'a Unit<gimli::EndianSlice<'a, gimli::LittleEndian>, usize>,
262 dwp: &DwarfPackage<gimli::EndianSlice<'a, gimli::LittleEndian>>,
263 parent: &Dwarf<gimli::EndianSlice<'a, gimli::LittleEndian>>,
264) -> Option<(
265 Unit<gimli::EndianSlice<'a, gimli::LittleEndian>, usize>,
266 Dwarf<gimli::EndianSlice<'a, gimli::LittleEndian>>,
267)> {
268 let dwo_id = unit.dwo_id?;
269 let split_unit_dwarf = dwp.find_cu(dwo_id, parent).ok()??;
270 let unit_header = split_unit_dwarf.debug_info.units().next().ok()??;
271 let mut split_unit = split_unit_dwarf.unit(unit_header).ok()?;
272 split_unit.copy_relocated_attributes(unit);
273 Some((split_unit, split_unit_dwarf))
274}