1use crate::{
10 error::TraceError,
11 get_entry_type_reference_tree_recursive,
12 gimli_extensions::{AttributeExt, DebuggingInformationEntryExt},
13 type_value_tree::{
14 value::{StringFormat, Value},
15 variable_type::{Archetype, VariableType},
16 TypeValue, TypeValueNode, TypeValueTree, VariableDataError,
17 },
18 DefaultReader, Location, Variable, VariableKind, VariableLocationResult,
19};
20use bitvec::prelude::*;
21use gimli::{
22 Abbreviations, Attribute, AttributeValue, DebugInfoOffset, DebuggingInformationEntry, Dwarf,
23 EntriesTree, Evaluation, EvaluationResult, Piece, Reader, Unit, UnitHeader, UnitOffset,
24};
25use stackdump_core::device_memory::DeviceMemory;
26use std::{collections::HashMap, pin::Pin};
27
28mod type_value_tree_building;
29
30fn div_ceil(lhs: u64, rhs: u64) -> u64 {
31 let d = lhs / rhs;
32 let r = lhs % rhs;
33 if r > 0 && rhs > 0 {
34 d + 1
35 } else {
36 d
37 }
38}
39fn get_entry_name(
41 dwarf: &Dwarf<DefaultReader>,
42 unit: &Unit<DefaultReader, usize>,
43 entry: &DebuggingInformationEntry<DefaultReader, usize>,
44) -> Result<String, TraceError> {
45 if entry.tag() == gimli::constants::DW_TAG_volatile_type
46 || entry.tag() == gimli::constants::DW_TAG_const_type
47 {
48 let abbreviations = dwarf.abbreviations(&unit.header)?;
52
53 get_entry_type_reference_tree_recursive!(
54 variable_type_value_tree = (dwarf, unit, &abbreviations, entry)
55 );
56
57 get_entry_name(dwarf, unit, variable_type_value_tree?.root()?.entry())
58 } else {
59 let name_attr = entry.required_attr(&unit.header, gimli::constants::DW_AT_name);
61
62 match name_attr {
63 Ok(name_attr) => {
64 let attr_string = dwarf.attr_string(unit, name_attr.value())?;
66 Ok(attr_string.to_string()?.into())
68 }
69 Err(_) if entry.tag() == gimli::constants::DW_TAG_array_type => {
70 Ok("array".into())
72 }
73 Err(_) if entry.tag() == gimli::constants::DW_TAG_subroutine_type => {
74 Ok("subroutine".into())
76 }
77 Err(e) => Err(e),
78 }
79 }
80}
81
82fn get_entry_linkage_name(
84 dwarf: &Dwarf<DefaultReader>,
85 unit: &Unit<DefaultReader, usize>,
86 entry: &DebuggingInformationEntry<DefaultReader, usize>,
87) -> Result<Option<String>, TraceError> {
88 let Some(linkage_name_attr) = entry.attr(gimli::constants::DW_AT_linkage_name)? else {
90 return Ok(None);
91 };
92
93 let attr_string = dwarf.attr_string(unit, linkage_name_attr.value())?;
94 Ok(Some(attr_string.to_string()?.into()))
95}
96
97fn get_entry_abstract_origin_reference_tree<'abbrev, 'unit>(
99 dwarf: &Dwarf<DefaultReader>,
100 unit_header: &'unit UnitHeader<DefaultReader>,
101 abbreviations: &'abbrev Abbreviations,
102 entry: &DebuggingInformationEntry<DefaultReader>,
103) -> Result<EntriesTree<'abbrev, 'unit, DefaultReader>, GetEntryTreeError> {
104 let abstract_origin_attr = entry
106 .required_attr(unit_header, gimli::constants::DW_AT_abstract_origin)
107 .map_err(GetEntryTreeError::TraceError)?;
108
109 match abstract_origin_attr.value() {
111 AttributeValue::UnitRef(offset) => {
112 Ok(unit_header
114 .entries_tree(abbreviations, Some(offset))
115 .map_err(|e| GetEntryTreeError::TraceError(e.into()))?)
116 }
117 AttributeValue::DebugInfoRef(offset) => {
118 if let Some(offset) = offset.to_unit_offset(unit_header) {
119 return unit_header
120 .entries_tree(abbreviations, Some(offset))
121 .map_err(|e| GetEntryTreeError::TraceError(e.into()));
122 }
123
124 let mut units = dwarf.units();
126 while let Ok(Some(unit_header)) = units.next() {
127 if offset.to_unit_offset(&unit_header).is_some() {
128 return Err(GetEntryTreeError::WrongUnit(unit_header));
131 }
132 }
133
134 Err(GetEntryTreeError::TraceError(
135 TraceError::DebugInfoOffsetUnitNotFound {
136 debug_info_offset: offset.0,
137 },
138 ))
139 }
140 value => Err(GetEntryTreeError::TraceError(
141 TraceError::WrongAttributeValueType {
142 attribute_name: abstract_origin_attr.name().to_string(),
143 expected_type_name: "UnitRef or DebugInfoRef",
144 gotten_value: format!("{value:X?}"),
145 },
146 )),
147 }
148}
149
150fn get_entry_type_reference_tree<'abbrev, 'unit>(
152 dwarf: &Dwarf<DefaultReader>,
153 unit_header: &'unit UnitHeader<DefaultReader, usize>,
154 abbreviations: &'abbrev Abbreviations,
155 entry: &DebuggingInformationEntry<DefaultReader, usize>,
156) -> Result<EntriesTree<'abbrev, 'unit, DefaultReader>, GetEntryTreeError> {
157 let type_attr = entry
159 .required_attr(unit_header, gimli::constants::DW_AT_type)
160 .map_err(GetEntryTreeError::TraceError)?;
161
162 match type_attr.value() {
164 AttributeValue::UnitRef(offset) => {
165 Ok(unit_header
167 .entries_tree(abbreviations, Some(offset))
168 .map_err(|e| GetEntryTreeError::TraceError(e.into()))?)
169 }
170 AttributeValue::DebugInfoRef(offset) => {
171 if let Some(offset) = offset.to_unit_offset(unit_header) {
172 return unit_header
173 .entries_tree(abbreviations, Some(offset))
174 .map_err(|e| GetEntryTreeError::TraceError(e.into()));
175 }
176
177 let mut units = dwarf.units();
179 while let Ok(Some(unit_header)) = units.next() {
180 if offset.to_unit_offset(&unit_header).is_some() {
181 return Err(GetEntryTreeError::WrongUnit(unit_header));
184 }
185 }
186
187 Err(GetEntryTreeError::TraceError(
188 TraceError::DebugInfoOffsetUnitNotFound {
189 debug_info_offset: offset.0,
190 },
191 ))
192 }
193 value => Err(GetEntryTreeError::TraceError(
194 TraceError::WrongAttributeValueType {
195 attribute_name: type_attr.name().to_string(),
196 expected_type_name: "UnitRef or DebugInfoRef",
197 gotten_value: format!("{value:X?}"),
198 },
199 )),
200 }
201}
202
203pub(crate) enum GetEntryTreeError {
204 WrongUnit(UnitHeader<DefaultReader>),
205 TraceError(TraceError),
206}
207
208impl GetEntryTreeError {
209 fn into_trace_error(self) -> TraceError {
210 match self {
211 Self::TraceError(e) => e,
212 Self::WrongUnit(_) => TraceError::UnitNotFoundAgain,
213 }
214 }
215}
216
217#[macro_export]
218macro_rules! get_entry_abstract_origin_reference_tree_recursive {
219 ($tree_name:ident = ($dwarf:expr, $unit:expr, $abbreviations:expr, $entry:expr)) => {
220 let mut __unit_header = $unit.header.clone();
221 #[allow(unused_mut)]
222 let mut $tree_name = match $crate::variables::get_entry_abstract_origin_reference_tree(
223 $dwarf,
224 &__unit_header,
225 $abbreviations,
226 $entry,
227 ) {
228 Err($crate::variables::GetEntryTreeError::WrongUnit(target_unit)) => {
229 __unit_header = target_unit;
230 $crate::variables::get_entry_abstract_origin_reference_tree(
231 $dwarf,
232 &__unit_header,
233 $abbreviations,
234 $entry,
235 )
236 }
237 value => value,
238 }
239 .map_err(|e| e.into_trace_error());
240 };
241}
242
243#[macro_export]
244macro_rules! get_entry_type_reference_tree_recursive {
245 ($tree_name:ident = ($dwarf:expr, $unit:expr, $abbreviations:expr, $entry:expr)) => {
246 let mut __unit_header = $unit.header.clone();
247 #[allow(unused_mut)]
248 let mut $tree_name = match $crate::variables::get_entry_type_reference_tree(
249 $dwarf,
250 &__unit_header,
251 $abbreviations,
252 $entry,
253 ) {
254 Err($crate::variables::GetEntryTreeError::WrongUnit(target_unit)) => {
255 __unit_header = target_unit;
256 $crate::variables::get_entry_type_reference_tree(
257 $dwarf,
258 &__unit_header,
259 $abbreviations,
260 $entry,
261 )
262 }
263 value => value,
264 }
265 .map_err(|e| e.into_trace_error());
266 };
267}
268
269fn try_read_frame_base<W: funty::Integral>(
270 dwarf: &Dwarf<DefaultReader>,
271 unit: &Unit<DefaultReader, usize>,
272 device_memory: &DeviceMemory<W>,
273 entry: &DebuggingInformationEntry<DefaultReader, usize>,
274) -> Result<Option<W>, TraceError>
275where
276 <W as funty::Numeric>::Bytes: bitvec::view::BitView<Store = u8>,
277{
278 let frame_base_location = evaluate_location(
279 dwarf,
280 unit,
281 device_memory,
282 entry.attr(gimli::constants::DW_AT_frame_base)?,
283 None,
284 )?;
285 let frame_base_data = get_variable_data(
286 device_memory,
287 core::mem::size_of::<W>() as u64 * 8,
288 &frame_base_location,
289 );
290
291 Ok(frame_base_data.ok().map(|data| data.load_le()))
292}
293
294fn find_entry_location(
299 dwarf: &Dwarf<DefaultReader>,
300 unit: &Unit<DefaultReader, usize>,
301 entry: &DebuggingInformationEntry<DefaultReader, usize>,
302) -> Result<Location, TraceError> {
303 let variable_decl_file = entry
305 .attr_value(gimli::constants::DW_AT_decl_file)?
306 .and_then(|f| match f {
307 AttributeValue::FileIndex(index) => Some(index),
308 _ => None,
309 });
310 let variable_decl_line = entry
311 .attr_value(gimli::constants::DW_AT_decl_line)?
312 .and_then(|l| l.udata_value());
313 let variable_decl_column = entry
314 .attr_value(gimli::constants::DW_AT_decl_column)?
315 .and_then(|c| c.udata_value());
316
317 fn path_push(path: &mut String, p: &str) {
318 fn has_unix_root(p: &str) -> bool {
320 p.starts_with('/')
321 }
322
323 fn has_windows_root(p: &str) -> bool {
325 p.starts_with('\\') || p.get(1..3) == Some(":\\")
326 }
327
328 if has_unix_root(p) || has_windows_root(p) {
329 *path = p.to_string();
330 } else {
331 let dir_separator = if has_windows_root(path.as_str()) {
332 '\\'
333 } else {
334 '/'
335 };
336
337 if !path.ends_with(dir_separator) {
338 path.push(dir_separator);
339 }
340 *path += p;
341 }
342 }
343
344 let variable_file = if let (Some(variable_decl_file), Some(line_program)) =
346 (variable_decl_file, unit.line_program.as_ref())
347 {
348 if let Some(file_entry) = line_program.header().file(variable_decl_file) {
350 let mut path = if let Some(comp_dir) = &unit.comp_dir {
351 comp_dir.to_string_lossy()?.into_owned()
352 } else {
353 String::new()
354 };
355
356 if variable_decl_file != 0 {
358 if let Some(directory) = file_entry.directory(line_program.header()) {
359 path_push(
360 &mut path,
361 &dwarf.attr_string(unit, directory)?.to_string_lossy()?,
362 )
363 }
364 }
365
366 path_push(
367 &mut path,
368 &dwarf
369 .attr_string(unit, file_entry.path_name())?
370 .to_string()?,
371 );
372
373 Some(path)
374 } else {
375 None
376 }
377 } else {
378 None
379 };
380
381 Ok(Location {
382 file: variable_file,
383 line: variable_decl_line,
384 column: variable_decl_column,
385 })
386}
387
388fn read_data_member_location(
390 unit_header: &UnitHeader<DefaultReader, usize>,
391 entry: &DebuggingInformationEntry<DefaultReader, usize>,
392) -> Result<u64, TraceError> {
393 Ok(entry
397 .required_attr(unit_header, gimli::constants::DW_AT_data_member_location)?
398 .required_udata_value()?
399 * 8)
400}
401
402fn build_type_value_tree<W: funty::Integral>(
407 dwarf: &Dwarf<DefaultReader>,
408 unit: &Unit<DefaultReader, usize>,
409 abbreviations: &Abbreviations,
410 node: gimli::EntriesTreeNode<DefaultReader>,
411 type_cache: &mut HashMap<DebugInfoOffset, Result<TypeValueTree<W>, TraceError>>,
412) -> Result<TypeValueTree<W>, TraceError> {
413 let entry = node.entry();
415 let entry_die_offset = entry.offset().to_debug_info_offset(&unit.header).unwrap();
416
417 if let Some(existing_type) = type_cache.get(&entry_die_offset) {
418 log::trace!(
419 "Using cached type value tree for {:?} at {:X} (tag: {})",
420 get_entry_name(dwarf, unit, entry),
421 entry_die_offset.0,
422 entry.tag()
423 );
424
425 return (*existing_type).clone();
426 }
427
428 log::trace!(
429 "Building type value tree for {:?} at {:X} (tag: {})",
430 get_entry_name(dwarf, unit, entry),
431 entry_die_offset.0,
432 entry.tag()
433 );
434
435 let result = match entry.tag() {
437 gimli::constants::DW_TAG_variant_part => type_value_tree_building::build_tagged_union(
438 dwarf,
439 unit,
440 abbreviations,
441 node,
442 type_cache,
443 ),
444 tag @ gimli::constants::DW_TAG_structure_type
445 | tag @ gimli::constants::DW_TAG_union_type
446 | tag @ gimli::constants::DW_TAG_class_type => type_value_tree_building::build_object(
447 dwarf,
448 unit,
449 abbreviations,
450 node,
451 type_cache,
452 tag,
453 ),
454 gimli::constants::DW_TAG_base_type => {
455 type_value_tree_building::build_base_type(dwarf, unit, node)
456 }
457 gimli::constants::DW_TAG_pointer_type => {
458 type_value_tree_building::build_pointer(dwarf, unit, abbreviations, node, type_cache)
459 }
460 gimli::constants::DW_TAG_array_type => {
461 type_value_tree_building::build_array(dwarf, unit, abbreviations, node, type_cache)
462 }
463 gimli::constants::DW_TAG_typedef => {
464 type_value_tree_building::build_typedef(dwarf, unit, abbreviations, node, type_cache)
465 }
466 gimli::constants::DW_TAG_enumeration_type => type_value_tree_building::build_enumeration(
467 dwarf,
468 unit,
469 abbreviations,
470 node,
471 type_cache,
472 ),
473 gimli::constants::DW_TAG_subroutine_type => {
474 let mut type_value_tree = TypeValueTree::new(TypeValue::default());
475 let mut type_value = type_value_tree.root_mut();
476
477 type_value.data_mut().variable_type.archetype = Archetype::Subroutine;
478 Ok(type_value_tree)
479 } gimli::constants::DW_TAG_volatile_type => type_value_tree_building::build_volatile_type(
481 dwarf,
482 unit,
483 abbreviations,
484 node,
485 type_cache,
486 ),
487 gimli::constants::DW_TAG_const_type => {
488 type_value_tree_building::build_const_type(dwarf, unit, abbreviations, node, type_cache)
489 }
490 tag => Err(TraceError::TagNotImplemented {
491 tag_name: tag.to_string(),
492 entry_debug_info_offset: entry.offset().to_debug_info_offset(&unit.header).unwrap().0,
493 }),
494 };
495
496 type_cache
497 .entry(entry_die_offset)
498 .or_insert_with(|| result.clone());
499
500 result
501}
502
503fn evaluate_location<W: funty::Integral>(
508 dwarf: &Dwarf<DefaultReader>,
509 unit: &Unit<DefaultReader, usize>,
510 device_memory: &DeviceMemory<W>,
511 location: Option<Attribute<DefaultReader>>,
512 frame_base: Option<W>,
513) -> Result<VariableLocationResult, TraceError>
514where
515 <W as funty::Numeric>::Bytes: bitvec::view::BitView<Store = u8>,
516{
517 let location = match location {
519 Some(location) => location.value(),
520 None => return Ok(VariableLocationResult::NoLocationAttribute),
521 };
522
523 let location_expression = match location {
525 AttributeValue::Block(ref data) => gimli::Expression(data.clone()),
526 AttributeValue::Exprloc(ref data) => data.clone(),
527 AttributeValue::LocationListsRef(l) => {
528 let mut locations = dwarf.locations(unit, l)?;
529 let mut location = None;
530
531 while let Ok(Some(maybe_location)) = locations.next() {
532 let check_pc = device_memory.register(gimli::Arm::PC)?;
533
534 if check_pc.as_u64() >= maybe_location.range.begin
535 && check_pc.as_u64() < maybe_location.range.end
536 {
537 location = Some(maybe_location);
538 break;
539 }
540 }
541
542 if let Some(location) = location {
543 location.data
544 } else {
545 return Ok(VariableLocationResult::LocationListNotFound);
546 }
547 }
548 _ => unreachable!(),
549 };
550
551 let result = evaluate_expression(
553 unit,
554 device_memory,
555 frame_base,
556 location_expression.evaluation(unit.encoding()),
557 );
558
559 match result {
560 Err(TraceError::LocationEvaluationStepNotImplemented(step)) => {
561 Ok(VariableLocationResult::LocationEvaluationStepNotImplemented(step))
562 }
563 Err(TraceError::MissingMemory(_)) => Ok(VariableLocationResult::NoLocationFound),
564 Err(e) => Err(e),
565 Ok(pieces) if pieces.is_empty() => Ok(VariableLocationResult::NoLocationFound),
566 Ok(pieces) => Ok(VariableLocationResult::LocationsFound(pieces)),
567 }
568}
569
570fn evaluate_expression<W: funty::Integral>(
571 unit: &Unit<DefaultReader, usize>,
572 device_memory: &DeviceMemory<W>,
573 frame_base: Option<W>,
574 mut evaluation: Evaluation<DefaultReader>,
575) -> Result<Vec<Piece<DefaultReader, usize>>, TraceError>
576where
577 <W as funty::Numeric>::Bytes: bitvec::view::BitView<Store = u8>,
578{
579 let mut result = evaluation.evaluate()?;
584 while result != EvaluationResult::Complete {
585 log::trace!("Location evaluation result: {result:?}");
586 match result {
587 EvaluationResult::RequiresRegister {
588 register,
589 base_type,
590 } => {
591 let value = device_memory.register(register)?;
592 let value = match base_type.0 {
593 0 => gimli::Value::Generic(value.as_u64()),
594 val => return Err(TraceError::OperationNotImplemented { operation: format!("Other types than generic haven't been implemented yet. base_type value: {val}"), file: file!(), line: line!() } ),
595 };
596 result = evaluation.resume_with_register(value)?;
597 }
598 EvaluationResult::RequiresFrameBase if frame_base.is_some() => {
599 result = evaluation.resume_with_frame_base(
600 frame_base.ok_or(TraceError::UnknownFrameBase)?.as_u64(),
601 )?;
602 }
603 EvaluationResult::RequiresRelocatedAddress(address) => {
604 result = evaluation.resume_with_relocated_address(address)?;
606 }
607 EvaluationResult::RequiresEntryValue(ex) => {
608 let entry_pieces = evaluate_expression(
609 unit,
610 device_memory,
611 frame_base,
612 ex.evaluation(unit.encoding()),
613 )?;
614
615 let entry_data = get_variable_data(
616 device_memory,
617 W::BITS as u64,
618 &VariableLocationResult::LocationsFound(entry_pieces),
619 )?;
620
621 result = evaluation.resume_with_entry_value(gimli::Value::Generic(
622 entry_data.load_le::<W>().as_u64(), ))?;
624 }
625 EvaluationResult::RequiresMemory {
626 address,
627 size,
628 space: None,
629 base_type: UnitOffset(0),
630 } => {
631 let mut data = device_memory
635 .read_slice(address..address + size as u64)?
636 .ok_or(TraceError::MissingMemory(address))?;
637
638 data.extend(
639 std::iter::once(0)
640 .cycle()
641 .take((W::BITS / 8) as usize - size as usize),
642 );
643
644 let value = gimli::Value::Generic(data.as_bits::<Lsb0>().load_le::<W>().as_u64());
645 result = evaluation.resume_with_memory(value)?;
646 }
647 r => {
648 return Err(TraceError::LocationEvaluationStepNotImplemented(
649 std::rc::Rc::new(r),
650 ))
651 }
652 }
653 }
654
655 Ok(evaluation.result())
656}
657
658fn get_piece_data<W: funty::Integral>(
666 device_memory: &DeviceMemory<W>,
667 piece: &Piece<DefaultReader, usize>,
668 variable_size: u64,
669) -> Result<Option<bitvec::vec::BitVec<u8, Lsb0>>, VariableDataError>
670where
671 <W as funty::Numeric>::Bytes: bitvec::view::BitView<Store = u8>,
672{
673 let mut data = match piece.location.clone() {
674 gimli::Location::Empty => return Err(VariableDataError::OptimizedAway),
675 gimli::Location::Register { register } => Some(
676 device_memory
677 .register(register)
678 .map(|r| r.to_ne_bytes().view_bits().to_bitvec()) .map_err(|e| VariableDataError::NoDataAvailableAt(e.to_string()))?,
680 ),
681 gimli::Location::Address { address } => device_memory
682 .read_slice(address..(address + variable_size))?
683 .map(|b| b.view_bits().to_bitvec()),
684 gimli::Location::Value { value } => {
685 let mut data = BitVec::new();
686
687 match value {
688 gimli::Value::Generic(v) => data.extend(v.view_bits::<Lsb0>()),
689 gimli::Value::I8(v) => data.extend((v as u8).view_bits::<Lsb0>()),
690 gimli::Value::U8(v) => data.extend(v.view_bits::<Lsb0>()),
691 gimli::Value::I16(v) => data.extend((v as u16).view_bits::<Lsb0>()),
692 gimli::Value::U16(v) => data.extend(v.view_bits::<Lsb0>()),
693 gimli::Value::I32(v) => data.extend((v as u32).view_bits::<Lsb0>()),
694 gimli::Value::U32(v) => data.extend(v.view_bits::<Lsb0>()),
695 gimli::Value::I64(v) => data.extend((v as u64).view_bits::<Lsb0>()),
696 gimli::Value::U64(v) => data.extend(v.view_bits::<Lsb0>()),
697 gimli::Value::F32(v) => data.extend(v.to_bits().view_bits::<Lsb0>()),
698 gimli::Value::F64(v) => data.extend(v.to_bits().view_bits::<Lsb0>()),
699 }
700
701 Some(data)
702 }
703 gimli::Location::Bytes { value } => value
704 .get(0..variable_size as usize)
705 .map(|b| b.view_bits().to_bitvec()),
706 gimli::Location::ImplicitPointer {
707 value: _,
708 byte_offset: _,
709 } => {
710 return Err(VariableDataError::OperationNotImplemented {
711 operation: "`ImplicitPointer` location not yet supported".into(),
712 file: file!(),
713 line: line!(),
714 })
715 }
716 };
717
718 if let Some(data) = data.as_mut() {
720 if let Some(offset) = piece.bit_offset {
721 data.drain(0..offset as usize);
722 }
723 if let Some(length) = piece.size_in_bits {
724 data.truncate(length as usize);
725 }
726 }
727
728 Ok(data)
729}
730
731fn get_variable_data<W: funty::Integral>(
737 device_memory: &DeviceMemory<W>,
738 variable_size: u64,
739 variable_location: &VariableLocationResult,
740) -> Result<BitVec<u8, Lsb0>, VariableDataError>
741where
742 <W as funty::Numeric>::Bytes: bitvec::view::BitView<Store = u8>,
743{
744 match variable_location {
745 VariableLocationResult::NoLocationAttribute => Err(VariableDataError::OptimizedAway),
746 VariableLocationResult::LocationListNotFound => Err(VariableDataError::OptimizedAway),
747 VariableLocationResult::NoLocationFound => Err(VariableDataError::OptimizedAway),
748 VariableLocationResult::LocationsFound(pieces) => {
749 let mut data = BitVec::new();
750
751 let variable_size_bytes = div_ceil(variable_size, 8);
753
754 for piece in pieces {
756 let piece_data = get_piece_data(device_memory, piece, variable_size_bytes)?;
757
758 if let Some(mut piece_data) = piece_data {
759 data.append(&mut piece_data);
761 } else {
762 return Err(VariableDataError::NoDataAvailableAt(format!(
764 "{:X?}",
765 piece.location
766 )));
767 };
768 }
769
770 Ok(data)
771 }
772 VariableLocationResult::LocationEvaluationStepNotImplemented(step) => Err(
773 VariableDataError::UnimplementedLocationEvaluationStep(format!("{step:?}")),
774 ),
775 }
776}
777
778fn get_variable_address(variable_location: &VariableLocationResult) -> Option<u64> {
780 match variable_location {
781 VariableLocationResult::LocationsFound(pieces) => {
782 for piece in pieces {
783 if let gimli::Location::Address { address } = piece.location {
784 return Some(address);
785 }
786 }
787 None
788 }
789 _ => None,
790 }
791}
792
793fn read_base_type<W: funty::Integral>(
794 encoding: gimli::DwAte,
795 data: &BitSlice<u8, Lsb0>,
796) -> Result<Value<W>, VariableDataError> {
797 match encoding {
798 gimli::constants::DW_ATE_unsigned | gimli::constants::DW_ATE_unsigned_char => {
799 match data.len() {
800 8 => Ok(Value::Uint(data.load_le::<u8>() as _)),
801 16 => Ok(Value::Uint(data.load_le::<u16>() as _)),
802 32 => Ok(Value::Uint(data.load_le::<u32>() as _)),
803 64 => Ok(Value::Uint(data.load_le::<u64>() as _)),
804 128 => Ok(Value::Uint(data.load_le::<u128>() as _)),
805 _ => Err(VariableDataError::InvalidSize { bits: data.len() }),
806 }
807 }
808 gimli::constants::DW_ATE_signed | gimli::constants::DW_ATE_signed_char => {
809 match data.len() {
810 8 => Ok(Value::Int(data.load_le::<u8>() as _)),
811 16 => Ok(Value::Int(data.load_le::<u16>() as _)),
812 32 => Ok(Value::Int(data.load_le::<u32>() as _)),
813 64 => Ok(Value::Int(data.load_le::<u64>() as _)),
814 128 => Ok(Value::Int(data.load_le::<u128>() as _)),
815 _ => Err(VariableDataError::InvalidSize { bits: data.len() }),
816 }
817 }
818 gimli::constants::DW_ATE_float => match data.len() {
819 32 => Ok(Value::Float(f32::from_bits(data.load_le::<u32>()) as _)),
820 64 => Ok(Value::Float(f64::from_bits(data.load_le::<u64>()) as _)),
821 _ => Err(VariableDataError::InvalidSize { bits: data.len() }),
822 },
823 gimli::constants::DW_ATE_boolean => Ok(Value::Bool(data.iter().any(|v| *v))),
824 gimli::constants::DW_ATE_address => match data.len() {
825 8 => Ok(Value::Address(
826 data.load_le::<u8>().try_into().ok().unwrap(),
827 )),
828 16 => Ok(Value::Address(
829 data.load_le::<u16>().try_into().ok().unwrap(),
830 )),
831 32 => Ok(Value::Address(
832 data.load_le::<u32>().try_into().ok().unwrap(),
833 )),
834 64 => Ok(Value::Address(
835 data.load_le::<u64>().try_into().ok().unwrap(),
836 )),
837 _ => Err(VariableDataError::InvalidSize { bits: data.len() }),
838 },
839 t => Err(VariableDataError::UnsupportedBaseType {
840 base_type: t,
841 data: data.to_bitvec(),
842 }),
843 }
844}
845
846fn read_variable_data<W: funty::Integral>(
848 mut variable: Pin<&mut TypeValueNode<W>>,
849 data: &BitSlice<u8, Lsb0>,
850 device_memory: &DeviceMemory<W>,
851 type_cache: &mut HashMap<DebugInfoOffset, Result<TypeValueTree<W>, TraceError>>,
852) {
853 if variable.data().bit_length() > data.len() as u64 {
856 log::warn!(
857 "Variable of type {} claims to take up {} bits, but only {} bits are available",
858 variable.data().variable_type.name,
859 variable.data().bit_range.end,
860 data.len()
861 );
862 }
863
864 match variable.data().variable_type.archetype {
865 Archetype::TaggedUnion => {
866 assert!(variable.front_mut().unwrap().data().name == "discriminant");
868
869 read_variable_data(
871 variable.front_mut().unwrap(),
872 data,
873 device_memory,
874 type_cache,
875 );
876
877 let discriminator_value = match &variable.front().unwrap().data().variable_value {
878 Ok(value) => value.clone(),
879 _ => {
880 return;
881 }
882 };
883
884 let active_variant = variable
887 .iter_mut()
888 .skip(1)
889 .find(|variant| variant.data().variable_value.as_ref() == Ok(&discriminator_value));
890
891 if let Some(active_variant) = active_variant {
892 read_variable_data(active_variant, data, device_memory, type_cache);
893 } else if let Some(default_variant) = variable
894 .iter_mut()
895 .skip(1)
896 .find(|variant| variant.data().variable_value.is_err())
897 {
898 read_variable_data(default_variant, data, device_memory, type_cache);
900 }
901 }
902 Archetype::TaggedUnionVariant => {
903 read_variable_data(
904 variable.front_mut().unwrap(),
905 data,
906 device_memory,
907 type_cache,
908 );
909 }
910 Archetype::Structure
911 | Archetype::Union
912 | Archetype::Class
913 | Archetype::ObjectMemberPointer => {
914 for mut child in variable.iter_mut() {
918 let child_data = match data.get(child.data().bit_range_usize()) {
919 Some(child_data) => child_data,
920 None => BitSlice::empty(),
921 };
922
923 let range = child.data().bit_range.clone();
924 child.data_mut().bit_range = 0..range.end - range.start;
925 read_variable_data(child, child_data, device_memory, type_cache);
926 }
927
928 if &variable.data().variable_type.name == "&str" {
929 let pointer = &variable
931 .iter()
932 .find(|field| field.data().name == "data_ptr")
933 .ok_or(())
934 .map(|node| &node.data().variable_value);
935 let length = &variable
936 .iter()
937 .find(|field| field.data().name == "length")
938 .ok_or(())
939 .map(|node| &node.data().variable_value);
940
941 match (pointer, length) {
942 (Ok(Ok(Value::Address(pointer))), Ok(Ok(Value::Uint(length))))
943 if *length < 64 * 1024 =>
944 {
945 let data = device_memory
947 .read_slice(pointer.as_u64()..pointer.as_u64() + *length as u64);
948 if let Ok(Some(data)) = data {
949 variable.data_mut().variable_value =
950 Ok(Value::String(data, StringFormat::Utf8));
951 } else {
952 variable.data_mut().variable_value = Ok(Value::Object);
954 }
955 }
956 (Ok(Ok(Value::Address(_))), Ok(Ok(Value::Uint(length))))
957 if *length >= 64 * 1024 =>
958 {
959 log::warn!(
960 "We started decoding the string {}, but it is {length} bytes long",
961 variable.data().name
962 );
963 variable.data_mut().variable_value = Ok(Value::Object);
965 }
966 _ => {
967 log::error!(
968 "We started decoding the string {}, but found an error",
969 variable.data().name
970 );
971 variable.data_mut().variable_value = Ok(Value::Object);
973 }
974 }
975 } else {
976 variable.data_mut().variable_value = Ok(Value::Object);
978 }
979 }
980 Archetype::BaseType(encoding) => {
981 if variable.data().bit_length() == 0 && variable.data().variable_type.name == "()" {
982 variable.data_mut().variable_value = Ok(Value::Unit);
983 } else {
984 variable.data_mut().variable_value =
985 match data.get(variable.data().bit_range_usize()) {
986 Some(data) => read_base_type(encoding, data),
987 None => Err(VariableDataError::NoDataAvailable),
988 };
989 }
990 }
991 Archetype::Pointer(die_offset) => {
992 variable.data_mut().variable_value = match data.get(variable.data().bit_range_usize()) {
997 Some(data) => read_base_type(gimli::constants::DW_ATE_address, data),
998 None => Err(VariableDataError::NoDataAvailable),
999 };
1000
1001 let address = match variable.data().variable_value {
1002 Ok(Value::Address(addr)) => Ok(addr),
1003 _ => Err(VariableDataError::InvalidPointerData),
1004 };
1005
1006 let pointee_tree_clone = match type_cache
1007 .get(&die_offset)
1008 .expect("Pointers must have their pointee type cached")
1009 .clone()
1010 {
1011 Ok(pointee_tree_clone) => pointee_tree_clone,
1012 Err(_) => TypeValueTree::new(TypeValue {
1013 name: "Pointee".into(),
1014 variable_type: VariableType {
1015 archetype: Archetype::Unknown,
1016 ..Default::default()
1017 },
1018 bit_range: 0..0,
1019 variable_value: Err(VariableDataError::Unknown),
1020 }),
1021 };
1022 variable.push_back(pointee_tree_clone);
1023 let mut pointee = variable.back_mut().unwrap();
1024
1025 match address {
1026 Ok(address) if address == W::ZERO => {
1027 pointee.data_mut().variable_value = Err(VariableDataError::NullPointer)
1028 }
1029 Ok(address) => {
1030 let pointee_data = device_memory.read_slice(
1031 address.as_u64()
1032 ..address.as_u64() + div_ceil(pointee.data().bit_range.end, 8),
1033 );
1034
1035 match pointee_data {
1036 Ok(Some(pointee_data)) => {
1037 read_variable_data(
1038 pointee,
1039 pointee_data.view_bits(),
1040 device_memory,
1041 type_cache,
1042 );
1043 }
1044 Ok(None) => {
1045 pointee.data_mut().variable_value =
1046 Err(VariableDataError::NoDataAvailable);
1047 }
1048 Err(e) => {
1049 pointee.data_mut().variable_value = Err(e.into());
1050 }
1051 }
1052 }
1053 Err(e) => pointee.data_mut().variable_value = Err(e),
1054 }
1055 }
1056 Archetype::Array => {
1057 variable.data_mut().variable_value = Ok(Value::Array);
1058 for mut element in variable.iter_mut() {
1060 match data.get(element.data().bit_range_usize()) {
1061 Some(data) => {
1062 let element_bitrange = element.data().bit_range.clone();
1063 element.data_mut().bit_range =
1064 0..element_bitrange.end - element_bitrange.start;
1065 read_variable_data(element, data, device_memory, type_cache)
1066 }
1067 None => {
1068 element.data_mut().variable_value = Err(VariableDataError::NoDataAvailable)
1069 }
1070 }
1071 }
1072 }
1073 Archetype::Enumeration => {
1074 variable.data_mut().variable_value = Ok(Value::Enumeration);
1075
1076 read_variable_data(
1078 variable.front_mut().expect("Enumerations have a child"),
1079 data,
1080 device_memory,
1081 type_cache,
1082 );
1083 }
1084 Archetype::Typedef => {
1085 variable.data_mut().variable_value = Ok(Value::Typedef);
1086
1087 read_variable_data(
1089 variable.front_mut().expect("Typedefs have a child"),
1090 data,
1091 device_memory,
1092 type_cache,
1093 );
1094 }
1095 Archetype::Enumerator => {
1096 }
1098 Archetype::Subroutine => {
1099 variable.data_mut().variable_value = Ok(Value::Object);
1100 }
1102 Archetype::Unknown => {
1103 }
1105 }
1106}
1107
1108fn read_variable_entry<W: funty::Integral>(
1109 dwarf: &Dwarf<DefaultReader>,
1110 unit: &Unit<DefaultReader, usize>,
1111 abbreviations: &Abbreviations,
1112 device_memory: &DeviceMemory<W>,
1113 frame_base: Option<W>,
1114 entry: &DebuggingInformationEntry<DefaultReader, usize>,
1115 type_cache: &mut HashMap<DebugInfoOffset, Result<TypeValueTree<W>, TraceError>>,
1116) -> Result<Option<Variable<W>>, TraceError>
1117where
1118 <W as funty::Numeric>::Bytes: bitvec::view::BitView<Store = u8>,
1119{
1120 get_entry_abstract_origin_reference_tree_recursive!(
1121 abstract_origin_tree = (dwarf, unit, abbreviations, entry)
1122 );
1123 let mut abstract_origin_tree = abstract_origin_tree.ok();
1124
1125 let abstract_origin_node = abstract_origin_tree
1126 .as_mut()
1127 .and_then(|tree| tree.root().ok());
1128 let abstract_origin_entry = abstract_origin_node.as_ref().map(|node| node.entry());
1129
1130 let variable_name = get_entry_name(dwarf, unit, entry);
1132
1133 let mut variable_name = match (variable_name, abstract_origin_entry) {
1135 (Err(_), Some(entry)) => get_entry_name(dwarf, unit, entry),
1136 (variable_name, _) => variable_name,
1137 };
1138
1139 if entry.tag() == gimli::constants::DW_TAG_formal_parameter && variable_name.is_err() {
1140 log::trace!("Formal parameter does not have a name, renaming it to 'param'");
1141 variable_name = Ok("param".into());
1142 }
1143
1144 let variable_linkage_name = get_entry_linkage_name(dwarf, unit, entry)?;
1146
1147 get_entry_type_reference_tree_recursive!(
1149 variable_type_tree = (dwarf, unit, abbreviations, entry)
1150 );
1151
1152 get_entry_abstract_origin_reference_tree_recursive!(
1153 abstract_origin_tree = (dwarf, unit, abbreviations, entry)
1154 );
1155
1156 let variable_type_value_tree = (|| match (variable_type_tree, abstract_origin_tree) {
1157 (Ok(mut variable_type_tree), _) => {
1158 let type_root = variable_type_tree.root()?;
1159 build_type_value_tree(dwarf, unit, abbreviations, type_root, type_cache)
1160 }
1161 (_, Ok(mut abstract_origin_tree)) => {
1162 let abstract_entry = abstract_origin_tree.root()?.entry().clone();
1163 get_entry_type_reference_tree_recursive!(
1164 abstract_variable_type_tree = (dwarf, unit, abbreviations, &abstract_entry)
1165 );
1166
1167 match abstract_variable_type_tree {
1168 Ok(mut abstract_variable_type_tree) => {
1169 let type_root = abstract_variable_type_tree.root()?;
1170 build_type_value_tree(dwarf, unit, abbreviations, type_root, type_cache)
1171 }
1172 Err(e) => Err(e),
1173 }
1174 }
1175 (Err(e), _) => Err(e),
1176 })();
1177
1178 let variable_kind = VariableKind {
1179 zero_sized: variable_type_value_tree
1180 .as_ref()
1181 .map(|vt| vt.data().bit_length() == 0)
1182 .unwrap_or_default(),
1183 inlined: abstract_origin_entry.is_some(),
1184 parameter: entry.tag() == gimli::constants::DW_TAG_formal_parameter,
1185 };
1186
1187 let mut variable_file_location = find_entry_location(dwarf, unit, entry)?;
1189 if let (None, Some(abstract_origin_entry)) =
1190 (&variable_file_location.file, abstract_origin_entry)
1191 {
1192 variable_file_location = find_entry_location(dwarf, unit, abstract_origin_entry)?;
1193 }
1194
1195 match (variable_name, variable_type_value_tree) {
1196 (Ok(variable_name), Ok(variable_type_value_tree)) if variable_kind.zero_sized => {
1197 Ok(Some(Variable {
1198 name: variable_name,
1199 kind: variable_kind,
1200 type_value: variable_type_value_tree,
1201 location: variable_file_location,
1202 linkage_name: variable_linkage_name,
1203 address: None, }))
1205 }
1206 (Ok(variable_name), Ok(mut variable_type_value_tree)) => {
1207 let location_attr = entry.attr(gimli::constants::DW_AT_location)?;
1208
1209 let location_attr = match (location_attr, abstract_origin_entry) {
1210 (None, Some(entry)) => entry.attr(gimli::constants::DW_AT_location)?,
1211 (location_attr, _) => location_attr,
1212 };
1213
1214 let variable_location =
1216 evaluate_location(dwarf, unit, device_memory, location_attr, frame_base)?;
1217
1218 log::debug!(
1219 "Reading variable data for `{variable_name}` at {variable_location:X?} of {} bits",
1220 variable_type_value_tree.data().bit_length()
1221 );
1222 let variable_data = get_variable_data(
1223 device_memory,
1224 variable_type_value_tree.data().bit_length(),
1225 &variable_location,
1226 );
1227
1228 match variable_data {
1229 Ok(variable_data) => read_variable_data(
1231 variable_type_value_tree.root_mut(),
1232 &variable_data,
1233 device_memory,
1234 type_cache,
1235 ),
1236 Err(e) => {
1238 variable_type_value_tree
1239 .root_mut()
1240 .data_mut()
1241 .variable_value = Err(e)
1242 }
1243 }
1244
1245 Ok(Some(Variable {
1246 name: variable_name,
1247 kind: variable_kind,
1248 type_value: variable_type_value_tree,
1249 location: variable_file_location,
1250 linkage_name: variable_linkage_name,
1251 address: get_variable_address(&variable_location),
1252 }))
1253 }
1254 (Ok(variable_name), Err(type_error)) => {
1255 log::info!(
1256 "Could not read the type of variable `{}` of entry {:X?}: {}",
1257 variable_name,
1258 entry.offset().to_debug_info_offset(&unit.header),
1259 type_error
1260 );
1261 Ok(None)
1262 }
1263 (Err(name_error), _) => {
1264 log::debug!(
1265 "Could not get the name of a variable of entry {:X?}: {}",
1266 entry.offset().to_debug_info_offset(&unit.header),
1267 name_error
1268 );
1269 Ok(None)
1270 }
1271 }
1272}
1273
1274pub fn find_variables_in_function<W: funty::Integral>(
1275 dwarf: &Dwarf<DefaultReader>,
1276 unit: &Unit<DefaultReader, usize>,
1277 abbreviations: &Abbreviations,
1278 device_memory: &DeviceMemory<W>,
1279 node: gimli::EntriesTreeNode<DefaultReader>,
1280 type_cache: &mut HashMap<DebugInfoOffset, Result<TypeValueTree<W>, TraceError>>,
1281) -> Result<Vec<Variable<W>>, TraceError>
1282where
1283 <W as funty::Numeric>::Bytes: bitvec::view::BitView<Store = u8>,
1284{
1285 #[allow(clippy::too_many_arguments)]
1286 fn recursor<W: funty::Integral>(
1287 dwarf: &Dwarf<DefaultReader>,
1288 unit: &Unit<DefaultReader, usize>,
1289 abbreviations: &Abbreviations,
1290 device_memory: &DeviceMemory<W>,
1291 node: gimli::EntriesTreeNode<DefaultReader>,
1292 variables: &mut Vec<Variable<W>>,
1293 mut frame_base: Option<W>,
1294 type_cache: &mut HashMap<DebugInfoOffset, Result<TypeValueTree<W>, TraceError>>,
1295 ) -> Result<(), TraceError>
1296 where
1297 <W as funty::Numeric>::Bytes: bitvec::view::BitView<Store = u8>,
1298 {
1299 let entry = node.entry();
1300
1301 log::trace!(
1302 "Checking out the entry @ .debug_info: {:X}",
1303 unit.header.offset().as_debug_info_offset().unwrap().0 + entry.offset().0
1304 );
1305
1306 if let Some(new_frame_base) = try_read_frame_base(dwarf, unit, device_memory, entry)? {
1307 frame_base = Some(new_frame_base);
1308 }
1309
1310 if entry.tag() == gimli::constants::DW_TAG_variable
1311 || entry.tag() == gimli::constants::DW_TAG_formal_parameter
1312 {
1313 if let Some(variable) = read_variable_entry(
1314 dwarf,
1315 unit,
1316 abbreviations,
1317 device_memory,
1318 frame_base,
1319 entry,
1320 type_cache,
1321 )? {
1322 variables.push(variable);
1323 }
1324 }
1325
1326 let mut children = node.children();
1327 while let Some(child) = children.next()? {
1328 recursor(
1329 dwarf,
1330 unit,
1331 abbreviations,
1332 device_memory,
1333 child,
1334 variables,
1335 frame_base,
1336 type_cache,
1337 )?;
1338 }
1339
1340 Ok(())
1341 }
1342
1343 let mut variables = Vec::new();
1344 recursor(
1345 dwarf,
1346 unit,
1347 abbreviations,
1348 device_memory,
1349 node,
1350 &mut variables,
1351 None,
1352 type_cache,
1353 )?;
1354 Ok(variables)
1355}
1356
1357pub fn find_static_variables<W: funty::Integral>(
1358 dwarf: &Dwarf<DefaultReader>,
1359 device_memory: &DeviceMemory<W>,
1360 type_cache: &mut HashMap<DebugInfoOffset, Result<TypeValueTree<W>, TraceError>>,
1361) -> Result<Vec<Variable<W>>, TraceError>
1362where
1363 <W as funty::Numeric>::Bytes: bitvec::view::BitView<Store = u8>,
1364{
1365 fn recursor<W: funty::Integral>(
1366 dwarf: &Dwarf<DefaultReader>,
1367 unit: &Unit<DefaultReader, usize>,
1368 abbreviations: &Abbreviations,
1369 device_memory: &DeviceMemory<W>,
1370 node: gimli::EntriesTreeNode<DefaultReader>,
1371 variables: &mut Vec<Variable<W>>,
1372 type_cache: &mut HashMap<DebugInfoOffset, Result<TypeValueTree<W>, TraceError>>,
1373 ) -> Result<(), TraceError>
1374 where
1375 <W as funty::Numeric>::Bytes: bitvec::view::BitView<Store = u8>,
1376 {
1377 let entry = node.entry();
1378
1379 match entry.tag() {
1380 gimli::constants::DW_TAG_compile_unit => {}
1381 gimli::constants::DW_TAG_namespace => {}
1382 gimli::constants::DW_TAG_structure_type
1383 | gimli::constants::DW_TAG_subprogram
1384 | gimli::constants::DW_TAG_enumeration_type
1385 | gimli::constants::DW_TAG_base_type
1386 | gimli::constants::DW_TAG_array_type
1387 | gimli::constants::DW_TAG_pointer_type
1388 | gimli::constants::DW_TAG_subroutine_type
1389 | gimli::constants::DW_TAG_typedef
1390 | gimli::constants::DW_TAG_restrict_type
1391 | gimli::constants::DW_TAG_const_type
1392 | gimli::constants::DW_TAG_union_type
1393 | gimli::constants::DW_TAG_volatile_type => return Ok(()),
1394 gimli::constants::DW_TAG_variable => {
1395 if let Some(variable) = read_variable_entry(
1396 dwarf,
1397 unit,
1398 abbreviations,
1399 device_memory,
1400 None,
1401 entry,
1402 type_cache,
1403 )? {
1404 variables.push(variable);
1405 }
1406 }
1407 tag => {
1408 log::error!(
1409 "Unexpected tag in the search of static variables: {} at {:X?}",
1410 tag,
1411 entry.offset().to_debug_info_offset(&unit.header)
1412 );
1413 return Ok(());
1414 }
1415 }
1416
1417 let mut children = node.children();
1418 while let Some(child) = children.next()? {
1419 recursor(
1420 dwarf,
1421 unit,
1422 abbreviations,
1423 device_memory,
1424 child,
1425 variables,
1426 type_cache,
1427 )?;
1428 }
1429
1430 Ok(())
1431 }
1432
1433 let mut variables = Vec::new();
1434 let mut units = dwarf.units();
1435 while let Some(unit_header) = units.next()? {
1436 let abbreviations = dwarf.abbreviations(&unit_header)?;
1437 recursor(
1438 dwarf,
1439 &dwarf.unit(unit_header.clone())?,
1440 &abbreviations,
1441 device_memory,
1442 unit_header.entries_tree(&abbreviations, None)?.root()?,
1443 &mut variables,
1444 type_cache,
1445 )?;
1446 }
1447
1448 Ok(variables)
1449}