1 use crate::debug::Reader; 2 use crate::debug::transform::utils::resolve_die_ref; 3 4 use super::address_transform::AddressTransform; 5 use super::dbi_log; 6 use super::expression::{CompiledExpression, FunctionFrameInfo, compile_expression}; 7 use super::range_info_builder::RangeInfoBuilder; 8 use super::unit::InheritedAttr; 9 use cranelift_codegen::isa::TargetIsa; 10 use gimli::{AttributeValue, UnitOffset, write}; 11 use wasmtime_environ::error::Error; 12 13 #[derive(Debug)] 14 pub(crate) struct EntryAttributesContext<'a> { 15 pub subprograms: &'a mut InheritedAttr<SubprogramContext>, 16 pub frame_base: Option<&'a CompiledExpression>, 17 } 18 19 #[derive(Debug)] 20 pub struct SubprogramContext { 21 pub obj_ptr: UnitOffset, 22 pub param_num: isize, 23 } 24 25 fn is_exprloc_to_loclist_allowed(attr_name: gimli::constants::DwAt) -> bool { 26 match attr_name { 27 gimli::DW_AT_location 28 | gimli::DW_AT_string_length 29 | gimli::DW_AT_return_addr 30 | gimli::DW_AT_data_member_location 31 | gimli::DW_AT_frame_base 32 | gimli::DW_AT_segment 33 | gimli::DW_AT_static_link 34 | gimli::DW_AT_use_location 35 | gimli::DW_AT_vtable_elem_location => true, 36 _ => false, 37 } 38 } 39 40 pub(crate) fn clone_die_attributes<'a>( 41 convert_unit: &mut gimli::write::ConvertUnit<Reader<'a>>, 42 entry: &gimli::write::ConvertUnitEntry<Reader<'a>>, 43 addr_tr: &AddressTransform, 44 frame_info: Option<&FunctionFrameInfo>, 45 out_entry_id: write::UnitEntryId, 46 subprogram_range_builder: Option<RangeInfoBuilder>, 47 scope_ranges: Option<&Vec<(u64, u64)>>, 48 mut attr_context: EntryAttributesContext, 49 isa: &dyn TargetIsa, 50 ) -> Result<(), Error> { 51 let unit = entry.read_unit; 52 let unit_encoding = unit.encoding(); 53 54 let range_info = if let Some(subprogram_range_builder) = subprogram_range_builder { 55 subprogram_range_builder 56 } else { 57 // FIXME for CU: currently address_transform operate on a single 58 // function range, and when CU spans multiple ranges the 59 // transformation may be incomplete. 60 RangeInfoBuilder::from(entry)? 61 }; 62 range_info.build(addr_tr, convert_unit.unit, out_entry_id); 63 64 let mut is_obj_ptr = false; 65 prepare_die_context(entry, &mut attr_context, &mut is_obj_ptr)?; 66 67 for attr in &entry.attrs { 68 match attr.name() { 69 gimli::DW_AT_low_pc | gimli::DW_AT_high_pc | gimli::DW_AT_ranges => { 70 // Handled by RangeInfoBuilder. 71 continue; 72 } 73 gimli::DW_AT_object_pointer => { 74 // Our consumers cannot handle 'this' typed as a non-pointer (recall 75 // we translate all pointers to wrapper types), making it unusable. 76 // To remedy this, we 'strip' instance-ness off of methods by removing 77 // DW_AT_object_pointer and renaming 'this' to '__this'. 78 if let Some(ref mut subprogram) = 79 attr_context.subprograms.top_with_depth_mut(entry.depth) 80 { 81 // We expect this to reference a child entry in the same unit. 82 if let Some(unit_offs) = match attr.value() { 83 AttributeValue::DebugInfoRef(di_ref) => di_ref.to_unit_offset(&unit.header), 84 AttributeValue::UnitRef(unit_ref) => Some(unit_ref), 85 _ => None, 86 } { 87 subprogram.obj_ptr = unit_offs; 88 dbi_log!("Stripped DW_AT_object_pointer"); 89 continue; 90 } 91 } 92 } 93 _ => {} 94 } 95 96 if is_obj_ptr { 97 match attr.name() { 98 gimli::DW_AT_artificial => { 99 dbi_log!("Object pointer: stripped DW_AT_artificial"); 100 continue; 101 } 102 gimli::DW_AT_name => { 103 let old_name: &str = &unit.attr_string(attr.value())?.to_string_lossy(); 104 let new_name = format!("__{old_name}"); 105 dbi_log!( 106 "Object pointer: renamed '{}' -> '{}'", 107 old_name, 108 new_name.as_str() 109 ); 110 111 let attr_value = 112 write::AttributeValue::StringRef(convert_unit.strings.add(new_name)); 113 convert_unit 114 .unit 115 .get_mut(out_entry_id) 116 .set(gimli::DW_AT_name, attr_value); 117 continue; 118 } 119 _ => {} 120 } 121 } 122 123 let attr_value = attr.value(); 124 let out_attr_value = match attr_value { 125 AttributeValue::Addr(u) => { 126 let addr = addr_tr.translate(u).unwrap_or(write::Address::Constant(0)); 127 write::AttributeValue::Address(addr) 128 } 129 AttributeValue::DebugAddrIndex(i) => { 130 let u = unit.address(i)?; 131 let addr = addr_tr.translate(u).unwrap_or(write::Address::Constant(0)); 132 write::AttributeValue::Address(addr) 133 } 134 AttributeValue::RangeListsRef(_) | AttributeValue::DebugRngListsIndex(_) => { 135 let r = unit.attr_ranges_offset(attr_value)?.unwrap(); 136 let range_info = RangeInfoBuilder::from_ranges_ref(unit, r)?; 137 let range_list_id = range_info.build_ranges(addr_tr, &mut convert_unit.unit.ranges); 138 write::AttributeValue::RangeListRef(range_list_id) 139 } 140 AttributeValue::LocationListsRef(_) | AttributeValue::DebugLocListsIndex(_) => { 141 let mut locs = unit.attr_locations(attr_value)?.unwrap(); 142 let frame_base = attr_context.frame_base; 143 144 let mut result: Option<Vec<_>> = None; 145 while let Some(loc) = locs.next()? { 146 if let Some(expr) = compile_expression(&loc.data, unit_encoding, frame_base)? { 147 let chunk = expr 148 .build_with_locals( 149 &[(loc.range.begin, loc.range.end)], 150 addr_tr, 151 frame_info, 152 isa, 153 ) 154 .expressions 155 .filter(|i| { 156 // Ignore empty range 157 if let Ok((_, 0, _)) = i { false } else { true } 158 }) 159 .map(|i| { 160 i.map(|(start, len, expr)| write::Location::StartLength { 161 begin: start, 162 length: len, 163 data: expr, 164 }) 165 }) 166 .collect::<Result<Vec<_>, _>>()?; 167 match &mut result { 168 Some(r) => r.extend(chunk), 169 x @ None => *x = Some(chunk), 170 } 171 } else { 172 // FIXME _expr contains invalid expression 173 continue; // ignore entry 174 } 175 } 176 if result.is_none() { 177 continue; // no valid locations 178 } 179 let list_id = convert_unit 180 .unit 181 .locations 182 .add(write::LocationList(result.unwrap())); 183 write::AttributeValue::LocationListRef(list_id) 184 } 185 AttributeValue::Exprloc(_) if attr.name() == gimli::DW_AT_frame_base => { 186 // We do not really "rewrite" the frame base so much as replace it outright. 187 // References to it through the DW_OP_fbreg opcode will be expanded below. 188 let mut cfa = write::Expression::new(); 189 cfa.op(gimli::DW_OP_call_frame_cfa); 190 write::AttributeValue::Exprloc(cfa) 191 } 192 AttributeValue::Exprloc(ref expr) => { 193 let frame_base = attr_context.frame_base; 194 if let Some(expr) = compile_expression(expr, unit_encoding, frame_base)? { 195 if expr.is_simple() { 196 if let Some(expr) = expr.build() { 197 write::AttributeValue::Exprloc(expr) 198 } else { 199 continue; 200 } 201 } else { 202 // Conversion to loclist is required. 203 if let Some(scope_ranges) = scope_ranges { 204 let built_expression = 205 expr.build_with_locals(scope_ranges, addr_tr, frame_info, isa); 206 let exprs = built_expression 207 .expressions 208 .collect::<Result<Vec<_>, _>>()?; 209 if exprs.is_empty() { 210 continue; 211 } 212 // Micro-optimization all expressions alike, use one exprloc. 213 let mut single_expr: Option<write::Expression> = None; 214 if built_expression.covers_entire_scope { 215 for (_, _, expr) in &exprs { 216 if let Some(ref prev_expr) = single_expr { 217 if expr == prev_expr { 218 continue; // the same expression 219 } 220 single_expr = None; 221 break; 222 } 223 single_expr = Some(expr.clone()) 224 } 225 } 226 if let Some(expr) = single_expr { 227 write::AttributeValue::Exprloc(expr) 228 } else if is_exprloc_to_loclist_allowed(attr.name()) { 229 // Converting exprloc to loclist. 230 let mut locs = Vec::new(); 231 for (begin, length, data) in exprs { 232 if length == 0 { 233 // Ignore empty range 234 continue; 235 } 236 locs.push(write::Location::StartLength { 237 begin, 238 length, 239 data, 240 }); 241 } 242 let list_id = 243 convert_unit.unit.locations.add(write::LocationList(locs)); 244 write::AttributeValue::LocationListRef(list_id) 245 } else { 246 continue; 247 } 248 } else { 249 continue; 250 } 251 } 252 } else { 253 // FIXME _expr contains invalid expression 254 continue; // ignore attribute 255 } 256 } 257 // No other attributes contain addresses or address offsets. 258 _ => match convert_unit.convert_attribute_value(unit, attr, &|_| None) { 259 Ok(value) => value, 260 Err(e) => { 261 // Invalid `FileIndex` was seen in #8884 and #8904. In general it's 262 // better to ignore invalid or unknown DWARF rather then failing outright. 263 dbi_log!( 264 "Ignoring entry {:x?} attribute {} = {:x?}: {e}", 265 entry.offset.to_unit_section_offset(&convert_unit.read_unit), 266 attr.name(), 267 attr_value, 268 ); 269 continue; 270 } 271 }, 272 }; 273 let out_entry = convert_unit.unit.get_mut(out_entry_id); 274 out_entry.set(attr.name(), out_attr_value); 275 } 276 Ok(()) 277 } 278 279 fn prepare_die_context<'a>( 280 entry: &gimli::write::ConvertUnitEntry<Reader<'a>>, 281 attr_context: &mut EntryAttributesContext, 282 is_obj_ptr: &mut bool, 283 ) -> Result<(), Error> { 284 let subprograms = &mut attr_context.subprograms; 285 286 // Update the current context based on what kind of entry this is. 287 match entry.tag { 288 gimli::DW_TAG_subprogram | gimli::DW_TAG_inlined_subroutine | gimli::DW_TAG_entry_point => { 289 // Push the 'context' of there being no parameters (yet). 290 subprograms.push( 291 entry.depth, 292 SubprogramContext { 293 obj_ptr: UnitOffset { 0: 0 }, 294 param_num: -1, 295 }, 296 ); 297 } 298 gimli::DW_TAG_formal_parameter => { 299 // Formal parameter tags can be parented by catch blocks 300 // and such - not just subprogram DIEs. So we need to check 301 // that this DIE is indeed a direct child of a subprogram. 302 if let Some(subprogram) = subprograms.top_with_depth_mut(entry.depth - 1) { 303 subprogram.param_num += 1; 304 305 if subprogram.obj_ptr == entry.offset 306 || is_obj_ptr_param(entry, subprogram.param_num)? 307 { 308 *is_obj_ptr = true; 309 } 310 } 311 } 312 _ => {} 313 } 314 Ok(()) 315 } 316 317 fn is_obj_ptr_param( 318 entry: &gimli::write::ConvertUnitEntry<Reader<'_>>, 319 param_num: isize, 320 ) -> Result<bool, Error> { 321 debug_assert_eq!(entry.tag, gimli::DW_TAG_formal_parameter); 322 let unit = entry.read_unit; 323 324 // This logic was taken loosely from LLDB. It is known 325 // that it is not fully correct (doesn't handle 'deduced 326 // this', for example). 327 // Q: DWARF includes DW_AT_object_pointer as we use it, 328 // why do we need this heuristic as well? 329 // A: Declarations do not include DW_AT_object_pointer. 330 if param_num == 0 331 && entry.attr_value(gimli::DW_AT_artificial) == Some(AttributeValue::Flag(true)) 332 { 333 // Either this has no name (declarations omit them), or its explicitly "this". 334 let name = entry.attr_value(gimli::DW_AT_name); 335 if name.is_none() || unit.attr_string(name.unwrap())?.slice().eq(b"this") { 336 // Finally, a type check. We expect a pointer. 337 if let Some(type_attr) = entry.attr_value(gimli::DW_AT_type) { 338 if let Some(type_die) = resolve_die_ref(unit, &type_attr)? { 339 return Ok(type_die.tag == gimli::DW_TAG_pointer_type); 340 } 341 } 342 } 343 }; 344 345 return Ok(false); 346 } 347