1 use crate::debug::transform::utils::resolve_die_ref; 2 use crate::debug::Reader; 3 4 use super::address_transform::AddressTransform; 5 use super::expression::{compile_expression, CompiledExpression, FunctionFrameInfo}; 6 use super::range_info_builder::RangeInfoBuilder; 7 use super::refs::{PendingDebugInfoRefs, PendingUnitRefs}; 8 use super::unit::InheritedAttr; 9 use super::{dbi_log, TransformError}; 10 use anyhow::{bail, Error}; 11 use cranelift_codegen::isa::TargetIsa; 12 use gimli::{ 13 write, AttributeValue, DebugLineOffset, DebuggingInformationEntry, Dwarf, Unit, UnitOffset, 14 }; 15 16 #[derive(Debug)] 17 pub(crate) enum EntryAttributesContext<'a> { 18 Root(Option<DebugLineOffset>), 19 Children { 20 depth: usize, 21 subprograms: &'a mut InheritedAttr<SubprogramContext>, 22 file_map: &'a [write::FileId], 23 file_index_base: u64, 24 frame_base: Option<&'a CompiledExpression>, 25 }, 26 } 27 28 #[derive(Debug)] 29 pub struct SubprogramContext { 30 pub obj_ptr: UnitOffset, 31 pub param_num: isize, 32 } 33 34 fn is_exprloc_to_loclist_allowed(attr_name: gimli::constants::DwAt) -> bool { 35 match attr_name { 36 gimli::DW_AT_location 37 | gimli::DW_AT_string_length 38 | gimli::DW_AT_return_addr 39 | gimli::DW_AT_data_member_location 40 | gimli::DW_AT_frame_base 41 | gimli::DW_AT_segment 42 | gimli::DW_AT_static_link 43 | gimli::DW_AT_use_location 44 | gimli::DW_AT_vtable_elem_location => true, 45 _ => false, 46 } 47 } 48 49 pub(crate) fn clone_die_attributes<'a>( 50 dwarf: &gimli::Dwarf<Reader<'a>>, 51 unit: &Unit<Reader<'a>>, 52 entry: &DebuggingInformationEntry<Reader<'a>>, 53 addr_tr: &'a AddressTransform, 54 frame_info: Option<&FunctionFrameInfo>, 55 out_unit: &mut write::Unit, 56 out_entry_id: write::UnitEntryId, 57 subprogram_range_builder: Option<RangeInfoBuilder>, 58 scope_ranges: Option<&Vec<(u64, u64)>>, 59 out_strings: &mut write::StringTable, 60 pending_die_refs: &mut PendingUnitRefs, 61 pending_di_refs: &mut PendingDebugInfoRefs, 62 mut attr_context: EntryAttributesContext<'a>, 63 isa: &dyn TargetIsa, 64 ) -> Result<(), Error> { 65 let unit_encoding = unit.encoding(); 66 67 let range_info = if let Some(subprogram_range_builder) = subprogram_range_builder { 68 subprogram_range_builder 69 } else { 70 // FIXME for CU: currently address_transform operate on a single 71 // function range, and when CU spans multiple ranges the 72 // transformation may be incomplete. 73 RangeInfoBuilder::from(dwarf, unit, entry)? 74 }; 75 range_info.build(addr_tr, out_unit, out_entry_id); 76 77 let mut is_obj_ptr = false; 78 prepare_die_context(dwarf, unit, entry, &mut attr_context, &mut is_obj_ptr)?; 79 80 let mut attrs = entry.attrs(); 81 while let Some(attr) = attrs.next()? { 82 match attr.name() { 83 gimli::DW_AT_low_pc | gimli::DW_AT_high_pc | gimli::DW_AT_ranges => { 84 // Handled by RangeInfoBuilder. 85 continue; 86 } 87 gimli::DW_AT_object_pointer => { 88 // Our consumers cannot handle 'this' typed as a non-pointer (recall 89 // we translate all pointers to wrapper types), making it unusable. 90 // To remedy this, we 'strip' instance-ness off of methods by removing 91 // DW_AT_object_pointer and renaming 'this' to '__this'. 92 if let EntryAttributesContext::Children { 93 depth, 94 ref mut subprograms, 95 .. 96 } = attr_context 97 { 98 if let Some(ref mut subprogram) = subprograms.top_with_depth_mut(depth) { 99 // We expect this to reference a child entry in the same unit. 100 if let Some(unit_offs) = match attr.value() { 101 AttributeValue::DebugInfoRef(di_ref) => { 102 di_ref.to_unit_offset(&unit.header) 103 } 104 AttributeValue::UnitRef(unit_ref) => Some(unit_ref), 105 _ => None, 106 } { 107 subprogram.obj_ptr = unit_offs; 108 dbi_log!("Stripped DW_AT_object_pointer"); 109 continue; 110 } 111 } 112 } 113 } 114 gimli::DW_AT_str_offsets_base 115 | gimli::DW_AT_addr_base 116 | gimli::DW_AT_rnglists_base 117 | gimli::DW_AT_loclists_base 118 | gimli::DW_AT_dwo_name 119 | gimli::DW_AT_GNU_addr_base 120 | gimli::DW_AT_GNU_ranges_base 121 | gimli::DW_AT_GNU_dwo_name 122 | gimli::DW_AT_GNU_dwo_id => { 123 // DWARF encoding details that we don't need to copy. 124 continue; 125 } 126 _ => {} 127 } 128 129 if is_obj_ptr { 130 match attr.name() { 131 gimli::DW_AT_artificial => { 132 dbi_log!("Object pointer: stripped DW_AT_artificial"); 133 continue; 134 } 135 gimli::DW_AT_name => { 136 let old_name: &str = &dwarf.attr_string(unit, attr.value())?.to_string_lossy(); 137 let new_name = format!("__{old_name}"); 138 dbi_log!( 139 "Object pointer: renamed '{}' -> '{}'", 140 old_name, 141 new_name.as_str() 142 ); 143 144 let attr_value = write::AttributeValue::StringRef(out_strings.add(new_name)); 145 out_unit 146 .get_mut(out_entry_id) 147 .set(gimli::DW_AT_name, attr_value); 148 continue; 149 } 150 _ => {} 151 } 152 } 153 154 let attr_value = attr.value(); 155 let out_attr_value = match attr_value { 156 AttributeValue::Addr(u) => { 157 let addr = addr_tr.translate(u).unwrap_or(write::Address::Constant(0)); 158 write::AttributeValue::Address(addr) 159 } 160 AttributeValue::DebugAddrIndex(i) => { 161 let u = dwarf.address(unit, i)?; 162 let addr = addr_tr.translate(u).unwrap_or(write::Address::Constant(0)); 163 write::AttributeValue::Address(addr) 164 } 165 AttributeValue::Block(d) => write::AttributeValue::Block(d.to_vec()), 166 AttributeValue::Udata(u) => write::AttributeValue::Udata(u), 167 AttributeValue::Data1(d) => write::AttributeValue::Data1(d), 168 AttributeValue::Data2(d) => write::AttributeValue::Data2(d), 169 AttributeValue::Data4(d) => write::AttributeValue::Data4(d), 170 AttributeValue::Data8(d) => write::AttributeValue::Data8(d), 171 AttributeValue::Sdata(d) => write::AttributeValue::Sdata(d), 172 AttributeValue::Flag(f) => write::AttributeValue::Flag(f), 173 AttributeValue::DebugLineRef(line_program_offset) => { 174 if let EntryAttributesContext::Root(o) = attr_context { 175 if o != Some(line_program_offset) { 176 return Err(TransformError("invalid debug_line offset").into()); 177 } 178 write::AttributeValue::LineProgramRef 179 } else { 180 return Err(TransformError("unexpected debug_line index attribute").into()); 181 } 182 } 183 AttributeValue::FileIndex(i) => { 184 if let EntryAttributesContext::Children { 185 file_map, 186 file_index_base, 187 .. 188 } = attr_context 189 { 190 let index = usize::try_from(i - file_index_base) 191 .ok() 192 .and_then(|i| file_map.get(i).copied()); 193 match index { 194 Some(index) => write::AttributeValue::FileIndex(Some(index)), 195 // This was seen to be invalid in #8884 and #8904 so 196 // ignore this seemingly invalid DWARF from LLVM 197 None => continue, 198 } 199 } else { 200 return Err(TransformError("unexpected file index attribute").into()); 201 } 202 } 203 AttributeValue::String(d) => write::AttributeValue::String(d.to_vec()), 204 AttributeValue::DebugStrRef(_) | AttributeValue::DebugStrOffsetsIndex(_) => { 205 let s = dwarf 206 .attr_string(unit, attr_value)? 207 .to_string_lossy() 208 .into_owned(); 209 write::AttributeValue::StringRef(out_strings.add(s)) 210 } 211 AttributeValue::RangeListsRef(_) | AttributeValue::DebugRngListsIndex(_) => { 212 let r = dwarf.attr_ranges_offset(unit, attr_value)?.unwrap(); 213 let range_info = RangeInfoBuilder::from_ranges_ref(dwarf, unit, r)?; 214 let range_list_id = range_info.build_ranges(addr_tr, &mut out_unit.ranges); 215 write::AttributeValue::RangeListRef(range_list_id) 216 } 217 AttributeValue::LocationListsRef(_) | AttributeValue::DebugLocListsIndex(_) => { 218 let r = dwarf.attr_locations_offset(unit, attr_value)?.unwrap(); 219 let low_pc = 0; 220 let mut locs = dwarf.locations.locations( 221 r, 222 unit_encoding, 223 low_pc, 224 &dwarf.debug_addr, 225 unit.addr_base, 226 )?; 227 let frame_base = 228 if let EntryAttributesContext::Children { frame_base, .. } = attr_context { 229 frame_base 230 } else { 231 None 232 }; 233 234 let mut result: Option<Vec<_>> = None; 235 while let Some(loc) = locs.next()? { 236 if let Some(expr) = compile_expression(&loc.data, unit_encoding, frame_base)? { 237 let chunk = expr 238 .build_with_locals( 239 &[(loc.range.begin, loc.range.end)], 240 addr_tr, 241 frame_info, 242 isa, 243 ) 244 .expressions 245 .filter(|i| { 246 // Ignore empty range 247 if let Ok((_, 0, _)) = i { 248 false 249 } else { 250 true 251 } 252 }) 253 .map(|i| { 254 i.map(|(start, len, expr)| write::Location::StartLength { 255 begin: start, 256 length: len, 257 data: expr, 258 }) 259 }) 260 .collect::<Result<Vec<_>, _>>()?; 261 match &mut result { 262 Some(r) => r.extend(chunk), 263 x @ None => *x = Some(chunk), 264 } 265 } else { 266 // FIXME _expr contains invalid expression 267 continue; // ignore entry 268 } 269 } 270 if result.is_none() { 271 continue; // no valid locations 272 } 273 let list_id = out_unit.locations.add(write::LocationList(result.unwrap())); 274 write::AttributeValue::LocationListRef(list_id) 275 } 276 AttributeValue::Exprloc(_) if attr.name() == gimli::DW_AT_frame_base => { 277 // We do not really "rewrite" the frame base so much as replace it outright. 278 // References to it through the DW_OP_fbreg opcode will be expanded below. 279 let mut cfa = write::Expression::new(); 280 cfa.op(gimli::DW_OP_call_frame_cfa); 281 write::AttributeValue::Exprloc(cfa) 282 } 283 AttributeValue::Exprloc(ref expr) => { 284 let frame_base = 285 if let EntryAttributesContext::Children { frame_base, .. } = attr_context { 286 frame_base 287 } else { 288 None 289 }; 290 if let Some(expr) = compile_expression(expr, unit_encoding, frame_base)? { 291 if expr.is_simple() { 292 if let Some(expr) = expr.build() { 293 write::AttributeValue::Exprloc(expr) 294 } else { 295 continue; 296 } 297 } else { 298 // Conversion to loclist is required. 299 if let Some(scope_ranges) = scope_ranges { 300 let built_expression = 301 expr.build_with_locals(scope_ranges, addr_tr, frame_info, isa); 302 let exprs = built_expression 303 .expressions 304 .collect::<Result<Vec<_>, _>>()?; 305 if exprs.is_empty() { 306 continue; 307 } 308 // Micro-optimization all expressions alike, use one exprloc. 309 let mut single_expr: Option<write::Expression> = None; 310 if built_expression.covers_entire_scope { 311 for (_, _, expr) in &exprs { 312 if let Some(ref prev_expr) = single_expr { 313 if expr == prev_expr { 314 continue; // the same expression 315 } 316 single_expr = None; 317 break; 318 } 319 single_expr = Some(expr.clone()) 320 } 321 } 322 if let Some(expr) = single_expr { 323 write::AttributeValue::Exprloc(expr) 324 } else if is_exprloc_to_loclist_allowed(attr.name()) { 325 // Converting exprloc to loclist. 326 let mut locs = Vec::new(); 327 for (begin, length, data) in exprs { 328 if length == 0 { 329 // Ignore empty range 330 continue; 331 } 332 locs.push(write::Location::StartLength { 333 begin, 334 length, 335 data, 336 }); 337 } 338 let list_id = out_unit.locations.add(write::LocationList(locs)); 339 write::AttributeValue::LocationListRef(list_id) 340 } else { 341 continue; 342 } 343 } else { 344 continue; 345 } 346 } 347 } else { 348 // FIXME _expr contains invalid expression 349 continue; // ignore attribute 350 } 351 } 352 AttributeValue::Encoding(e) => write::AttributeValue::Encoding(e), 353 AttributeValue::DecimalSign(e) => write::AttributeValue::DecimalSign(e), 354 AttributeValue::Endianity(e) => write::AttributeValue::Endianity(e), 355 AttributeValue::Accessibility(e) => write::AttributeValue::Accessibility(e), 356 AttributeValue::Visibility(e) => write::AttributeValue::Visibility(e), 357 AttributeValue::Virtuality(e) => write::AttributeValue::Virtuality(e), 358 AttributeValue::Language(e) => write::AttributeValue::Language(e), 359 AttributeValue::AddressClass(e) => write::AttributeValue::AddressClass(e), 360 AttributeValue::IdentifierCase(e) => write::AttributeValue::IdentifierCase(e), 361 AttributeValue::CallingConvention(e) => write::AttributeValue::CallingConvention(e), 362 AttributeValue::Inline(e) => write::AttributeValue::Inline(e), 363 AttributeValue::Ordering(e) => write::AttributeValue::Ordering(e), 364 AttributeValue::UnitRef(offset) => { 365 pending_die_refs.insert(out_entry_id, attr.name(), offset); 366 continue; 367 } 368 AttributeValue::DebugInfoRef(offset) => { 369 pending_di_refs.insert(out_entry_id, attr.name(), offset); 370 continue; 371 } 372 a => bail!("Unexpected attribute: {:?}", a), 373 }; 374 let out_entry: &mut write::DebuggingInformationEntry = out_unit.get_mut(out_entry_id); 375 out_entry.set(attr.name(), out_attr_value); 376 } 377 Ok(()) 378 } 379 380 fn prepare_die_context( 381 dwarf: &Dwarf<Reader<'_>>, 382 unit: &Unit<Reader<'_>>, 383 entry: &DebuggingInformationEntry<Reader<'_>>, 384 attr_context: &mut EntryAttributesContext<'_>, 385 is_obj_ptr: &mut bool, 386 ) -> Result<(), Error> { 387 let EntryAttributesContext::Children { 388 depth, subprograms, .. 389 } = attr_context 390 else { 391 return Ok(()); 392 }; 393 394 // Update the current context based on what kind of entry this is. 395 match entry.tag() { 396 gimli::DW_TAG_subprogram | gimli::DW_TAG_inlined_subroutine | gimli::DW_TAG_entry_point => { 397 // Push the 'context' of there being no parameters (yet). 398 subprograms.push( 399 *depth, 400 SubprogramContext { 401 obj_ptr: UnitOffset { 0: 0 }, 402 param_num: -1, 403 }, 404 ); 405 } 406 gimli::DW_TAG_formal_parameter => { 407 // Formal parameter tags can be parented by catch blocks 408 // and such - not just subprogram DIEs. So we need to check 409 // that this DIE is indeed a direct child of a subprogram. 410 if let Some(subprogram) = subprograms.top_with_depth_mut(*depth - 1) { 411 subprogram.param_num += 1; 412 413 if subprogram.obj_ptr == entry.offset() 414 || is_obj_ptr_param(dwarf, unit, entry, subprogram.param_num)? 415 { 416 *is_obj_ptr = true; 417 } 418 } 419 } 420 _ => {} 421 } 422 Ok(()) 423 } 424 425 fn is_obj_ptr_param( 426 dwarf: &Dwarf<Reader<'_>>, 427 unit: &Unit<Reader<'_>>, 428 entry: &DebuggingInformationEntry<Reader<'_>>, 429 param_num: isize, 430 ) -> Result<bool, Error> { 431 debug_assert!(entry.tag() == gimli::DW_TAG_formal_parameter); 432 433 // This logic was taken loosely from LLDB. It is known 434 // that it is not fully correct (doesn't handle 'deduced 435 // this', for example). 436 // Q: DWARF includes DW_AT_object_pointer as we use it, 437 // why do we need this heuristic as well? 438 // A: Declarations do not include DW_AT_object_pointer. 439 if param_num == 0 440 && entry.attr_value(gimli::DW_AT_artificial)? == Some(AttributeValue::Flag(true)) 441 { 442 // Either this has no name (declarations omit them), or its explicitly "this". 443 let name = entry.attr_value(gimli::DW_AT_name)?; 444 if name.is_none() || dwarf.attr_string(unit, name.unwrap())?.slice().eq(b"this") { 445 // Finally, a type check. We expect a pointer. 446 if let Some(type_attr) = entry.attr_value(gimli::DW_AT_type)? { 447 if let Some(type_die) = resolve_die_ref(unit, &type_attr)? { 448 return Ok(type_die.tag() == gimli::DW_TAG_pointer_type); 449 } 450 } 451 } 452 }; 453 454 return Ok(false); 455 } 456