1 use crate::transform::AddressTransform;
2 use gimli::constants;
3 use gimli::read;
4 use gimli::{Reader, UnitSectionOffset};
5 use std::collections::{HashMap, HashSet};
6 
7 #[derive(Debug)]
8 pub struct Dependencies {
9     edges: HashMap<UnitSectionOffset, HashSet<UnitSectionOffset>>,
10     roots: HashSet<UnitSectionOffset>,
11 }
12 
13 impl Dependencies {
new() -> Dependencies14     fn new() -> Dependencies {
15         Dependencies {
16             edges: HashMap::new(),
17             roots: HashSet::new(),
18         }
19     }
20 
add_edge(&mut self, a: UnitSectionOffset, b: UnitSectionOffset)21     fn add_edge(&mut self, a: UnitSectionOffset, b: UnitSectionOffset) {
22         use std::collections::hash_map::Entry;
23         match self.edges.entry(a) {
24             Entry::Occupied(mut o) => {
25                 o.get_mut().insert(b);
26             }
27             Entry::Vacant(v) => {
28                 let mut set = HashSet::new();
29                 set.insert(b);
30                 v.insert(set);
31             }
32         }
33     }
34 
add_root(&mut self, root: UnitSectionOffset)35     fn add_root(&mut self, root: UnitSectionOffset) {
36         self.roots.insert(root);
37     }
38 
get_reachable(&self) -> HashSet<UnitSectionOffset>39     pub fn get_reachable(&self) -> HashSet<UnitSectionOffset> {
40         let mut reachable = self.roots.clone();
41         let mut queue = Vec::new();
42         for i in self.roots.iter() {
43             if let Some(deps) = self.edges.get(i) {
44                 for j in deps {
45                     if reachable.contains(j) {
46                         continue;
47                     }
48                     reachable.insert(*j);
49                     queue.push(*j);
50                 }
51             }
52         }
53         while let Some(i) = queue.pop() {
54             if let Some(deps) = self.edges.get(&i) {
55                 for j in deps {
56                     if reachable.contains(j) {
57                         continue;
58                     }
59                     reachable.insert(*j);
60                     queue.push(*j);
61                 }
62             }
63         }
64         reachable
65     }
66 }
67 
build_dependencies<R: Reader<Offset = usize>>( dwarf: &read::Dwarf<R>, at: &AddressTransform, ) -> read::Result<Dependencies>68 pub fn build_dependencies<R: Reader<Offset = usize>>(
69     dwarf: &read::Dwarf<R>,
70     at: &AddressTransform,
71 ) -> read::Result<Dependencies> {
72     let mut deps = Dependencies::new();
73     let mut units = dwarf.units();
74     while let Some(unit) = units.next()? {
75         build_unit_dependencies(unit, dwarf, at, &mut deps)?;
76     }
77     Ok(deps)
78 }
79 
build_unit_dependencies<R: Reader<Offset = usize>>( header: read::CompilationUnitHeader<R>, dwarf: &read::Dwarf<R>, at: &AddressTransform, deps: &mut Dependencies, ) -> read::Result<()>80 fn build_unit_dependencies<R: Reader<Offset = usize>>(
81     header: read::CompilationUnitHeader<R>,
82     dwarf: &read::Dwarf<R>,
83     at: &AddressTransform,
84     deps: &mut Dependencies,
85 ) -> read::Result<()> {
86     let unit = dwarf.unit(header)?;
87     let mut tree = unit.entries_tree(None)?;
88     let root = tree.root()?;
89     build_die_dependencies(root, dwarf, &unit, at, deps)?;
90     Ok(())
91 }
92 
has_die_back_edge<R: Reader<Offset = usize>>(die: &read::DebuggingInformationEntry<R>) -> bool93 fn has_die_back_edge<R: Reader<Offset = usize>>(die: &read::DebuggingInformationEntry<R>) -> bool {
94     match die.tag() {
95         constants::DW_TAG_variable
96         | constants::DW_TAG_constant
97         | constants::DW_TAG_inlined_subroutine
98         | constants::DW_TAG_lexical_block
99         | constants::DW_TAG_label
100         | constants::DW_TAG_with_stmt
101         | constants::DW_TAG_try_block
102         | constants::DW_TAG_catch_block
103         | constants::DW_TAG_template_type_parameter
104         | constants::DW_TAG_member
105         | constants::DW_TAG_formal_parameter => true,
106         _ => false,
107     }
108 }
109 
has_valid_code_range<R: Reader<Offset = usize>>( die: &read::DebuggingInformationEntry<R>, dwarf: &read::Dwarf<R>, unit: &read::Unit<R>, at: &AddressTransform, ) -> read::Result<bool>110 fn has_valid_code_range<R: Reader<Offset = usize>>(
111     die: &read::DebuggingInformationEntry<R>,
112     dwarf: &read::Dwarf<R>,
113     unit: &read::Unit<R>,
114     at: &AddressTransform,
115 ) -> read::Result<bool> {
116     match die.tag() {
117         constants::DW_TAG_subprogram => {
118             if let Some(ranges_attr) = die.attr_value(constants::DW_AT_ranges)? {
119                 let offset = match ranges_attr {
120                     read::AttributeValue::RangeListsRef(val) => val,
121                     read::AttributeValue::DebugRngListsIndex(index) => {
122                         dwarf.ranges_offset(unit, index)?
123                     }
124                     _ => return Ok(false),
125                 };
126                 let mut has_valid_base = if let Some(read::AttributeValue::Addr(low_pc)) =
127                     die.attr_value(constants::DW_AT_low_pc)?
128                 {
129                     Some(at.can_translate_address(low_pc))
130                 } else {
131                     None
132                 };
133                 let mut it = dwarf.ranges.raw_ranges(offset, unit.encoding())?;
134                 while let Some(range) = it.next()? {
135                     // If at least one of the range addresses can be converted,
136                     // declaring code range as valid.
137                     match range {
138                         read::RawRngListEntry::AddressOrOffsetPair { .. }
139                             if has_valid_base.is_some() =>
140                         {
141                             if has_valid_base.unwrap() {
142                                 return Ok(true);
143                             }
144                         }
145                         read::RawRngListEntry::StartEnd { begin, .. }
146                         | read::RawRngListEntry::StartLength { begin, .. }
147                         | read::RawRngListEntry::AddressOrOffsetPair { begin, .. } => {
148                             if at.can_translate_address(begin) {
149                                 return Ok(true);
150                             }
151                         }
152                         read::RawRngListEntry::StartxEndx { begin, .. }
153                         | read::RawRngListEntry::StartxLength { begin, .. } => {
154                             let addr = dwarf.address(unit, begin)?;
155                             if at.can_translate_address(addr) {
156                                 return Ok(true);
157                             }
158                         }
159                         read::RawRngListEntry::BaseAddress { addr } => {
160                             has_valid_base = Some(at.can_translate_address(addr));
161                         }
162                         read::RawRngListEntry::BaseAddressx { addr } => {
163                             let addr = dwarf.address(unit, addr)?;
164                             has_valid_base = Some(at.can_translate_address(addr));
165                         }
166                         read::RawRngListEntry::OffsetPair { .. } => (),
167                     }
168                 }
169                 return Ok(false);
170             } else if let Some(low_pc) = die.attr_value(constants::DW_AT_low_pc)? {
171                 if let read::AttributeValue::Addr(a) = low_pc {
172                     return Ok(at.can_translate_address(a));
173                 } else if let read::AttributeValue::DebugAddrIndex(i) = low_pc {
174                     let a = dwarf.debug_addr.get_address(4, unit.addr_base, i)?;
175                     return Ok(at.can_translate_address(a));
176                 }
177             }
178         }
179         _ => (),
180     }
181     Ok(false)
182 }
183 
build_die_dependencies<R: Reader<Offset = usize>>( die: read::EntriesTreeNode<R>, dwarf: &read::Dwarf<R>, unit: &read::Unit<R>, at: &AddressTransform, deps: &mut Dependencies, ) -> read::Result<()>184 fn build_die_dependencies<R: Reader<Offset = usize>>(
185     die: read::EntriesTreeNode<R>,
186     dwarf: &read::Dwarf<R>,
187     unit: &read::Unit<R>,
188     at: &AddressTransform,
189     deps: &mut Dependencies,
190 ) -> read::Result<()> {
191     let entry = die.entry();
192     let offset = entry.offset().to_unit_section_offset(unit);
193     let mut attrs = entry.attrs();
194     while let Some(attr) = attrs.next()? {
195         build_attr_dependencies(&attr, offset, dwarf, unit, at, deps)?;
196     }
197 
198     let mut children = die.children();
199     while let Some(child) = children.next()? {
200         let child_entry = child.entry();
201         let child_offset = child_entry.offset().to_unit_section_offset(unit);
202         deps.add_edge(child_offset, offset);
203         if has_die_back_edge(child_entry) {
204             deps.add_edge(offset, child_offset);
205         }
206         if has_valid_code_range(child_entry, dwarf, unit, at)? {
207             deps.add_root(child_offset);
208         }
209         build_die_dependencies(child, dwarf, unit, at, deps)?;
210     }
211     Ok(())
212 }
213 
build_attr_dependencies<R: Reader<Offset = usize>>( attr: &read::Attribute<R>, offset: UnitSectionOffset, _dwarf: &read::Dwarf<R>, unit: &read::Unit<R>, _at: &AddressTransform, deps: &mut Dependencies, ) -> read::Result<()>214 fn build_attr_dependencies<R: Reader<Offset = usize>>(
215     attr: &read::Attribute<R>,
216     offset: UnitSectionOffset,
217     _dwarf: &read::Dwarf<R>,
218     unit: &read::Unit<R>,
219     _at: &AddressTransform,
220     deps: &mut Dependencies,
221 ) -> read::Result<()> {
222     match attr.value() {
223         read::AttributeValue::UnitRef(val) => {
224             let ref_offset = val.to_unit_section_offset(unit);
225             deps.add_edge(offset, ref_offset);
226         }
227         read::AttributeValue::DebugInfoRef(val) => {
228             let ref_offset = UnitSectionOffset::DebugInfoOffset(val);
229             deps.add_edge(offset, ref_offset);
230         }
231         _ => (),
232     }
233     Ok(())
234 }
235