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