1use std::{collections::HashMap, ops::Range};
2
3use super::{
4 DebugError, DebugRegisters, EndianReader, SourceLocation, VariableCache, debug_info::*,
5 extract_byte_size, extract_file, extract_line, function_die::FunctionDie, variable::*,
6};
7use crate::{language, stack_frame::StackFrameInfo};
8use gimli::{
9 AttributeValue, DebugInfoOffset, DebuggingInformationEntry, EvaluationResult, Location,
10 UnitOffset,
11};
12use probe_rs::MemoryInterface;
13
14#[derive(Debug)]
16pub(crate) enum ExpressionResult {
17 Value(VariableValue),
18 Location(VariableLocation),
19}
20
21pub struct UnitInfo {
23 pub(crate) unit: gimli::Unit<GimliReader, usize>,
24 dwarf_language: gimli::DwLang,
25 language: Box<dyn language::ProgrammingLanguage>,
26 parents: HashMap<UnitOffset, UnitOffset>,
28 function_dies: Vec<(Range<u64>, UnitOffset)>,
30}
31
32impl UnitInfo {
33 pub fn new(unit: gimli::Unit<GimliReader, usize>, dwarf: &gimli::Dwarf<GimliReader>) -> Self {
35 let dwarf_language = if let Some(AttributeValue::Language(unit_language)) = unit
36 .entry(unit.root_offset())
37 .ok()
38 .and_then(|root| root.attr_value(gimli::DW_AT_language))
39 {
40 unit_language
41 } else {
42 tracing::warn!("Unable to retrieve DW_AT_language attribute, assuming Rust.");
43 gimli::DW_LANG_Rust
44 };
45
46 let mut this = Self {
47 unit,
48 dwarf_language,
49 language: language::from_dwarf(dwarf_language),
50 parents: HashMap::new(),
51 function_dies: Vec::new(),
52 };
53
54 this.process_unit(dwarf);
55
56 this
57 }
58
59 fn process_unit(&mut self, dwarf: &gimli::Dwarf<GimliReader>) {
60 let mut entries_cursor = self.unit.entries();
61
62 let mut prev_offset = None;
63 let mut previous_depth = entries_cursor.depth();
64 while let Ok(Some(current)) = entries_cursor.next_dfs() {
65 let parent_offset = match current.depth() - previous_depth {
66 1 => {
67 prev_offset
69 }
70 x if x <= 0 => {
71 let walk_up = |mut levels| {
72 let mut cursor = prev_offset.map(|off| self.parents.get(&off).copied())?;
75 while levels != 0 {
76 cursor = cursor.map(|off| self.parents.get(&off).copied())?;
77 levels += 1;
78 }
79 cursor
80 };
81 walk_up(x)
82 }
83 _ => unreachable!("DFS algorithms never jump down multiple levels in the graph"),
84 };
85
86 if let Some(offset) = parent_offset {
87 self.parents.insert(current.offset(), offset);
88 }
89 previous_depth = current.depth();
90 prev_offset = Some(current.offset());
91
92 if current.tag() == gimli::DW_TAG_subprogram
94 && let Ok(Some(ranges)) = FunctionDie::function_ranges(current, self, dwarf)
95 {
96 for range in ranges {
97 self.function_dies.push((range, current.offset()));
98 }
99 }
100
101 }
103 }
104
105 pub(crate) fn get_language(&self) -> gimli::DwLang {
109 self.dwarf_language
110 }
111
112 pub(crate) fn debug_info_offset(&self) -> Result<DebugInfoOffset, DebugError> {
113 self.unit.header.offset().to_debug_info_offset(&self.unit.header).ok_or_else(|| DebugError::Other(
114 "Failed to convert unit header offset to debug info offset. This is a bug, please report it.".to_string()
115 ))
116 }
117
118 pub(crate) fn get_function_dies<'debug_info>(
122 &'debug_info self,
123 debug_info: &'debug_info super::DebugInfo,
124 address: u64,
125 ) -> Result<Vec<FunctionDie<'debug_info>>, DebugError> {
126 tracing::trace!("Searching Function DIE for address {:#010x}", address);
127
128 let Some((_, start_offset)) = self
130 .function_dies
131 .iter()
132 .find(|(range, _)| range.contains(&address))
133 .cloned()
134 else {
135 return Ok(vec![]);
136 };
137
138 let mut entries_cursor = self.unit.entries_at_offset(start_offset)?;
139 while let Ok(Some(current)) = entries_cursor.next_dfs() {
140 let Some(die) = FunctionDie::new(current.clone(), self, debug_info, address)? else {
141 continue;
142 };
143
144 let mut functions = vec![die];
145 tracing::debug!(
146 "Found DIE: name={:?}",
147 functions[0].function_name(debug_info)
148 );
149
150 tracing::debug!("Checking for inlined functions");
151 let inlined_functions =
152 self.find_inlined_functions(debug_info, address, current.offset())?;
153 tracing::debug!(
154 "{} inlined functions for address {:#010x}",
155 inlined_functions.len(),
156 address
157 );
158
159 functions.extend(inlined_functions);
160 return Ok(functions);
161 }
162 Ok(vec![])
163 }
164
165 pub(crate) fn find_inlined_functions<'abbrev>(
168 &'abbrev self,
169 debug_info: &'abbrev DebugInfo,
170 address: u64,
171 parent_offset: UnitOffset,
172 ) -> Result<Vec<FunctionDie<'abbrev>>, DebugError> {
173 let Ok(mut cursor) = self.unit.entries_at_offset(parent_offset) else {
176 return Ok(vec![]);
177 };
178
179 let mut abort_depth = cursor.depth();
185 let mut functions = Vec::new();
186
187 while let Ok(Some(current)) = cursor.next_dfs() {
188 if current.offset() == parent_offset {
189 continue;
191 }
192
193 if current.depth() < abort_depth {
194 break;
197 }
198
199 let Some(die) = FunctionDie::new(current.clone(), self, debug_info, address)? else {
201 continue;
202 };
203
204 abort_depth = current.depth();
207
208 functions.push(die);
209 }
210
211 Ok(functions)
212 }
213
214 #[expect(clippy::too_many_arguments)]
217 pub(crate) fn process_tree_node_attributes(
218 &self,
219 debug_info: &DebugInfo,
220 tree_node: &gimli::DebuggingInformationEntry<GimliReader>,
221 parent_variable: &mut Variable,
222 child_variable: &mut Variable,
223 memory: &mut dyn MemoryInterface,
224 cache: &mut VariableCache,
225 frame_info: StackFrameInfo<'_>,
226 ) -> Result<(), DebugError> {
227 child_variable.parent_key = parent_variable.variable_key;
229
230 let abstract_entry;
231
232 let attributes_entry = if let Some(abstract_origin) =
234 tree_node.attr(gimli::DW_AT_abstract_origin)
235 {
236 match abstract_origin.value() {
237 gimli::AttributeValue::UnitRef(unit_ref) => {
238 self.process_memory_location(
241 debug_info,
242 tree_node,
243 parent_variable,
244 child_variable,
245 memory,
246 frame_info,
247 )?;
248
249 abstract_entry = self.unit.entry(unit_ref)?;
250
251 Some(&abstract_entry)
252 }
253 other_attribute_value => {
254 child_variable.set_value(VariableValue::Error(format!(
255 "Unimplemented: Attribute Value for DW_AT_abstract_origin {other_attribute_value:?}"
256 )));
257 None
258 }
259 }
260 } else {
261 Some(tree_node)
262 };
263
264 let specification_entry;
265
266 let attributes_entry = if let Some(specification) =
269 tree_node.attr(gimli::DW_AT_specification)
270 {
271 match specification.value() {
272 gimli::AttributeValue::UnitRef(unit_ref) => {
273 self.process_memory_location(
276 debug_info,
277 tree_node,
278 parent_variable,
279 child_variable,
280 memory,
281 frame_info,
282 )?;
283
284 specification_entry = self.unit.entry(unit_ref)?;
285
286 Some(&specification_entry)
287 }
288 other_attribute_value => {
289 child_variable.set_value(VariableValue::Error(format!(
290 "Unimplemented: Attribute Value for DW_AT_specification {other_attribute_value:?}"
291 )));
292 None
293 }
294 }
295 } else {
296 attributes_entry
297 };
298
299 if let Some(entry) = attributes_entry.as_ref()
302 && let Ok(Some(name)) = extract_name(debug_info, entry)
303 {
304 child_variable.name = VariableName::Named(name);
305 }
306
307 if let Some(attributes_entry) = attributes_entry {
308 child_variable.source_location =
309 self.extract_source_location(debug_info, attributes_entry)?;
310
311 for attr in attributes_entry.attrs() {
313 match attr.name() {
314 gimli::DW_AT_location | gimli::DW_AT_data_member_location => {
315 }
318 gimli::DW_AT_name => {
319 }
321 gimli::DW_AT_decl_file | gimli::DW_AT_decl_line | gimli::DW_AT_decl_column => {
322 }
324 gimli::DW_AT_containing_type => {
325 }
327 gimli::DW_AT_type => {
328 self.process_type_attribute(
340 attr,
341 debug_info,
342 attributes_entry,
343 parent_variable,
344 child_variable,
345 memory,
346 frame_info,
347 cache,
348 )?;
349 }
350 gimli::DW_AT_enum_class => match attr.value() {
351 gimli::AttributeValue::Flag(true) => {
352 child_variable
353 .set_value(VariableValue::Valid(child_variable.type_name()));
354 }
355 gimli::AttributeValue::Flag(false) => {
356 child_variable.set_value(VariableValue::Error(
357 "Unimplemented: DW_AT_enum_class(false)".to_string(),
358 ));
359 }
360 other_attribute_value => {
361 child_variable.set_value(VariableValue::Error(format!(
362 "Unimplemented: Attribute Value for DW_AT_enum_class: {other_attribute_value:?}"
363 )));
364 }
365 },
366 gimli::DW_AT_const_value => {
367 let attr_value = attr.value();
368 let variable_value = if let Some(const_value) = attr_value.udata_value() {
369 VariableValue::Valid(const_value.to_string())
370 } else if let Some(const_value) = attr_value.sdata_value() {
371 VariableValue::Valid(const_value.to_string())
372 } else {
373 VariableValue::Error(format!(
374 "Unimplemented: Attribute Value for DW_AT_const_value: {attr_value:?}"
375 ))
376 };
377
378 child_variable.set_value(variable_value)
379 }
380 gimli::DW_AT_alignment => {
381 }
384 gimli::DW_AT_artificial => {
385 child_variable.name = VariableName::Artificial;
387 }
388 gimli::DW_AT_discr => match attr.value() {
389 gimli::AttributeValue::UnitRef(unit_ref) => {
391 let discriminant_node = self.unit.entry(unit_ref)?;
392 let mut discriminant_variable =
393 cache.create_variable(parent_variable.variable_key, Some(self))?;
394 self.process_tree_node_attributes(
395 debug_info,
396 &discriminant_node,
397 parent_variable,
398 &mut discriminant_variable,
399 memory,
400 cache,
401 frame_info,
402 )?;
403
404 let variant_part = if discriminant_variable.is_valid() {
405 discriminant_variable
406 .to_string(cache)
407 .parse()
408 .unwrap_or(u64::MAX)
409 } else {
410 u64::MAX
411 };
412
413 parent_variable.role = VariantRole::VariantPart(variant_part);
414 cache.remove_cache_entry(discriminant_variable.variable_key)?;
415 }
416 other_attribute_value => {
417 child_variable.set_value(VariableValue::Error(format!(
418 "Unimplemented: Attribute Value for DW_AT_discr {other_attribute_value:?}"
419 )));
420 }
421 },
422 gimli::DW_AT_linkage_name => {
423 let value = attr.value();
424 let raw_str = debug_info.dwarf.attr_string(&self.unit, value).ok();
425
426 let linkage_name = raw_str.and_then(|r| String::from_utf8(r.to_vec()).ok());
427
428 child_variable.linkage_name = linkage_name;
429 }
430 gimli::DW_AT_accessibility => {
431 }
434 gimli::DW_AT_external => {
435 }
437 gimli::DW_AT_declaration => {
438 }
440 gimli::DW_AT_encoding => {
441 }
443 gimli::DW_AT_discr_value => {
444 }
446 gimli::DW_AT_byte_size => {
447 }
449 gimli::DW_AT_abstract_origin => {
450 }
452 gimli::DW_AT_address_class => {
453 }
455 gimli::DW_AT_data_bit_offset
456 | gimli::DW_AT_bit_offset
457 | gimli::DW_AT_bit_size => {
458 }
460 other_attribute => {
461 tracing::info!(
462 "Unimplemented: Variable Attribute {:.100} : {:.100}, with children = {}",
463 format!("{:?}", other_attribute.static_string()),
464 format!("{:?}", attributes_entry.attr_value(other_attribute)),
465 attributes_entry.has_children()
466 );
467 }
468 }
469 }
470 }
471
472 self.process_bitfield_info(child_variable, tree_node, cache)?;
474
475 child_variable.extract_value(memory, cache);
476 cache.update_variable(child_variable)?;
477
478 Ok(())
479 }
480
481 #[expect(clippy::too_many_arguments)]
482 fn process_type_attribute(
483 &self,
484 attr: &gimli::Attribute<GimliReader>,
485 debug_info: &DebugInfo,
486 attributes_entry: &gimli::DebuggingInformationEntry<GimliReader>,
487 parent_variable: &Variable,
488 child_variable: &mut Variable,
489 memory: &mut dyn MemoryInterface,
490 frame_info: StackFrameInfo<'_>,
491 cache: &mut VariableCache,
492 ) -> Result<(), DebugError> {
493 self.process_memory_location(
497 debug_info,
498 attributes_entry,
499 parent_variable,
500 child_variable,
501 memory,
502 frame_info,
503 )?;
504
505 match debug_info.resolve_die_reference_with_unit(attr, self) {
506 Ok((unit_info, referenced_type_tree_node)) => {
507 unit_info.extract_type(
508 debug_info,
509 &referenced_type_tree_node,
510 parent_variable,
511 child_variable,
512 memory,
513 cache,
514 frame_info,
515 )?;
516 }
517 Err(error) => {
518 child_variable.set_value(VariableValue::Error(format!(
519 "Failed to process DW_AT_type: {error:?}"
520 )));
521 }
522 }
523
524 Ok(())
525 }
526
527 pub(crate) fn process_tree(
532 &self,
533 debug_info: &DebugInfo,
534 parent_node: gimli::EntriesTreeNode<GimliReader>,
535 parent_variable: &mut Variable,
536 memory: &mut dyn MemoryInterface,
537 cache: &mut VariableCache,
538 frame_info: StackFrameInfo<'_>,
539 ) -> Result<(), DebugError> {
540 if !parent_variable.is_valid() {
541 cache.update_variable(parent_variable)?;
542 return Ok(());
543 }
544
545 tracing::trace!("process_tree for parent {:?}", parent_variable.variable_key);
546
547 let mut child_nodes = parent_node.children();
548 while let Some(child_node) = child_nodes.next()? {
549 match child_node.entry().tag() {
550 gimli::DW_TAG_namespace => {
551 let variable_name =
552 if let Ok(Some(name)) = extract_name(debug_info, child_node.entry()) {
553 VariableName::Namespace(name)
554 } else {
555 VariableName::AnonymousNamespace
556 };
557
558 let mut namespace_variable = if let Some(existing_var) = cache
560 .get_variable_by_name_and_parent(
561 &variable_name,
562 parent_variable.variable_key,
563 ) {
564 existing_var
565 } else {
566 let mut namespace_variable = Variable::new(Some(self));
567
568 namespace_variable.name = variable_name;
569 namespace_variable.type_name = VariableType::Namespace;
570 namespace_variable.memory_location = VariableLocation::Unavailable;
571 cache
572 .add_variable(parent_variable.variable_key, &mut namespace_variable)?;
573
574 namespace_variable
575 };
576
577 self.process_tree(
579 debug_info,
580 child_node,
581 &mut namespace_variable,
582 memory,
583 cache,
584 frame_info,
585 )?;
586
587 if !cache.has_children(&namespace_variable) {
589 cache.remove_cache_entry(namespace_variable.variable_key)?;
590 }
591 }
592
593 gimli::DW_TAG_formal_parameter | gimli::DW_TAG_variable | gimli::DW_TAG_member => {
594 let mut child_variable =
600 cache.create_variable(parent_variable.variable_key, Some(self))?;
601 self.process_tree_node_attributes(
602 debug_info,
603 child_node.entry(),
604 parent_variable,
605 &mut child_variable,
606 memory,
607 cache,
608 frame_info,
609 )?;
610
611 let is_declaration = if let Some(AttributeValue::Flag(value)) =
614 child_node.entry().attr_value(gimli::DW_AT_declaration)
615 {
616 value
617 } else {
618 false
619 };
620
621 if is_declaration
623 || child_variable.type_name.is_phantom_data()
624 || child_variable.name == VariableName::Artificial
625 {
626 cache.remove_cache_entry(child_variable.variable_key)?;
627 } else if child_variable.is_valid() {
628 self.process_tree(
630 debug_info,
631 child_node,
632 &mut child_variable,
633 memory,
634 cache,
635 frame_info,
636 )?;
637 }
638 }
639 gimli::DW_TAG_variant_part => {
640 let mut child_variable =
654 cache.create_variable(parent_variable.variable_key, Some(self))?;
655 parent_variable.role = VariantRole::VariantPart(u64::MAX);
666 self.process_tree_node_attributes(
667 debug_info,
668 child_node.entry(),
669 parent_variable,
670 &mut child_variable,
671 memory,
672 cache,
673 frame_info,
674 )?;
675 cache.remove_cache_entry(child_variable.variable_key)?;
678 self.process_tree(
679 debug_info,
680 child_node,
681 parent_variable,
682 memory,
683 cache,
684 frame_info,
685 )?;
686 }
687
688 gimli::DW_TAG_variant => {
691 if !cache.has_children(parent_variable) {
693 let mut child_variable =
694 cache.create_variable(parent_variable.variable_key, Some(self))?;
695 self.extract_variant_discriminant(&child_node, &mut child_variable)?;
696 self.process_tree_node_attributes(
697 debug_info,
698 child_node.entry(),
699 parent_variable,
700 &mut child_variable,
701 memory,
702 cache,
703 frame_info,
704 )?;
705 if child_variable.is_valid() {
706 if let VariantRole::Variant(discriminant) = child_variable.role {
707 if parent_variable.role == VariantRole::VariantPart(discriminant)
709 || discriminant == u64::MAX
710 {
711 self.process_memory_location(
712 debug_info,
713 child_node.entry(),
714 parent_variable,
715 &mut child_variable,
716 memory,
717 frame_info,
718 )?;
719 self.process_tree(
721 debug_info,
722 child_node,
723 &mut child_variable,
724 memory,
725 cache,
726 frame_info,
727 )?;
728 if child_variable.is_valid() {
729 cache.adopt_grand_children(
731 parent_variable,
732 &child_variable,
733 )?;
734 }
735 } else {
736 cache.remove_cache_entry(child_variable.variable_key)?;
737 }
738 }
739 } else {
740 cache.remove_cache_entry(child_variable.variable_key)?;
741 }
742 }
743 }
744 gimli::DW_TAG_lexical_block => {
745 let Some(program_counter) = frame_info
746 .registers
747 .get_program_counter()
748 .and_then(|reg| reg.value)
749 else {
750 return Err(DebugError::WarnAndContinue {
751 message:
752 "Cannot unwind `Variable` without a valid PC (program_counter)"
753 .to_string(),
754 });
755 };
756 let program_counter = program_counter.try_into()?;
757
758 let mut in_scope = false;
761 if let Some(low_pc_attr) = child_node.entry().attr(gimli::DW_AT_low_pc) {
762 let low_pc = match low_pc_attr.value() {
763 gimli::AttributeValue::Addr(value) => value,
764 _other => u64::MAX,
765 };
766 let high_pc = if let Some(high_pc_attr) =
767 child_node.entry().attr(gimli::DW_AT_high_pc)
768 {
769 match high_pc_attr.value() {
770 gimli::AttributeValue::Addr(addr) => addr,
771 gimli::AttributeValue::Udata(unsigned_offset) => {
772 low_pc + unsigned_offset
773 }
774 _other => 0_u64,
775 }
776 } else {
777 0_u64
778 };
779 if low_pc == u64::MAX || high_pc == 0_u64 {
780 parent_variable.set_value(VariableValue::Error("Error: Processing of variables failed because of invalid/unsupported scope information. Please log a bug at 'https://github.com/probe-rs/probe-rs/issues'".to_string()));
782 }
783 let block_range = gimli::Range {
784 begin: low_pc,
785 end: high_pc,
786 };
787 if block_range.contains(program_counter) {
788 in_scope = true;
790 }
791 };
793 if !in_scope && let Some(ranges) = child_node.entry().attr(gimli::DW_AT_ranges)
795 {
796 match ranges.value() {
797 gimli::AttributeValue::RangeListsRef(raw_range_lists_offset) => {
798 let range_lists_offset = debug_info
799 .dwarf
800 .ranges_offset_from_raw(&self.unit, raw_range_lists_offset);
801
802 if let Ok(mut range_iter) =
803 debug_info.dwarf.ranges(&self.unit, range_lists_offset)
804 {
805 in_scope = range_iter.contains(program_counter);
806 }
807 }
808 other_range_attribute => {
809 let error = format!(
810 "Found unexpected scope attribute: {:?} for variable {:?}",
811 other_range_attribute, parent_variable.name
812 );
813 parent_variable.set_value(VariableValue::Error(error));
814 }
815 }
816 }
817 if in_scope {
818 self.process_tree(
822 debug_info,
823 child_node,
824 parent_variable,
825 memory,
826 cache,
827 frame_info,
828 )?;
829 } else {
830 }
833 }
834 gimli::DW_TAG_template_type_parameter => {
835 }
843
844 gimli::DW_TAG_inlined_subroutine
846 | gimli::DW_TAG_base_type
847 | gimli::DW_TAG_pointer_type
848 | gimli::DW_TAG_structure_type
849 | gimli::DW_TAG_enumeration_type
850 | gimli::DW_TAG_array_type
851 | gimli::DW_TAG_subroutine_type
852 | gimli::DW_TAG_subprogram
853 | gimli::DW_TAG_union_type
854 | gimli::DW_TAG_typedef
855 | gimli::DW_TAG_const_type
856 | gimli::DW_TAG_volatile_type => {
857 }
860 unimplemented => {
861 tracing::debug!(
862 "Unimplemented: Encountered unimplemented DwTag {:?} for Variable {:?}",
863 unimplemented.static_string(),
864 parent_variable.name
865 )
866 }
867 }
868 }
869
870 parent_variable.extract_value(memory, cache);
871 cache.update_variable(parent_variable)?;
872
873 Ok(())
874 }
875
876 fn extract_array_range(
888 &self,
889 array_parent_node: UnitOffset,
890 ) -> Result<Vec<Range<u64>>, DebugError> {
891 let mut tree = self.unit.entries_tree(Some(array_parent_node))?;
892
893 let root = tree.root()?;
894
895 let mut children = root.children();
896
897 let mut ranges = vec![];
898 while let Some(child) = children.next()? {
899 match child.entry().tag() {
900 gimli::DW_TAG_subrange_type => {
901 if let Some(range) = self.extract_array_range_attribute(child.entry())? {
902 ranges.push(range);
903 }
904 }
905 other => tracing::debug!(
906 "Ignoring unexpected child tag {} while extracting array range",
907 other
908 ),
909 }
910 }
911
912 Ok(ranges)
913 }
914
915 fn extract_array_range_attribute(
919 &self,
920 entry: &gimli::DebuggingInformationEntry<GimliReader>,
921 ) -> Result<Option<Range<u64>>, DebugError> {
922 let mut lower_bound = None;
923 let mut upper_bound = None;
924
925 for attr in entry.attrs() {
927 match attr.name() {
928 gimli::DW_AT_lower_bound => match attr.value().udata_value() {
930 Some(bound) => lower_bound = Some(bound),
931 None => {
932 return Err(DebugError::Other(format!(
933 "Unimplemented: Attribute Value for DW_AT_lower_bound: {:?}",
934 attr.value()
935 )));
936 }
937 },
938 gimli::DW_AT_count => match attr.value().udata_value() {
939 Some(count) => upper_bound = Some(count),
940 None => {
941 return Err(DebugError::Other(format!(
942 "Unimplemented: Attribute Value for DW_AT_count: {:?}",
943 attr.value()
944 )));
945 }
946 },
947 gimli::DW_AT_upper_bound => {
948 match attr.value().udata_value() {
949 Some(bound) => upper_bound = Some(bound + 1),
951 None => {
952 return Err(DebugError::Other(format!(
953 "Unimplemented: Attribute Value for DW_AT_upper_bound: {:?}",
954 attr.value()
955 )));
956 }
957 }
958 }
959 gimli::DW_AT_type => (),
962 other_attribute => {
963 tracing::debug!(
964 "Unimplemented: Ignoring attribute {} while extracting array range",
965 other_attribute,
966 );
967 }
968 }
969 }
970
971 if let Some(upper_bound) = upper_bound {
972 Ok(Some(lower_bound.unwrap_or_default()..upper_bound))
973 } else {
974 Ok(None)
975 }
976 }
977
978 pub(crate) fn extract_variant_discriminant(
981 &self,
982 node: &gimli::EntriesTreeNode<GimliReader>,
983 variable: &mut Variable,
984 ) -> Result<(), DebugError> {
985 variable.role = match node.entry().attr(gimli::DW_AT_discr_value) {
986 Some(discr_value_attr) => {
987 let attr_value = discr_value_attr.value();
988 let variant = if let Some(const_value) = attr_value.udata_value() {
989 const_value
990 } else {
991 variable.set_value(VariableValue::Error(format!(
992 "Unimplemented: Attribute Value for DW_AT_discr_value: {:.100}",
993 format!("{attr_value:?}")
994 )));
995 u64::MAX
996 };
997
998 VariantRole::Variant(variant)
999 }
1000 None => {
1001 VariantRole::Variant(u64::MAX)
1004 }
1005 };
1006
1007 Ok(())
1008 }
1009
1010 #[expect(clippy::too_many_arguments)]
1018 fn extract_type(
1019 &self,
1020 debug_info: &DebugInfo,
1021 node: &gimli::DebuggingInformationEntry<GimliReader>,
1022 parent_variable: &Variable,
1023 child_variable: &mut Variable,
1024 memory: &mut dyn MemoryInterface,
1025 cache: &mut VariableCache,
1026 frame_info: StackFrameInfo<'_>,
1027 ) -> Result<(), DebugError> {
1028 let type_name = match self.extract_type_name(debug_info, node) {
1029 Ok(name) => name,
1030 Err(error) => {
1031 let message = format!("Error: evaluating type name: {error:?}");
1032 child_variable.set_value(VariableValue::Error(message.clone()));
1033 Some(message)
1034 }
1035 };
1036
1037 if !child_variable.is_valid() {
1038 cache.update_variable(child_variable)?;
1039
1040 return Ok(());
1041 }
1042 child_variable.type_node_offset = Some(node.offset());
1043
1044 match node.tag() {
1045 gimli::DW_TAG_base_type => {
1046 child_variable.type_name = VariableType::Base(
1047 type_name.unwrap_or_else(|| "<unnamed base type>".to_string()),
1048 );
1049 self.process_memory_location(
1050 debug_info,
1051 node,
1052 parent_variable,
1053 child_variable,
1054 memory,
1055 frame_info,
1056 )?;
1057 }
1058 gimli::DW_TAG_pointer_type => {
1059 child_variable.type_name = VariableType::Pointer(type_name);
1060 self.process_memory_location(
1061 debug_info,
1062 node,
1063 parent_variable,
1064 child_variable,
1065 memory,
1066 frame_info,
1067 )?;
1068
1069 match node.attr_value(gimli::DW_AT_type) {
1071 Some(gimli::AttributeValue::UnitRef(unit_ref)) => {
1072 if !cache.has_children(child_variable) {
1074 let mut referenced_variable =
1075 cache.create_variable(child_variable.variable_key, Some(self))?;
1076
1077 referenced_variable.name = match &child_variable.name {
1079 VariableName::Named(name) if name.starts_with("Some ") => {
1080 VariableName::Named(name.replacen('&', "*", 1))
1081 }
1082 VariableName::Named(name) => {
1083 VariableName::Named(format!("*{name}"))
1084 }
1085 other => VariableName::Named(format!(
1086 "Error: Unable to generate name, parent variable does not have a name but is special variable {other:?}"
1087 )),
1088 };
1089
1090 let referenced_node = self.unit.entry(unit_ref)?;
1091
1092 self.extract_type(
1093 debug_info,
1094 &referenced_node,
1095 child_variable,
1096 &mut referenced_variable,
1097 memory,
1098 cache,
1099 frame_info,
1100 )?;
1101
1102 if matches!(referenced_variable.type_name.inner(), VariableType::Base(name) if name == "()")
1103 {
1104 cache.remove_cache_entry(referenced_variable.variable_key)?;
1106 }
1107 }
1108 }
1109 Some(other_attribute_value) => {
1110 child_variable.set_value(VariableValue::Error(format!(
1111 "Unimplemented: Attribute Value for DW_AT_type {:.100}",
1112 format!("{other_attribute_value:?}")
1113 )));
1114 }
1115 None => {
1116 child_variable.set_value(
1121 self.language.process_tag_with_no_type(
1122 child_variable,
1123 gimli::DW_TAG_pointer_type,
1124 ),
1125 );
1126 }
1127 }
1128 }
1129 gimli::DW_TAG_structure_type => {
1130 self.extract_struct(
1131 type_name,
1132 debug_info,
1133 node,
1134 parent_variable,
1135 child_variable,
1136 memory,
1137 cache,
1138 frame_info,
1139 )?;
1140 }
1141 gimli::DW_TAG_enumeration_type => {
1142 self.extract_enumeration_type(
1143 child_variable,
1144 type_name,
1145 debug_info,
1146 node,
1147 parent_variable,
1148 memory,
1149 frame_info,
1150 )?;
1151 }
1152 gimli::DW_TAG_array_type => {
1153 self.extract_array_type(
1154 node,
1155 debug_info,
1156 parent_variable,
1157 child_variable,
1158 memory,
1159 frame_info,
1160 cache,
1161 )?;
1162 }
1163 gimli::DW_TAG_union_type => {
1164 child_variable.type_name =
1165 VariableType::Base(type_name.unwrap_or_else(|| "<unnamed union>".to_string()));
1166 self.process_memory_location(
1167 debug_info,
1168 node,
1169 parent_variable,
1170 child_variable,
1171 memory,
1172 frame_info,
1173 )?;
1174
1175 let mut tree = self.unit.entries_tree(Some(node.offset()))?;
1176
1177 self.process_tree(
1179 debug_info,
1180 tree.root()?,
1181 child_variable,
1182 memory,
1183 cache,
1184 frame_info,
1185 )?;
1186 if child_variable.is_valid() && !cache.has_children(child_variable) {
1187 child_variable.set_value(VariableValue::Valid(child_variable.type_name()));
1189 }
1190 }
1191 gimli::DW_TAG_subroutine_type => {
1192 match node.attr(gimli::DW_AT_type) {
1195 Some(data_type_attribute) => match data_type_attribute.value() {
1196 gimli::AttributeValue::UnitRef(unit_ref) => {
1197 let subroutine_type_node =
1198 self.unit.header.entry(&self.unit.abbreviations, unit_ref)?;
1199
1200 child_variable.type_name =
1201 match extract_name(debug_info, &subroutine_type_node) {
1202 Ok(Some(name_attr)) => VariableType::Other(name_attr),
1203 Ok(None) => VariableType::Unknown,
1204 Err(error) => VariableType::Other(format!(
1205 "Error: evaluating subroutine type name: {error:?} "
1206 )),
1207 };
1208 }
1209 other_attribute_value => {
1210 child_variable.set_value(VariableValue::Error(format!(
1211 "Unimplemented: Attribute Value for DW_AT_type {:.100}",
1212 format!("{other_attribute_value:?}")
1213 )));
1214 }
1215 },
1216
1217 None => {
1218 child_variable
1220 .set_value(VariableValue::Valid("<No Return Value>".to_string()));
1221 child_variable.type_name = VariableType::Unknown;
1222 }
1223 }
1224 }
1225 other @ (gimli::DW_TAG_typedef
1226 | gimli::DW_TAG_const_type
1227 | gimli::DW_TAG_volatile_type
1228 | gimli::DW_TAG_restrict_type
1229 | gimli::DW_TAG_atomic_type) => match node.attr(gimli::DW_AT_type) {
1230 Some(attr) => {
1231 self.process_type_attribute(
1232 attr,
1233 debug_info,
1234 node,
1235 parent_variable,
1236 child_variable,
1237 memory,
1238 frame_info,
1239 cache,
1240 )?;
1241
1242 let modifier = match other {
1243 gimli::DW_TAG_typedef => {
1244 if child_variable.variable_node_type.is_deferred() {
1245 child_variable.value = VariableValue::Empty;
1248 }
1249 Modifier::Typedef(
1250 type_name.unwrap_or_else(|| "<unnamed typedef>".to_string()),
1251 )
1252 }
1253 gimli::DW_TAG_const_type => Modifier::Const,
1254 gimli::DW_TAG_volatile_type => Modifier::Volatile,
1255 gimli::DW_TAG_restrict_type => Modifier::Restrict,
1256 gimli::DW_TAG_atomic_type => Modifier::Atomic,
1257 _ => unreachable!(),
1258 };
1259
1260 child_variable.type_name = VariableType::Modified(
1261 modifier,
1262 Box::new(std::mem::replace(
1263 &mut child_variable.type_name,
1264 VariableType::Unknown,
1265 )),
1266 );
1267 }
1268
1269 None => child_variable.set_value(
1270 self.language
1271 .process_tag_with_no_type(child_variable, other),
1272 ),
1273 },
1274
1275 other => {
1277 child_variable.set_value(VariableValue::Error(format!(
1278 "<unimplemented: type: {other}>"
1279 )));
1280 child_variable.type_name = VariableType::Other("unimplemented".to_string());
1281 cache.remove_cache_entry_children(child_variable.variable_key)?;
1282 }
1283 }
1284
1285 child_variable.extract_value(memory, cache);
1286 cache.update_variable(child_variable)?;
1287
1288 Ok(())
1289 }
1290
1291 #[expect(clippy::too_many_arguments)]
1292 fn extract_struct(
1293 &self,
1294 type_name: Option<String>,
1295 debug_info: &DebugInfo,
1296 node: &DebuggingInformationEntry<GimliReader>,
1297 parent_variable: &Variable,
1298 child_variable: &mut Variable,
1299 memory: &mut dyn MemoryInterface,
1300 cache: &mut VariableCache,
1301 frame_info: StackFrameInfo<'_>,
1302 ) -> Result<(), DebugError> {
1303 let type_name = type_name.unwrap_or_else(|| "<unnamed struct>".to_string());
1304 child_variable.type_name = VariableType::Struct(type_name.clone());
1305 self.process_memory_location(
1306 debug_info,
1307 node,
1308 parent_variable,
1309 child_variable,
1310 memory,
1311 frame_info,
1312 )?;
1313
1314 if child_variable.memory_location != VariableLocation::Unavailable {
1315 child_variable.variable_node_type =
1317 VariableNodeType::TypeOffset(self.debug_info_offset()?, node.offset());
1318 if self.language.auto_resolve_children(&type_name) {
1321 let temp_node_type = std::mem::replace(
1322 &mut child_variable.variable_node_type,
1323 VariableNodeType::RecurseToBaseType,
1324 );
1325
1326 let mut tree = self.unit.entries_tree(Some(node.offset()))?;
1327
1328 self.process_tree(
1329 debug_info,
1330 tree.root()?,
1331 child_variable,
1332 memory,
1333 cache,
1334 frame_info,
1335 )?;
1336 child_variable.variable_node_type = temp_node_type;
1337 }
1338 } else {
1339 child_variable.variable_node_type = VariableNodeType::DoNotRecurse;
1341 }
1342
1343 self.language.process_struct(
1344 self,
1345 debug_info,
1346 node,
1347 child_variable,
1348 memory,
1349 cache,
1350 frame_info,
1351 )
1352 }
1353
1354 #[expect(clippy::too_many_arguments)]
1355 fn extract_array_type(
1356 &self,
1357 node: &DebuggingInformationEntry<GimliReader>,
1358 debug_info: &DebugInfo,
1359 parent_variable: &Variable,
1360 child_variable: &mut Variable,
1361 memory: &mut dyn MemoryInterface,
1362 frame_info: StackFrameInfo,
1363 cache: &mut VariableCache,
1364 ) -> Result<(), DebugError> {
1365 let subranges = match self.extract_array_range(node.offset()) {
1366 Ok(subranges) => subranges,
1367 Err(error) => {
1368 child_variable.set_value(VariableValue::Error(format!(
1369 "Error: Failed to extract array range: {error:?}"
1370 )));
1371 return Ok(());
1372 }
1373 };
1374
1375 match node.attr_value(gimli::DW_AT_type) {
1376 Some(gimli::AttributeValue::UnitRef(unit_ref)) => {
1377 self.process_memory_location(
1379 debug_info,
1380 node,
1381 parent_variable,
1382 child_variable,
1383 memory,
1384 frame_info,
1385 )?;
1386
1387 if let Ok(array_member_type_node) = self.unit.entry(unit_ref) {
1389 self.expand_array_members(
1393 debug_info,
1394 &array_member_type_node,
1395 cache,
1396 child_variable,
1397 memory,
1398 &subranges,
1399 frame_info,
1400 )?;
1401 };
1402 }
1403 Some(other_attribute_value) => {
1404 child_variable.set_value(VariableValue::Error(format!(
1405 "Unimplemented: Attribute Value for DW_AT_type {other_attribute_value:?}"
1406 )));
1407 }
1408 None => {
1409 child_variable.set_value(
1410 self.language
1411 .process_tag_with_no_type(child_variable, gimli::DW_TAG_array_type),
1412 );
1413 }
1414 }
1415
1416 Ok(())
1417 }
1418
1419 #[expect(clippy::too_many_arguments)]
1420 fn extract_enumeration_type(
1421 &self,
1422 child_variable: &mut Variable,
1423 type_name: Option<String>,
1424 debug_info: &DebugInfo,
1425 node: &DebuggingInformationEntry<GimliReader>,
1426 parent_variable: &Variable,
1427 memory: &mut dyn MemoryInterface,
1428 frame_info: StackFrameInfo,
1429 ) -> Result<(), DebugError> {
1430 child_variable.type_name =
1431 VariableType::Enum(type_name.unwrap_or_else(|| "<unnamed enum>".to_string()));
1432
1433 self.process_memory_location(
1434 debug_info,
1435 node,
1436 parent_variable,
1437 child_variable,
1438 memory,
1439 frame_info,
1440 )?;
1441
1442 let mut tree = self.unit.entries_tree(Some(node.offset()))?;
1443 let enumerator_values = self.process_enumerator(debug_info, tree.root()?)?;
1444
1445 if !(parent_variable.is_valid() && child_variable.is_valid()) {
1446 return Ok(());
1447 }
1448
1449 let value = if let VariableLocation::Address(address) = child_variable.memory_location {
1450 let mut buff = 0u8;
1452 memory.read(address, std::slice::from_mut(&mut buff))?;
1453 let this_enum_const_value = buff.to_string();
1454
1455 let enumerator_value = match enumerator_values
1456 .iter()
1457 .find(|(_name, value)| value.to_string() == this_enum_const_value)
1458 {
1459 Some((name, _value)) => name,
1460 None => &VariableName::Named("<Error: Unresolved enum value>".to_string()),
1461 };
1462
1463 self.language
1464 .format_enum_value(&child_variable.type_name, enumerator_value)
1465 } else {
1466 VariableValue::Error(format!(
1467 "Unsupported variable location {:?}",
1468 child_variable.memory_location
1469 ))
1470 };
1471
1472 child_variable.set_value(value);
1473
1474 Ok(())
1475 }
1476
1477 fn process_enumerator(
1482 &self,
1483 debug_info: &DebugInfo,
1484 parent_node: gimli::EntriesTreeNode<GimliReader>,
1485 ) -> Result<Vec<(VariableName, VariableValue)>, DebugError> {
1486 let mut enumerator_values = Vec::new();
1487
1488 let mut child_nodes = parent_node.children();
1489 while let Some(child_node) = child_nodes.next()? {
1490 match child_node.entry().tag() {
1491 gimli::DW_TAG_enumerator => {
1492 let attributes_entry = child_node.entry();
1493
1494 let name_result = extract_name(debug_info, attributes_entry);
1495
1496 let Some(attr_value) = attributes_entry.attr_value(gimli::DW_AT_const_value)
1497 else {
1498 continue;
1500 };
1501 let variable_value = if let Some(const_value) = attr_value.udata_value() {
1502 VariableValue::Valid(const_value.to_string())
1503 } else if let Some(const_value) = attr_value.sdata_value() {
1504 VariableValue::Valid(const_value.to_string())
1505 } else {
1506 VariableValue::Error(format!(
1507 "Unimplemented: Attribute Value for DW_AT_const_value: {attr_value:?}"
1508 ))
1509 };
1510
1511 let enumerator_name = if let Ok(Some(ref name)) = name_result {
1512 name.to_string()
1513 } else {
1514 tracing::warn!("Enumerator has no name");
1515
1516 format!("<unknown enumerator {}", enumerator_values.len())
1517 };
1518
1519 enumerator_values.push((VariableName::Named(enumerator_name), variable_value))
1520 }
1521 gimli::DW_TAG_subprogram => (),
1523 other => {
1524 tracing::debug!("Ignoring tag {other} under DW_TAG_enumeration_type");
1525 }
1526 }
1527 }
1528
1529 Ok(enumerator_values)
1530 }
1531
1532 #[expect(clippy::too_many_arguments)]
1534 pub(crate) fn expand_array_members(
1535 &self,
1536 debug_info: &DebugInfo,
1537 array_member_type_node: &DebuggingInformationEntry<GimliReader>,
1538 cache: &mut VariableCache,
1539 array_variable: &mut Variable,
1540 memory: &mut dyn MemoryInterface,
1541 subranges: &[Range<u64>],
1542 frame_info: StackFrameInfo<'_>,
1543 ) -> Result<(), DebugError> {
1544 let Some((current_range, remaining_ranges)) = subranges.split_first() else {
1545 array_variable.set_value(VariableValue::Error(
1546 "Error processing range for array, unexpected empty range. \
1547 This is a known issue, see https://github.com/probe-rs/probe-rs/issues/2687"
1548 .to_string(),
1549 ));
1550 return Ok(());
1551 };
1552
1553 let explode_range = if current_range.is_empty() {
1555 0..1
1556 } else {
1557 current_range.clone()
1558 };
1559
1560 for member_index in explode_range.clone() {
1561 let mut array_member_variable =
1562 cache.create_variable(array_variable.variable_key, Some(self))?;
1563 array_member_variable.name = VariableName::Indexed(member_index);
1564 array_member_variable.source_location = array_variable.source_location.clone();
1565
1566 self.process_memory_location(
1573 debug_info,
1574 array_member_type_node,
1575 array_variable,
1576 &mut array_member_variable,
1577 memory,
1578 frame_info,
1579 )?;
1580
1581 if !remaining_ranges.is_empty() {
1582 self.expand_array_members(
1585 debug_info,
1586 array_member_type_node,
1587 cache,
1588 &mut array_member_variable,
1589 memory,
1590 remaining_ranges,
1591 frame_info,
1592 )?;
1593 } else {
1594 self.extract_type(
1595 debug_info,
1596 array_member_type_node,
1597 array_variable,
1598 &mut array_member_variable,
1599 memory,
1600 cache,
1601 frame_info,
1602 )?;
1603 }
1604
1605 if member_index == explode_range.start {
1606 let item_count = current_range.clone().count();
1607
1608 array_variable.type_name = VariableType::Array {
1609 count: item_count,
1610 item_type_name: Box::new(array_member_variable.type_name.clone()),
1611 };
1612 if let Some(item_byte_size) = array_member_variable.byte_size {
1613 array_variable.byte_size = Some(item_byte_size * item_count as u64);
1614 }
1615 }
1616
1617 array_member_variable.extract_value(memory, cache);
1618 cache.update_variable(&array_member_variable)?;
1619 }
1620
1621 if current_range.is_empty() {
1624 cache.remove_cache_entry_children(array_variable.variable_key)?;
1625 }
1626
1627 Ok(())
1628 }
1629
1630 pub(crate) fn process_memory_location(
1632 &self,
1633 debug_info: &DebugInfo,
1634 node_die: &gimli::DebuggingInformationEntry<GimliReader>,
1635 parent_variable: &Variable,
1636 child_variable: &mut Variable,
1637 memory: &mut dyn MemoryInterface,
1638 frame_info: StackFrameInfo<'_>,
1639 ) -> Result<(), DebugError> {
1640 child_variable.byte_size = child_variable
1643 .byte_size
1644 .or_else(|| extract_byte_size(node_die))
1645 .or_else(|| {
1646 if let VariableType::Array { count, .. } = parent_variable.type_name {
1647 parent_variable
1648 .byte_size
1649 .map(|byte_size| byte_size.checked_div(count as u64).unwrap_or(byte_size))
1650 } else {
1651 None
1652 }
1653 });
1654
1655 if child_variable.memory_location == VariableLocation::Unknown {
1656 let expression_result = match self.extract_location(
1658 debug_info,
1659 node_die,
1660 &parent_variable.memory_location,
1661 memory,
1662 frame_info,
1663 ) {
1664 Ok(expr) => expr,
1665 Err(debug_error) => {
1666 child_variable.memory_location = VariableLocation::Error(
1668 "Unsupported location expression while resolving the location. Please reduce optimization levels in your build profile.".to_string()
1669 );
1670 let variable_name = &child_variable.name;
1671 tracing::debug!(
1672 "Encountered an unsupported location expression while resolving the location for variable {variable_name:?}: {debug_error:?}. Please reduce optimization levels in your build profile."
1673 );
1674 return Ok(());
1675 }
1676 };
1677
1678 match expression_result {
1679 ExpressionResult::Value(value_from_expression @ VariableValue::Valid(_)) => {
1680 child_variable.memory_location = VariableLocation::Value;
1682 child_variable.set_value(value_from_expression);
1683 }
1684 ExpressionResult::Value(value_from_expression) => {
1685 child_variable.set_value(value_from_expression);
1686 }
1687 ExpressionResult::Location(VariableLocation::Unavailable) => {
1688 child_variable.set_value(VariableValue::Error(
1689 "<value optimized away by compiler, out of scope, or dropped>".to_string(),
1690 ));
1691 }
1692 ExpressionResult::Location(
1693 ref location @ VariableLocation::Error(ref error_message)
1694 | ref location @ VariableLocation::Unsupported(ref error_message),
1695 ) => {
1696 child_variable.set_value(VariableValue::Error(error_message.clone()));
1697 child_variable.memory_location = location.clone();
1698 }
1699 ExpressionResult::Location(location_from_expression) => {
1700 child_variable.memory_location = location_from_expression;
1701 }
1702 }
1703 }
1704
1705 self.handle_memory_location_special_cases(
1706 node_die.offset(),
1707 child_variable,
1708 parent_variable,
1709 memory,
1710 );
1711
1712 Ok(())
1713 }
1714
1715 pub(crate) fn extract_location(
1722 &self,
1723 debug_info: &DebugInfo,
1724 node_die: &gimli::DebuggingInformationEntry<GimliReader>,
1725 parent_location: &VariableLocation,
1726 memory: &mut dyn MemoryInterface,
1727 frame_info: StackFrameInfo<'_>,
1728 ) -> Result<ExpressionResult, DebugError> {
1729 trait ResultExt {
1730 fn convert_incomplete(self) -> Result<ExpressionResult, DebugError>;
1732 }
1733
1734 impl ResultExt for Result<ExpressionResult, DebugError> {
1735 fn convert_incomplete(self) -> Result<ExpressionResult, DebugError> {
1736 match self {
1737 Ok(result) => Ok(result),
1738 Err(DebugError::WarnAndContinue { message }) => {
1739 tracing::warn!("UnwindIncompleteResults: {:?}", message);
1740 Ok(ExpressionResult::Location(VariableLocation::Unavailable))
1741 }
1742 e => e,
1743 }
1744 }
1745 }
1746
1747 for attr in node_die.attrs() {
1748 let result = match attr.name() {
1749 gimli::DW_AT_location
1750 | gimli::DW_AT_frame_base
1751 | gimli::DW_AT_data_member_location => match attr.value() {
1752 gimli::AttributeValue::Exprloc(expression) => self
1753 .evaluate_expression(memory, expression, frame_info)
1754 .convert_incomplete()?,
1755
1756 gimli::AttributeValue::Udata(offset_from_location) => {
1757 let location = if let VariableLocation::Address(address) = parent_location {
1758 let Some(location) = address.checked_add(offset_from_location) else {
1759 return Err(DebugError::WarnAndContinue {
1760 message: "Overflow calculating variable address".to_string(),
1761 });
1762 };
1763
1764 VariableLocation::Address(location)
1765 } else {
1766 parent_location.clone()
1767 };
1768
1769 ExpressionResult::Location(location)
1770 }
1771
1772 gimli::AttributeValue::LocationListsRef(location_list_offset) => self
1773 .evaluate_location_list_ref(
1774 debug_info,
1775 location_list_offset,
1776 frame_info,
1777 memory,
1778 )
1779 .convert_incomplete()?,
1780
1781 other_attribute_value => {
1782 ExpressionResult::Location(VariableLocation::Unsupported(format!(
1783 "Unimplemented: extract_location() Could not extract location from: {:.100}",
1784 format!("{other_attribute_value:?}")
1785 )))
1786 }
1787 },
1788
1789 gimli::DW_AT_address_class => {
1790 let location = match attr.value() {
1791 gimli::AttributeValue::AddressClass(gimli::DwAddr(0)) => {
1792 parent_location.clone()
1794 }
1795 gimli::AttributeValue::AddressClass(address_class) => {
1796 VariableLocation::Unsupported(format!(
1797 "Unimplemented: extract_location() found unsupported DW_AT_address_class(gimli::DwAddr({address_class:?}))"
1798 ))
1799 }
1800 other_attribute_value => VariableLocation::Unsupported(format!(
1801 "Unimplemented: extract_location() found invalid DW_AT_address_class: {:.100}",
1802 format!("{other_attribute_value:?}")
1803 )),
1804 };
1805
1806 ExpressionResult::Location(location)
1807 }
1808
1809 _other_attributes => {
1810 continue;
1812 }
1813 };
1814
1815 return Ok(result);
1816 }
1817
1818 Ok(ExpressionResult::Location(VariableLocation::Unknown))
1820 }
1821
1822 fn evaluate_location_list_ref(
1823 &self,
1824 debug_info: &DebugInfo,
1825 location_list_offset: gimli::LocationListsOffset,
1826 frame_info: StackFrameInfo<'_>,
1827 memory: &mut dyn MemoryInterface,
1828 ) -> Result<ExpressionResult, DebugError> {
1829 let mut locations = match debug_info.locations_section.locations(
1830 location_list_offset,
1831 self.unit.header.encoding(),
1832 self.unit.low_pc,
1833 &debug_info.address_section,
1834 self.unit.addr_base,
1835 ) {
1836 Ok(locations) => locations,
1837 Err(error) => {
1838 return Ok(ExpressionResult::Location(VariableLocation::Error(
1839 format!("Error: Resolving variable Location: {error:?}"),
1840 )));
1841 }
1842 };
1843 let Some(program_counter) = frame_info
1844 .registers
1845 .get_program_counter()
1846 .and_then(|reg| reg.value)
1847 else {
1848 return Ok(ExpressionResult::Location(VariableLocation::Error(
1849 "Cannot determine variable location without a valid program counter.".to_string(),
1850 )));
1851 };
1852
1853 let mut expression = None;
1854 'find_range: loop {
1855 let location = match locations.next() {
1856 Ok(Some(location_lists_entry)) => location_lists_entry,
1857 Ok(None) => break 'find_range,
1858 Err(error) => {
1859 return Ok(ExpressionResult::Location(VariableLocation::Error(
1860 format!("Error while iterating LocationLists for this variable: {error:?}"),
1861 )));
1862 }
1863 };
1864
1865 if let Ok(program_counter) = program_counter.try_into()
1866 && location.range.contains(program_counter)
1867 {
1868 expression = Some(location.data);
1869 break 'find_range;
1870 }
1871 }
1872
1873 let Some(valid_expression) = expression else {
1874 return Ok(ExpressionResult::Location(VariableLocation::Unavailable));
1875 };
1876
1877 self.evaluate_expression(memory, valid_expression, frame_info)
1878 }
1879
1880 pub(crate) fn evaluate_expression(
1886 &self,
1887 memory: &mut dyn MemoryInterface,
1888 expression: gimli::Expression<GimliReader>,
1889 frame_info: StackFrameInfo<'_>,
1890 ) -> Result<ExpressionResult, DebugError> {
1891 fn evaluate_address(address: u64, memory: &mut dyn MemoryInterface) -> ExpressionResult {
1892 let location = if address >= u32::MAX as u64 && !memory.supports_native_64bit_access() {
1893 VariableLocation::Error(format!(
1894 "The memory location for this variable value ({address:#010X}) is invalid. Please report this as a bug."
1895 ))
1896 } else {
1897 VariableLocation::Address(address)
1898 };
1899
1900 ExpressionResult::Location(location)
1901 }
1902
1903 let pieces = self.expression_to_piece(memory, expression, frame_info)?;
1904
1905 if pieces.is_empty() {
1906 return Ok(ExpressionResult::Location(VariableLocation::Error(
1907 "Error: expr_to_piece() returned 0 results".to_string(),
1908 )));
1909 }
1910 if pieces.len() > 1 {
1911 return Ok(ExpressionResult::Location(VariableLocation::Error(
1912 "<unsupported memory implementation>".to_string(),
1913 )));
1914 }
1915
1916 let result = match &pieces[0].location {
1917 Location::Empty => {
1918 ExpressionResult::Location(VariableLocation::Unavailable)
1920 }
1921 Location::Address { address: 0 } => {
1922 let error = "The value of this variable may have been optimized out of the debug info, by the compiler.".to_string();
1923 ExpressionResult::Location(VariableLocation::Error(error))
1924 }
1925 Location::Address { address } => evaluate_address(*address, memory),
1926 Location::Value { value } => {
1927 let value = match value {
1928 gimli::Value::Generic(value) => value.to_string(),
1929 gimli::Value::I8(value) => value.to_string(),
1930 gimli::Value::U8(value) => value.to_string(),
1931 gimli::Value::I16(value) => value.to_string(),
1932 gimli::Value::U16(value) => value.to_string(),
1933 gimli::Value::I32(value) => value.to_string(),
1934 gimli::Value::U32(value) => value.to_string(),
1935 gimli::Value::I64(value) => value.to_string(),
1936 gimli::Value::U64(value) => value.to_string(),
1937 gimli::Value::F32(value) => value.to_string(),
1938 gimli::Value::F64(value) => value.to_string(),
1939 };
1940
1941 ExpressionResult::Value(VariableValue::Valid(value))
1942 }
1943 Location::Register { register } => {
1944 if let Some(value) = frame_info
1945 .registers
1946 .get_register_by_dwarf_id(register.0)
1947 .and_then(|register| register.value)
1948 {
1949 ExpressionResult::Location(VariableLocation::RegisterValue(value))
1950 } else {
1951 ExpressionResult::Location(VariableLocation::Error(format!(
1952 "Error: Cannot resolve register: {register:?}"
1953 )))
1954 }
1955 }
1956 l => ExpressionResult::Location(VariableLocation::Error(format!(
1957 "Unimplemented: extract_location() found a location type: {:.100}",
1958 format!("{l:?}")
1959 ))),
1960 };
1961
1962 Ok(result)
1963 }
1964
1965 pub(crate) fn expression_to_piece(
1967 &self,
1968 memory: &mut dyn MemoryInterface,
1969 expression: gimli::Expression<GimliReader>,
1970 frame_info: StackFrameInfo<'_>,
1971 ) -> Result<Vec<gimli::Piece<GimliReader, usize>>, DebugError> {
1972 let mut evaluation = expression.evaluation(self.unit.encoding());
1973 let mut result = evaluation.evaluate()?;
1974
1975 loop {
1976 result = match result {
1977 EvaluationResult::Complete => return Ok(evaluation.result()),
1978 EvaluationResult::RequiresMemory { address, size, .. } => {
1979 read_memory(size, memory, address, &mut evaluation)?
1980 }
1981 EvaluationResult::RequiresFrameBase => {
1982 provide_frame_base(frame_info.frame_base, &mut evaluation)?
1983 }
1984 EvaluationResult::RequiresRegister {
1985 register,
1986 base_type,
1987 } => provide_register(frame_info.registers, register, base_type, &mut evaluation)?,
1988 EvaluationResult::RequiresRelocatedAddress(address_index) => {
1989 evaluation.resume_with_relocated_address(address_index)?
1991 }
1992 EvaluationResult::RequiresCallFrameCfa => {
1993 provide_cfa(frame_info.canonical_frame_address, &mut evaluation)?
1994 }
1995 unimplemented_expression => {
1996 return Err(DebugError::WarnAndContinue {
1997 message: format!(
1998 "Unimplemented: Expressions that include {unimplemented_expression:?} are not currently supported."
1999 ),
2000 });
2001 }
2002 }
2003 }
2004 }
2005
2006 fn handle_memory_location_special_cases(
2010 &self,
2011 unit_ref: UnitOffset,
2012 child_variable: &mut Variable,
2013 parent_variable: &Variable,
2014 memory: &mut dyn MemoryInterface,
2015 ) {
2016 let location = if let VariableName::Indexed(child_member_index) = child_variable.name {
2017 if let VariableLocation::Address(address) = parent_variable.memory_location {
2019 if let Some(byte_size) = child_variable.byte_size {
2020 let Some(location) = address.checked_add(child_member_index * byte_size) else {
2021 child_variable.set_value(VariableValue::Error(
2022 "Overflow calculating variable address".to_string(),
2023 ));
2024 return;
2025 };
2026
2027 VariableLocation::Address(location)
2028 } else {
2029 parent_variable.memory_location.clone()
2033 }
2034 } else {
2035 VariableLocation::Unavailable
2036 }
2037 } else if child_variable.memory_location == VariableLocation::Unknown {
2038 if self.is_pointer(child_variable, parent_variable, unit_ref) {
2040 match &parent_variable.memory_location {
2041 address @ (VariableLocation::Address(_)
2042 | VariableLocation::RegisterValue(_)) => {
2043 match memory.read_word_32(address.memory_address().unwrap()) {
2045 Ok(memory_location) => {
2046 VariableLocation::Address(memory_location as u64)
2047 }
2048 Err(error) => {
2049 tracing::debug!(
2050 "Failed to read referenced variable address from memory location {} : {error}.",
2051 parent_variable.memory_location
2052 );
2053 VariableLocation::Error(format!(
2054 "Failed to read referenced variable address from memory location {} : {error}.",
2055 parent_variable.memory_location
2056 ))
2057 }
2058 }
2059 }
2060 other => VariableLocation::Unsupported(format!(
2061 "Location {other:?} not supported for referenced variables."
2062 )),
2063 }
2064 } else {
2065 parent_variable.memory_location.clone()
2068 }
2069 } else {
2070 return;
2071 };
2072
2073 child_variable.memory_location = location;
2074 }
2075
2076 fn is_pointer(
2078 &self,
2079 child_variable: &mut Variable,
2080 parent_variable: &Variable,
2081 unit_ref: UnitOffset,
2082 ) -> bool {
2083 (matches!(child_variable.name, VariableName::Named(ref var_name) if var_name.starts_with('*'))
2090 && matches!(parent_variable.role, VariantRole::VariantPart(_)))
2091 || matches!(&parent_variable.type_name, VariableType::Pointer(Some(pointer_name)) if pointer_name.starts_with('*'))
2092 || (matches!(&parent_variable.type_name, VariableType::Pointer(_))
2093 && (matches!(child_variable.type_name, VariableType::Base(_))
2094 || matches!(child_variable.type_name, VariableType::Struct(ref type_name) if type_name.starts_with("&str"))
2095 || matches!(child_variable.name, VariableName::Named(ref var_name) if var_name.starts_with('*'))
2096 || self.has_address_pointer(unit_ref).unwrap_or_else(|error| {
2097 child_variable.set_value(VariableValue::Error(format!("Failed to determine if a struct has variant or generic type fields: {error}")));
2098 false
2099 })))
2100 }
2101
2102 fn has_address_pointer(&self, unit_ref: UnitOffset) -> Result<bool, DebugError> {
2104 let mut entries_tree = self.unit.entries_tree(Some(unit_ref))?;
2105 let entry_node = entries_tree.root()?;
2106 if matches!(
2107 entry_node.entry().tag(),
2108 gimli::DW_TAG_array_type | gimli::DW_TAG_enumeration_type | gimli::DW_TAG_union_type
2109 ) {
2110 return Ok(true);
2111 }
2112 let mut child_nodes = entry_node.children();
2114 while let Some(child_node) = child_nodes.next()? {
2115 if child_node.entry().tag() == gimli::DW_TAG_variant_part {
2116 return Ok(true);
2117 }
2118 }
2119 Ok(false)
2120 }
2121
2122 pub(crate) fn extract_type_name(
2124 &self,
2125 debug_info: &DebugInfo,
2126 entry: &gimli::DebuggingInformationEntry<GimliReader>,
2127 ) -> Result<Option<String>, gimli::Error> {
2128 match entry.attr(gimli::DW_AT_name) {
2129 Some(attr) => {
2130 let name = match attr.value() {
2131 gimli::AttributeValue::DebugStrRef(name_ref) => {
2132 if let Ok(name_raw) = debug_info.dwarf.string(name_ref) {
2133 String::from_utf8_lossy(&name_raw).to_string()
2134 } else {
2135 "Invalid DW_AT_name value".to_string()
2136 }
2137 }
2138 gimli::AttributeValue::String(name) => {
2139 String::from_utf8_lossy(&name).to_string()
2140 }
2141 other => format!("Unimplemented: Evaluate name from {other:?}"),
2142 };
2143
2144 Ok(Some(name))
2145 }
2146 None => {
2147 let Some(attr) = entry.attr(gimli::DW_AT_type) else {
2148 return Ok(None);
2150 };
2151
2152 let gimli::AttributeValue::UnitRef(unit_ref) = attr.value() else {
2153 return Ok(None);
2155 };
2156
2157 let node = self.unit.header.entry(&self.unit.abbreviations, unit_ref)?;
2159 self.extract_type_name(debug_info, &node)
2160 }
2161 }
2162 }
2163
2164 fn process_bitfield_info(
2165 &self,
2166 child_variable: &mut Variable,
2167 entry: &gimli::DebuggingInformationEntry<GimliReader>,
2168 cache: &mut VariableCache,
2169 ) -> Result<(), DebugError> {
2170 if !child_variable.is_valid() {
2171 return Ok(());
2173 }
2174 match self.extract_bitfield_info(child_variable, entry) {
2175 Ok(Some(bitfield)) => {
2176 if let Some(byte_size) = child_variable.byte_size {
2177 let bitfield = bitfield.normalize(byte_size);
2178 child_variable.type_name = VariableType::Bitfield(
2179 bitfield,
2180 Box::new(std::mem::replace(
2181 &mut child_variable.type_name,
2182 VariableType::Unknown,
2183 )),
2184 );
2185 child_variable.value = VariableValue::Empty;
2187 cache.update_variable(child_variable)?;
2188 } else {
2189 child_variable.set_value(VariableValue::Error(
2190 "Error: Failed to decode bitfield information: byte_size not found"
2191 .to_string(),
2192 ));
2193 }
2194 }
2195 Ok(None) => {}
2196 Err(e) => child_variable.set_value(VariableValue::Error(format!(
2197 "Error: Failed to decode bitfield information: {e:?}"
2198 ))),
2199 }
2200
2201 Ok(())
2202 }
2203
2204 fn extract_bitfield_info(
2205 &self,
2206 child_variable: &mut Variable,
2207 entry: &gimli::DebuggingInformationEntry<GimliReader>,
2208 ) -> Result<Option<Bitfield>, gimli::Error> {
2209 let offset = if let Some(attr) = entry.attr(gimli::DW_AT_data_bit_offset) {
2210 match attr.value().udata_value() {
2212 Some(offset) => Some(BitOffset::FromLsb(offset)),
2213 None => {
2214 child_variable.set_value(VariableValue::Error(format!(
2215 "Unimplemented: Attribute Value for DW_AT_data_bit_offset: {:?}",
2216 attr.value()
2217 )));
2218 return Ok(None);
2219 }
2220 }
2221 } else if let Some(attr) = entry.attr(gimli::DW_AT_bit_offset) {
2222 if let Some(offset) = attr.value().udata_value() {
2226 Some(BitOffset::FromMsb(offset))
2227 } else {
2228 child_variable.set_value(VariableValue::Error(format!(
2229 "Unimplemented: Attribute Value for DW_AT_bit_offset: {:?}",
2230 attr.value()
2231 )));
2232 return Ok(None);
2233 }
2234 } else {
2235 None
2236 };
2237
2238 let size = if let Some(attr) = entry.attr(gimli::DW_AT_bit_size) {
2239 match attr.value().udata_value() {
2240 Some(length) => Some(length),
2241 None => {
2242 child_variable.set_value(VariableValue::Error(format!(
2243 "Unimplemented: Attribute Value for DW_AT_bit_size: {:?}",
2244 attr.value()
2245 )));
2246 return Ok(None);
2247 }
2248 }
2249 } else {
2250 None
2251 };
2252
2253 if let (None, None) = (size, offset) {
2254 return Ok(None);
2255 }
2256
2257 Ok(Some(Bitfield {
2258 length: size.unwrap_or(0),
2259 offset: offset.unwrap_or(BitOffset::FromLsb(0)),
2260 }))
2261 }
2262
2263 fn extract_source_location(
2264 &self,
2265 debug_info: &DebugInfo,
2266 entry: &gimli::DebuggingInformationEntry<GimliReader>,
2267 ) -> Result<Option<SourceLocation>, gimli::Error> {
2268 let Some(file_attr) = entry.attr_value(gimli::DW_AT_decl_file) else {
2269 return Ok(None);
2270 };
2271
2272 let Some(path) = extract_file(debug_info, &self.unit, file_attr) else {
2273 return Ok(None);
2274 };
2275
2276 let mut source_location = SourceLocation {
2277 path,
2278 line: None,
2279 column: None,
2280 address: None,
2281 };
2282
2283 for attr in entry.attrs() {
2285 match attr.name() {
2286 gimli::DW_AT_decl_line => {
2287 if let Some(line_number) = extract_line(attr.value()) {
2288 source_location.line = Some(line_number);
2289 }
2290 }
2291 gimli::DW_AT_decl_column => {
2292 if let Some(column_number) = attr.udata_value() {
2293 if column_number != 0 {
2295 source_location.column = Some(super::ColumnType::Column(column_number));
2296 }
2297 }
2298 }
2299 _ => (),
2301 }
2302 }
2303
2304 Ok(Some(source_location))
2305 }
2306
2307 pub(crate) fn parent_offset(&self, offset: UnitOffset) -> Option<UnitOffset> {
2308 self.parents.get(&offset).copied()
2309 }
2310}
2311
2312fn extract_name(
2313 debug_info: &DebugInfo,
2314 entry: &gimli::DebuggingInformationEntry<GimliReader>,
2315) -> Result<Option<String>, gimli::Error> {
2316 let Some(attr) = entry.attr_value(gimli::DW_AT_name) else {
2317 return Ok(None);
2318 };
2319
2320 let name = match attr {
2321 gimli::AttributeValue::DebugStrRef(name_ref) => {
2322 if let Ok(name_raw) = debug_info.dwarf.string(name_ref) {
2323 String::from_utf8_lossy(&name_raw).to_string()
2324 } else {
2325 "Invalid DW_AT_name value".to_string()
2326 }
2327 }
2328 gimli::AttributeValue::String(name) => String::from_utf8_lossy(&name).to_string(),
2329 other => format!("Unimplemented: Evaluate name from {other:?}"),
2330 };
2331
2332 Ok(Some(name))
2333}
2334
2335fn provide_register(
2337 stack_frame_registers: &DebugRegisters,
2338 register: gimli::Register,
2339 base_type: UnitOffset,
2340 evaluation: &mut gimli::Evaluation<EndianReader>,
2341) -> Result<EvaluationResult<EndianReader>, DebugError> {
2342 match stack_frame_registers
2343 .get_register_by_dwarf_id(register.0)
2344 .and_then(|reg| reg.value)
2345 {
2346 Some(raw_value) if base_type == gimli::UnitOffset(0) => {
2347 let register_value = gimli::Value::Generic(raw_value.try_into()?);
2348 Ok(evaluation.resume_with_register(register_value)?)
2349 }
2350 Some(_) => Err(DebugError::WarnAndContinue {
2351 message: format!("Unimplemented: Support for type {base_type:?} in `RequiresRegister`"),
2352 }),
2353 None => Err(DebugError::WarnAndContinue {
2354 message: format!(
2355 "Error while calculating `Variable::memory_location`. No value for register #:{}.",
2356 register.0
2357 ),
2358 }),
2359 }
2360}
2361
2362fn provide_frame_base(
2364 frame_base: Option<u64>,
2365 evaluation: &mut gimli::Evaluation<EndianReader>,
2366) -> Result<EvaluationResult<EndianReader>, DebugError> {
2367 let Some(frame_base) = frame_base else {
2368 return Err(DebugError::WarnAndContinue {
2369 message: "Cannot unwind `Variable` location without a valid frame base address.)"
2370 .to_string(),
2371 });
2372 };
2373 match evaluation.resume_with_frame_base(frame_base) {
2374 Ok(evaluation_result) => Ok(evaluation_result),
2375 Err(error) => Err(DebugError::WarnAndContinue {
2376 message: format!("Error while calculating `Variable::memory_location`:{error}."),
2377 }),
2378 }
2379}
2380
2381fn provide_cfa(
2383 cfa: Option<u64>,
2384 evaluation: &mut gimli::Evaluation<EndianReader>,
2385) -> Result<EvaluationResult<EndianReader>, DebugError> {
2386 let Some(cfa) = cfa else {
2387 return Err(DebugError::WarnAndContinue {
2388 message: "Cannot unwind `Variable` location without a valid canonical frame address.)"
2389 .to_string(),
2390 });
2391 };
2392 match evaluation.resume_with_call_frame_cfa(cfa) {
2393 Ok(evaluation_result) => Ok(evaluation_result),
2394 Err(error) => Err(DebugError::WarnAndContinue {
2395 message: format!("Error while calculating `Variable::memory_location`:{error}."),
2396 }),
2397 }
2398}
2399
2400fn read_memory(
2402 size: u8,
2403 memory: &mut dyn MemoryInterface,
2404 address: u64,
2405 evaluation: &mut gimli::Evaluation<EndianReader>,
2406) -> Result<EvaluationResult<EndianReader>, DebugError> {
2407 fn read<const SIZE: usize>(
2409 memory: &mut dyn MemoryInterface,
2410 address: u64,
2411 ) -> Result<[u8; SIZE], DebugError> {
2412 let mut buff = [0u8; SIZE];
2413 memory.read(address, &mut buff).map_err(|error| {
2414 DebugError::WarnAndContinue {
2415 message: format!("Unexpected error while reading debug expressions from target memory: {error:?}. Please report this as a bug.")
2416 }
2417 })?;
2418 Ok(buff)
2419 }
2420
2421 let val = match size {
2422 1 => {
2423 let buff = read::<1>(memory, address)?;
2424 gimli::Value::U8(buff[0])
2425 }
2426 2 => {
2427 let buff = read::<2>(memory, address)?;
2428 gimli::Value::U16(u16::from_le_bytes(buff))
2429 }
2430 4 => {
2431 let buff = read::<4>(memory, address)?;
2432 gimli::Value::U32(u32::from_le_bytes(buff))
2433 }
2434 x => {
2435 return Err(DebugError::WarnAndContinue {
2436 message: format!(
2437 "Unimplemented: Requested memory with size {x}, which is not supported yet."
2438 ),
2439 });
2440 }
2441 };
2442
2443 Ok(evaluation.resume_with_memory(val)?)
2444}
2445
2446pub(crate) trait RangeExt {
2447 fn contains(self, addr: u64) -> bool;
2448}
2449
2450impl RangeExt for &mut gimli::RngListIter<GimliReader> {
2451 fn contains(self, addr: u64) -> bool {
2452 while let Ok(Some(range)) = self.next() {
2453 if range.contains(addr) {
2454 return true;
2455 }
2456 }
2457
2458 false
2459 }
2460}
2461
2462impl RangeExt for gimli::Range {
2463 fn contains(self, addr: u64) -> bool {
2464 self.begin <= addr && addr < self.end
2465 }
2466}