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::Compilation; 6 use crate::debug::gc::build_dependencies; 7 use anyhow::Error; 8 use cranelift_codegen::isa::TargetIsa; 9 use gimli::{Dwarf, DwarfPackage, LittleEndian, Section, Unit, UnitSectionOffset, write}; 10 use std::{collections::HashSet, fmt::Debug}; 11 use synthetic::ModuleSyntheticUnit; 12 use thiserror::Error; 13 use wasmtime_environ::{ 14 DefinedFuncIndex, ModuleTranslation, PrimaryMap, StaticModuleIndex, Tunables, 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 pub(crate) trait Reader: gimli::Reader<Offset = usize> + Send + Sync {} 47 48 impl<'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}")] 55 pub struct TransformError(&'static str); 56 57 pub(crate) struct DebugInputContext<'a> { 58 reachable: &'a HashSet<UnitSectionOffset>, 59 } 60 61 fn 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. 116 fn 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 148 pub 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 267 fn 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 } 282