1 use super::address_transform::AddressTransform; 2 use super::expression::{compile_expression, CompiledExpression, FunctionFrameInfo}; 3 use super::range_info_builder::RangeInfoBuilder; 4 use super::refs::{PendingDebugInfoRefs, PendingUnitRefs}; 5 use super::{DebugInputContext, Reader, TransformError}; 6 use anyhow::{bail, Error}; 7 use cranelift_codegen::isa::TargetIsa; 8 use gimli::{ 9 write, AttributeValue, DebugLineOffset, DebugLineStr, DebugStr, DebugStrOffsets, 10 DebuggingInformationEntry, Unit, 11 }; 12 13 #[derive(Debug)] 14 pub(crate) enum FileAttributeContext<'a> { 15 Root(Option<DebugLineOffset>), 16 Children { 17 file_map: &'a [write::FileId], 18 file_index_base: u64, 19 frame_base: Option<&'a CompiledExpression>, 20 }, 21 } 22 23 fn is_exprloc_to_loclist_allowed(attr_name: gimli::constants::DwAt) -> bool { 24 match attr_name { 25 gimli::DW_AT_location 26 | gimli::DW_AT_string_length 27 | gimli::DW_AT_return_addr 28 | gimli::DW_AT_data_member_location 29 | gimli::DW_AT_frame_base 30 | gimli::DW_AT_segment 31 | gimli::DW_AT_static_link 32 | gimli::DW_AT_use_location 33 | gimli::DW_AT_vtable_elem_location => true, 34 _ => false, 35 } 36 } 37 38 pub(crate) fn clone_die_attributes<'a, R>( 39 dwarf: &gimli::Dwarf<R>, 40 unit: &Unit<R, R::Offset>, 41 entry: &DebuggingInformationEntry<R>, 42 context: &DebugInputContext<R>, 43 addr_tr: &'a AddressTransform, 44 frame_info: Option<&FunctionFrameInfo>, 45 out_unit: &mut write::Unit, 46 current_scope_id: write::UnitEntryId, 47 subprogram_range_builder: Option<RangeInfoBuilder>, 48 scope_ranges: Option<&Vec<(u64, u64)>>, 49 cu_low_pc: u64, 50 out_strings: &mut write::StringTable, 51 pending_die_refs: &mut PendingUnitRefs, 52 pending_di_refs: &mut PendingDebugInfoRefs, 53 file_context: FileAttributeContext<'a>, 54 isa: &dyn TargetIsa, 55 ) -> Result<(), Error> 56 where 57 R: Reader, 58 { 59 let unit_encoding = unit.encoding(); 60 61 let range_info = if let Some(subprogram_range_builder) = subprogram_range_builder { 62 subprogram_range_builder 63 } else { 64 // FIXME for CU: currently address_transform operate on a single 65 // function range, and when CU spans multiple ranges the 66 // transformation may be incomplete. 67 RangeInfoBuilder::from(dwarf, unit, entry, context, cu_low_pc)? 68 }; 69 range_info.build(addr_tr, out_unit, current_scope_id); 70 71 let mut attrs = entry.attrs(); 72 while let Some(attr) = attrs.next()? { 73 let attr_value = match attr.value() { 74 AttributeValue::Addr(_) | AttributeValue::DebugAddrIndex(_) 75 if attr.name() == gimli::DW_AT_low_pc => 76 { 77 continue; 78 } 79 AttributeValue::Udata(_) if attr.name() == gimli::DW_AT_high_pc => { 80 continue; 81 } 82 AttributeValue::RangeListsRef(_) if attr.name() == gimli::DW_AT_ranges => { 83 continue; 84 } 85 AttributeValue::DebugAddrBase(_) | AttributeValue::DebugStrOffsetsBase(_) => { 86 continue; 87 } 88 89 AttributeValue::Addr(u) => { 90 let addr = addr_tr.translate(u).unwrap_or(write::Address::Constant(0)); 91 write::AttributeValue::Address(addr) 92 } 93 AttributeValue::DebugAddrIndex(i) => { 94 let u = context.debug_addr.get_address(4, unit.addr_base, i)?; 95 let addr = addr_tr.translate(u).unwrap_or(write::Address::Constant(0)); 96 write::AttributeValue::Address(addr) 97 } 98 AttributeValue::Block(d) => write::AttributeValue::Block(d.to_slice()?.into_owned()), 99 AttributeValue::Udata(u) => write::AttributeValue::Udata(u), 100 AttributeValue::Data1(d) => write::AttributeValue::Data1(d), 101 AttributeValue::Data2(d) => write::AttributeValue::Data2(d), 102 AttributeValue::Data4(d) => write::AttributeValue::Data4(d), 103 AttributeValue::Sdata(d) => write::AttributeValue::Sdata(d), 104 AttributeValue::Flag(f) => write::AttributeValue::Flag(f), 105 AttributeValue::DebugLineRef(line_program_offset) => { 106 if let FileAttributeContext::Root(o) = file_context { 107 if o != Some(line_program_offset) { 108 return Err(TransformError("invalid debug_line offset").into()); 109 } 110 write::AttributeValue::LineProgramRef 111 } else { 112 return Err(TransformError("unexpected debug_line index attribute").into()); 113 } 114 } 115 AttributeValue::FileIndex(i) => { 116 if let FileAttributeContext::Children { 117 file_map, 118 file_index_base, 119 .. 120 } = file_context 121 { 122 write::AttributeValue::FileIndex(Some(file_map[(i - file_index_base) as usize])) 123 } else { 124 return Err(TransformError("unexpected file index attribute").into()); 125 } 126 } 127 AttributeValue::DebugStrRef(str_offset) => { 128 let s = context.debug_str.get_str(str_offset)?.to_slice()?.to_vec(); 129 write::AttributeValue::StringRef(out_strings.add(s)) 130 } 131 AttributeValue::DebugStrOffsetsIndex(i) => { 132 let str_offset = context.debug_str_offsets.get_str_offset( 133 gimli::Format::Dwarf32, 134 unit.str_offsets_base, 135 i, 136 )?; 137 let s = context.debug_str.get_str(str_offset)?.to_slice()?.to_vec(); 138 write::AttributeValue::StringRef(out_strings.add(s)) 139 } 140 AttributeValue::RangeListsRef(r) => { 141 let r = dwarf.ranges_offset_from_raw(unit, r); 142 let range_info = RangeInfoBuilder::from_ranges_ref(unit, r, context, cu_low_pc)?; 143 let range_list_id = range_info.build_ranges(addr_tr, &mut out_unit.ranges); 144 write::AttributeValue::RangeListRef(range_list_id) 145 } 146 AttributeValue::LocationListsRef(r) => { 147 let low_pc = 0; 148 let mut locs = context.loclists.locations( 149 r, 150 unit_encoding, 151 low_pc, 152 &context.debug_addr, 153 unit.addr_base, 154 )?; 155 let frame_base = 156 if let FileAttributeContext::Children { frame_base, .. } = file_context { 157 frame_base 158 } else { 159 None 160 }; 161 162 let mut result: Option<Vec<_>> = None; 163 while let Some(loc) = locs.next()? { 164 if let Some(expr) = compile_expression(&loc.data, unit_encoding, frame_base)? { 165 let chunk = expr 166 .build_with_locals( 167 &[(loc.range.begin, loc.range.end)], 168 addr_tr, 169 frame_info, 170 isa, 171 ) 172 .filter(|i| { 173 // Ignore empty range 174 if let Ok((_, 0, _)) = i { 175 false 176 } else { 177 true 178 } 179 }) 180 .map(|i| { 181 i.map(|(start, len, expr)| write::Location::StartLength { 182 begin: start, 183 length: len, 184 data: expr, 185 }) 186 }) 187 .collect::<Result<Vec<_>, _>>()?; 188 match &mut result { 189 Some(r) => r.extend(chunk), 190 x @ None => *x = Some(chunk), 191 } 192 } else { 193 // FIXME _expr contains invalid expression 194 continue; // ignore entry 195 } 196 } 197 if result.is_none() { 198 continue; // no valid locations 199 } 200 let list_id = out_unit.locations.add(write::LocationList(result.unwrap())); 201 write::AttributeValue::LocationListRef(list_id) 202 } 203 AttributeValue::Exprloc(_) if attr.name() == gimli::DW_AT_frame_base => { 204 // We do not really "rewrite" the frame base so much as replace it outright. 205 // References to it through the DW_OP_fbreg opcode will be expanded below. 206 let mut cfa = write::Expression::new(); 207 cfa.op(gimli::DW_OP_call_frame_cfa); 208 write::AttributeValue::Exprloc(cfa) 209 } 210 AttributeValue::Exprloc(ref expr) => { 211 let frame_base = 212 if let FileAttributeContext::Children { frame_base, .. } = file_context { 213 frame_base 214 } else { 215 None 216 }; 217 if let Some(expr) = compile_expression(expr, unit_encoding, frame_base)? { 218 if expr.is_simple() { 219 if let Some(expr) = expr.build() { 220 write::AttributeValue::Exprloc(expr) 221 } else { 222 continue; 223 } 224 } else { 225 // Conversion to loclist is required. 226 if let Some(scope_ranges) = scope_ranges { 227 let exprs = expr 228 .build_with_locals(scope_ranges, addr_tr, frame_info, isa) 229 .collect::<Result<Vec<_>, _>>()?; 230 if exprs.is_empty() { 231 continue; 232 } 233 let found_single_expr = { 234 // Micro-optimization all expressions alike, use one exprloc. 235 let mut found_expr: Option<write::Expression> = None; 236 for (_, _, expr) in &exprs { 237 if let Some(ref prev_expr) = found_expr { 238 if expr == prev_expr { 239 continue; // the same expression 240 } 241 found_expr = None; 242 break; 243 } 244 found_expr = Some(expr.clone()) 245 } 246 found_expr 247 }; 248 if let Some(expr) = found_single_expr { 249 write::AttributeValue::Exprloc(expr) 250 } else if is_exprloc_to_loclist_allowed(attr.name()) { 251 // Converting exprloc to loclist. 252 let mut locs = Vec::new(); 253 for (begin, length, data) in exprs { 254 if length == 0 { 255 // Ignore empty range 256 continue; 257 } 258 locs.push(write::Location::StartLength { 259 begin, 260 length, 261 data, 262 }); 263 } 264 let list_id = out_unit.locations.add(write::LocationList(locs)); 265 write::AttributeValue::LocationListRef(list_id) 266 } else { 267 continue; 268 } 269 } else { 270 continue; 271 } 272 } 273 } else { 274 // FIXME _expr contains invalid expression 275 continue; // ignore attribute 276 } 277 } 278 AttributeValue::Encoding(e) => write::AttributeValue::Encoding(e), 279 AttributeValue::DecimalSign(e) => write::AttributeValue::DecimalSign(e), 280 AttributeValue::Endianity(e) => write::AttributeValue::Endianity(e), 281 AttributeValue::Accessibility(e) => write::AttributeValue::Accessibility(e), 282 AttributeValue::Visibility(e) => write::AttributeValue::Visibility(e), 283 AttributeValue::Virtuality(e) => write::AttributeValue::Virtuality(e), 284 AttributeValue::Language(e) => write::AttributeValue::Language(e), 285 AttributeValue::AddressClass(e) => write::AttributeValue::AddressClass(e), 286 AttributeValue::IdentifierCase(e) => write::AttributeValue::IdentifierCase(e), 287 AttributeValue::CallingConvention(e) => write::AttributeValue::CallingConvention(e), 288 AttributeValue::Inline(e) => write::AttributeValue::Inline(e), 289 AttributeValue::Ordering(e) => write::AttributeValue::Ordering(e), 290 AttributeValue::UnitRef(offset) => { 291 pending_die_refs.insert(current_scope_id, attr.name(), offset); 292 continue; 293 } 294 AttributeValue::DebugInfoRef(offset) => { 295 pending_di_refs.insert(current_scope_id, attr.name(), offset); 296 continue; 297 } 298 AttributeValue::String(d) => write::AttributeValue::String(d.to_slice()?.into_owned()), 299 a => bail!("Unexpected attribute: {:?}", a), 300 }; 301 let current_scope = out_unit.get_mut(current_scope_id); 302 current_scope.set(attr.name(), attr_value); 303 } 304 Ok(()) 305 } 306 307 pub(crate) fn clone_attr_string<R>( 308 attr_value: &AttributeValue<R>, 309 form: gimli::DwForm, 310 unit: &Unit<R, R::Offset>, 311 debug_str: &DebugStr<R>, 312 debug_str_offsets: &DebugStrOffsets<R>, 313 debug_line_str: &DebugLineStr<R>, 314 out_strings: &mut write::StringTable, 315 ) -> Result<write::LineString, Error> 316 where 317 R: Reader, 318 { 319 let content = match attr_value { 320 AttributeValue::DebugStrRef(str_offset) => { 321 debug_str.get_str(*str_offset)?.to_slice()?.to_vec() 322 } 323 AttributeValue::DebugStrOffsetsIndex(i) => { 324 let str_offset = debug_str_offsets.get_str_offset( 325 gimli::Format::Dwarf32, 326 unit.str_offsets_base, 327 *i, 328 )?; 329 debug_str.get_str(str_offset)?.to_slice()?.to_vec() 330 } 331 AttributeValue::DebugLineStrRef(str_offset) => { 332 debug_line_str.get_str(*str_offset)?.to_slice()?.to_vec() 333 } 334 AttributeValue::String(b) => b.to_slice()?.to_vec(), 335 v => bail!("Unexpected attribute value: {:?}", v), 336 }; 337 Ok(match form { 338 gimli::DW_FORM_strp => { 339 let id = out_strings.add(content); 340 write::LineString::StringRef(id) 341 } 342 gimli::DW_FORM_string => write::LineString::String(content), 343 _ => bail!("DW_FORM_line_strp or other not supported"), 344 }) 345 } 346