wasmtime_internal_cranelift/debug/transform/
mod.rs

1use self::debug_transform_logging::dbi_log;
2use self::refs::DebugInfoRefsMap;
3use self::simulate::generate_simulated_dwarf;
4use self::unit::clone_unit;
5use crate::debug::Compilation;
6use crate::debug::gc::build_dependencies;
7use anyhow::Error;
8use cranelift_codegen::isa::TargetIsa;
9use gimli::{Dwarf, DwarfPackage, LittleEndian, Section, Unit, UnitSectionOffset, write};
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
115/// Attempts to load a DWARF package using the passed bytes.
116fn 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
148pub 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
267fn 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}