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