1use crate::error::{Error, Result};
2use crate::loader::DwarfSlice;
3use gimli::{AttributeValue, Dwarf, Unit, UnitOffset};
4use std::collections::HashMap;
5
6pub struct TypeResolver<'a, 'b> {
7 dwarf: &'b Dwarf<DwarfSlice<'a>>,
8 unit: &'b Unit<DwarfSlice<'a>>,
9 address_size: u8,
10 cache: HashMap<UnitOffset, (String, Option<u64>)>,
11}
12
13impl<'a, 'b> TypeResolver<'a, 'b> {
14 pub fn new(
15 dwarf: &'b Dwarf<DwarfSlice<'a>>,
16 unit: &'b Unit<DwarfSlice<'a>>,
17 address_size: u8,
18 ) -> Self {
19 Self { dwarf, unit, address_size, cache: HashMap::new() }
20 }
21
22 pub fn resolve_type(&mut self, offset: UnitOffset) -> Result<(String, Option<u64>)> {
23 if let Some(cached) = self.cache.get(&offset) {
24 return Ok(cached.clone());
25 }
26
27 let result = self.resolve_type_inner(offset, 0)?;
28 self.cache.insert(offset, result.clone());
29 Ok(result)
30 }
31
32 fn resolve_type_inner(
33 &mut self,
34 offset: UnitOffset,
35 depth: usize,
36 ) -> Result<(String, Option<u64>)> {
37 if depth > 20 {
38 return Ok(("...".to_string(), None));
39 }
40
41 let entry = self
42 .unit
43 .entry(offset)
44 .map_err(|e| Error::Dwarf(format!("Failed to get type entry: {}", e)))?;
45
46 let tag = entry.tag();
47
48 match tag {
49 gimli::DW_TAG_base_type => {
50 let name = self.get_type_name(&entry)?.unwrap_or_else(|| "?".to_string());
51 let size = self.get_byte_size(&entry)?;
52 Ok((name, size))
53 }
54
55 gimli::DW_TAG_pointer_type => {
56 let pointee = if let Some(type_offset) = self.get_type_ref(&entry)? {
57 let (pointee_name, _) = self.resolve_type_inner(type_offset, depth + 1)?;
58 pointee_name
59 } else {
60 "void".to_string()
61 };
62 Ok((format!("*{}", pointee), Some(self.address_size as u64)))
63 }
64
65 gimli::DW_TAG_reference_type => {
66 let referee = if let Some(type_offset) = self.get_type_ref(&entry)? {
67 let (referee_name, _) = self.resolve_type_inner(type_offset, depth + 1)?;
68 referee_name
69 } else {
70 "void".to_string()
71 };
72 Ok((format!("&{}", referee), Some(self.address_size as u64)))
73 }
74
75 gimli::DW_TAG_const_type
76 | gimli::DW_TAG_volatile_type
77 | gimli::DW_TAG_restrict_type => {
78 let prefix = match tag {
79 gimli::DW_TAG_const_type => "const ",
80 gimli::DW_TAG_volatile_type => "volatile ",
81 gimli::DW_TAG_restrict_type => "restrict ",
82 _ => "",
83 };
84 if let Some(type_offset) = self.get_type_ref(&entry)? {
85 let (inner_name, size) = self.resolve_type_inner(type_offset, depth + 1)?;
86 Ok((format!("{}{}", prefix, inner_name), size))
87 } else {
88 Ok((format!("{}void", prefix), None))
89 }
90 }
91
92 gimli::DW_TAG_typedef => {
93 let name = self.get_type_name(&entry)?;
94 if let Some(type_offset) = self.get_type_ref(&entry)? {
95 let (_, size) = self.resolve_type_inner(type_offset, depth + 1)?;
96 Ok((name.unwrap_or_else(|| "typedef".to_string()), size))
97 } else {
98 Ok((name.unwrap_or_else(|| "typedef".to_string()), None))
99 }
100 }
101
102 gimli::DW_TAG_array_type => {
103 let element_type = if let Some(type_offset) = self.get_type_ref(&entry)? {
104 self.resolve_type_inner(type_offset, depth + 1)?
105 } else {
106 ("?".to_string(), None)
107 };
108
109 let count = self.get_array_count(&entry)?;
110 let size = match (element_type.1, count) {
111 (Some(elem_size), Some(c)) => Some(elem_size * c),
112 _ => self.get_byte_size(&entry)?,
113 };
114
115 let count_str = count.map(|c| c.to_string()).unwrap_or_else(|| "?".to_string());
116 Ok((format!("[{}; {}]", element_type.0, count_str), size))
117 }
118
119 gimli::DW_TAG_structure_type | gimli::DW_TAG_class_type | gimli::DW_TAG_union_type => {
120 let name = self.get_type_name(&entry)?.unwrap_or_else(|| "<anonymous>".to_string());
121 let size = self.get_byte_size(&entry)?;
122 Ok((name, size))
123 }
124
125 gimli::DW_TAG_enumeration_type => {
126 let name = self.get_type_name(&entry)?.unwrap_or_else(|| "enum".to_string());
127 let size = self.get_byte_size(&entry)?;
128 Ok((name, size))
129 }
130
131 gimli::DW_TAG_subroutine_type => {
132 Ok(("fn(...)".to_string(), Some(self.address_size as u64)))
133 }
134
135 _ => {
136 let name = self.get_type_name(&entry)?.unwrap_or_else(|| format!("?<{:?}>", tag));
137 let size = self.get_byte_size(&entry)?;
138 Ok((name, size))
139 }
140 }
141 }
142
143 fn get_type_name(
144 &self,
145 entry: &gimli::DebuggingInformationEntry<DwarfSlice<'a>>,
146 ) -> Result<Option<String>> {
147 match entry.attr_value(gimli::DW_AT_name) {
148 Ok(Some(attr)) => {
149 let name = self
150 .dwarf
151 .attr_string(self.unit, attr)
152 .map_err(|e| Error::Dwarf(format!("Failed to read type name: {}", e)))?;
153 Ok(Some(name.to_string_lossy().to_string()))
154 }
155 Ok(None) => Ok(None),
156 Err(e) => Err(Error::Dwarf(format!("Failed to read name attr: {}", e))),
157 }
158 }
159
160 fn get_byte_size(
161 &self,
162 entry: &gimli::DebuggingInformationEntry<DwarfSlice<'a>>,
163 ) -> Result<Option<u64>> {
164 match entry.attr_value(gimli::DW_AT_byte_size) {
165 Ok(Some(AttributeValue::Udata(s))) => Ok(Some(s)),
166 Ok(Some(AttributeValue::Data1(s))) => Ok(Some(s as u64)),
167 Ok(Some(AttributeValue::Data2(s))) => Ok(Some(s as u64)),
168 Ok(Some(AttributeValue::Data4(s))) => Ok(Some(s as u64)),
169 Ok(Some(AttributeValue::Data8(s))) => Ok(Some(s)),
170 _ => Ok(None),
171 }
172 }
173
174 fn get_type_ref(
175 &self,
176 entry: &gimli::DebuggingInformationEntry<DwarfSlice<'a>>,
177 ) -> Result<Option<UnitOffset>> {
178 match entry.attr_value(gimli::DW_AT_type) {
179 Ok(Some(AttributeValue::UnitRef(offset))) => Ok(Some(offset)),
180 Ok(Some(AttributeValue::DebugInfoRef(debug_info_offset))) => {
181 if let Some(unit_debug_offset) = self.unit.header.offset().as_debug_info_offset() {
183 let unit_offset =
184 UnitOffset(debug_info_offset.0.saturating_sub(unit_debug_offset.0));
185 Ok(Some(unit_offset))
186 } else {
187 Ok(None)
188 }
189 }
190 _ => Ok(None),
191 }
192 }
193
194 fn get_array_count(
195 &self,
196 entry: &gimli::DebuggingInformationEntry<DwarfSlice<'a>>,
197 ) -> Result<Option<u64>> {
198 let mut tree = self
199 .unit
200 .entries_tree(Some(entry.offset()))
201 .map_err(|e| Error::Dwarf(format!("Failed to create tree: {}", e)))?;
202
203 let root = tree.root().map_err(|e| Error::Dwarf(format!("Failed to get root: {}", e)))?;
204
205 let mut children = root.children();
206 while let Some(child) =
207 children.next().map_err(|e| Error::Dwarf(format!("Failed to iterate: {}", e)))?
208 {
209 let child_entry = child.entry();
210 if child_entry.tag() == gimli::DW_TAG_subrange_type {
211 if let Some(count) = self.extract_count_attr(child_entry, gimli::DW_AT_count)? {
213 return Ok(Some(count));
214 }
215 if let Some(upper) =
217 self.extract_count_attr(child_entry, gimli::DW_AT_upper_bound)?
218 {
219 return Ok(Some(upper + 1));
220 }
221 }
222 }
223
224 Ok(None)
225 }
226
227 fn extract_count_attr(
228 &self,
229 entry: &gimli::DebuggingInformationEntry<DwarfSlice<'a>>,
230 attr: gimli::DwAt,
231 ) -> Result<Option<u64>> {
232 match entry.attr_value(attr) {
233 Ok(Some(AttributeValue::Udata(v))) => Ok(Some(v)),
234 Ok(Some(AttributeValue::Data1(v))) => Ok(Some(v as u64)),
235 Ok(Some(AttributeValue::Data2(v))) => Ok(Some(v as u64)),
236 Ok(Some(AttributeValue::Data4(v))) => Ok(Some(v as u64)),
237 Ok(Some(AttributeValue::Data8(v))) => Ok(Some(v)),
238 Ok(Some(AttributeValue::Sdata(v))) if v >= 0 => Ok(Some(v as u64)),
239 _ => Ok(None),
240 }
241 }
242}