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