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