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