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