1use crate::error::{Error, Result};
2use crate::loader::{DwarfSlice, LoadedDwarf};
3use crate::types::{MemberLayout, SourceLocation, StructLayout};
4use gimli::{AttributeValue, DebuggingInformationEntry, Dwarf, Unit};
5
6use super::TypeResolver;
7use super::expr::{evaluate_member_offset, try_simple_offset};
8
9pub struct DwarfContext<'a> {
10 dwarf: &'a Dwarf<DwarfSlice<'a>>,
11 address_size: u8,
12}
13
14impl<'a> DwarfContext<'a> {
15 pub fn new(loaded: &'a LoadedDwarf<'a>) -> Self {
16 Self { dwarf: &loaded.dwarf, address_size: loaded.address_size }
17 }
18
19 pub fn find_structs(&self, filter: Option<&str>) -> Result<Vec<StructLayout>> {
20 let mut structs = Vec::new();
21 let mut units = self.dwarf.units();
22
23 while let Some(header) =
24 units.next().map_err(|e| Error::Dwarf(format!("Failed to read unit header: {}", e)))?
25 {
26 let unit = self
27 .dwarf
28 .unit(header)
29 .map_err(|e| Error::Dwarf(format!("Failed to parse unit: {}", e)))?;
30
31 self.process_unit(&unit, filter, &mut structs)?;
32 }
33
34 Ok(structs)
35 }
36
37 fn process_unit(
38 &self,
39 unit: &Unit<DwarfSlice<'a>>,
40 filter: Option<&str>,
41 structs: &mut Vec<StructLayout>,
42 ) -> Result<()> {
43 let mut type_resolver = TypeResolver::new(self.dwarf, unit, self.address_size);
44 let mut entries = unit.entries();
45
46 while let Some((_, entry)) =
47 entries.next_dfs().map_err(|e| Error::Dwarf(format!("Failed to read DIE: {}", e)))?
48 {
49 if !matches!(entry.tag(), gimli::DW_TAG_structure_type | gimli::DW_TAG_class_type) {
50 continue;
51 }
52
53 if let Some(layout) =
54 self.process_struct_entry(unit, entry, filter, &mut type_resolver)?
55 {
56 structs.push(layout);
57 }
58 }
59
60 Ok(())
61 }
62
63 fn process_struct_entry(
64 &self,
65 unit: &Unit<DwarfSlice<'a>>,
66 entry: &DebuggingInformationEntry<DwarfSlice<'a>>,
67 filter: Option<&str>,
68 type_resolver: &mut TypeResolver<'a, '_>,
69 ) -> Result<Option<StructLayout>> {
70 let size = match entry.attr_value(gimli::DW_AT_byte_size) {
71 Ok(Some(AttributeValue::Udata(s))) => s,
72 Ok(Some(AttributeValue::Data1(s))) => s as u64,
73 Ok(Some(AttributeValue::Data2(s))) => s as u64,
74 Ok(Some(AttributeValue::Data4(s))) => s as u64,
75 Ok(Some(AttributeValue::Data8(s))) => s,
76 _ => return Ok(None), };
78
79 let name = self.get_die_name(unit, entry)?;
80 let name = match name {
81 Some(n) if !n.starts_with("__") => n, None => return Ok(None), _ => return Ok(None),
84 };
85
86 if filter.is_some_and(|f| !name.contains(f)) {
87 return Ok(None);
88 }
89
90 let alignment = match entry.attr_value(gimli::DW_AT_alignment) {
91 Ok(Some(AttributeValue::Udata(a))) => Some(a),
92 _ => None,
93 };
94
95 let mut layout = StructLayout::new(name, size, alignment);
96 layout.source_location = self.get_source_location(unit, entry)?;
97 layout.members = self.extract_members(unit, entry, type_resolver)?;
98
99 Ok(Some(layout))
100 }
101
102 fn extract_members(
103 &self,
104 unit: &Unit<DwarfSlice<'a>>,
105 struct_entry: &DebuggingInformationEntry<DwarfSlice<'a>>,
106 type_resolver: &mut TypeResolver<'a, '_>,
107 ) -> Result<Vec<MemberLayout>> {
108 let mut members = Vec::new();
109 let mut tree = unit
110 .entries_tree(Some(struct_entry.offset()))
111 .map_err(|e| Error::Dwarf(format!("Failed to create entries tree: {}", e)))?;
112
113 let root =
114 tree.root().map_err(|e| Error::Dwarf(format!("Failed to get tree root: {}", e)))?;
115
116 let mut children = root.children();
117 while let Some(child) = children
118 .next()
119 .map_err(|e| Error::Dwarf(format!("Failed to iterate children: {}", e)))?
120 {
121 let entry = child.entry();
122 if entry.tag() != gimli::DW_TAG_member {
123 continue;
124 }
125
126 if let Some(member) = self.process_member(unit, entry, type_resolver)? {
127 members.push(member);
128 }
129 }
130
131 members.sort_by_key(|m| m.offset.unwrap_or(u64::MAX));
132 Ok(members)
133 }
134
135 fn process_member(
136 &self,
137 unit: &Unit<DwarfSlice<'a>>,
138 entry: &DebuggingInformationEntry<DwarfSlice<'a>>,
139 type_resolver: &mut TypeResolver<'a, '_>,
140 ) -> Result<Option<MemberLayout>> {
141 let name = self.get_die_name(unit, entry)?.unwrap_or_else(|| "<anonymous>".to_string());
142
143 let offset = self.get_member_offset(unit, entry)?;
144
145 let (type_name, size) = match entry.attr_value(gimli::DW_AT_type) {
146 Ok(Some(AttributeValue::UnitRef(type_offset))) => {
147 type_resolver.resolve_type(type_offset)?
148 }
149 Ok(Some(AttributeValue::DebugInfoRef(debug_info_offset))) => {
150 if let Some(unit_debug_offset) = unit.header.offset().as_debug_info_offset() {
152 let unit_offset =
153 gimli::UnitOffset(debug_info_offset.0.saturating_sub(unit_debug_offset.0));
154 type_resolver.resolve_type(unit_offset)?
155 } else {
156 ("unknown".to_string(), None)
157 }
158 }
159 _ => ("unknown".to_string(), None),
160 };
161
162 let mut member = MemberLayout::new(name, type_name, offset, size);
163
164 if let Ok(Some(AttributeValue::Udata(bit_offset))) =
166 entry.attr_value(gimli::DW_AT_bit_offset)
167 {
168 member.bit_offset = Some(bit_offset);
169 }
170
171 if let Ok(Some(AttributeValue::Udata(data_bit_offset))) =
173 entry.attr_value(gimli::DW_AT_data_bit_offset)
174 {
175 member.bit_offset = Some(data_bit_offset);
176 }
177
178 if let Ok(Some(AttributeValue::Udata(bit_size))) = entry.attr_value(gimli::DW_AT_bit_size) {
179 member.bit_size = Some(bit_size);
180 }
181
182 Ok(Some(member))
183 }
184
185 fn get_member_offset(
186 &self,
187 unit: &Unit<DwarfSlice<'a>>,
188 entry: &DebuggingInformationEntry<DwarfSlice<'a>>,
189 ) -> Result<Option<u64>> {
190 match entry.attr_value(gimli::DW_AT_data_member_location) {
191 Ok(Some(AttributeValue::Udata(offset))) => Ok(Some(offset)),
192 Ok(Some(AttributeValue::Data1(offset))) => Ok(Some(offset as u64)),
193 Ok(Some(AttributeValue::Data2(offset))) => Ok(Some(offset as u64)),
194 Ok(Some(AttributeValue::Data4(offset))) => Ok(Some(offset as u64)),
195 Ok(Some(AttributeValue::Data8(offset))) => Ok(Some(offset)),
196 Ok(Some(AttributeValue::Sdata(offset))) if offset >= 0 => Ok(Some(offset as u64)),
197 Ok(Some(AttributeValue::Exprloc(expr))) => {
198 if let Some(offset) = try_simple_offset(expr, unit.encoding()) {
200 return Ok(Some(offset));
201 }
202 evaluate_member_offset(expr, unit.encoding())
204 }
205 Ok(None) => Ok(None), _ => Ok(None),
207 }
208 }
209
210 fn get_die_name(
211 &self,
212 unit: &Unit<DwarfSlice<'a>>,
213 entry: &DebuggingInformationEntry<DwarfSlice<'a>>,
214 ) -> Result<Option<String>> {
215 match entry.attr_value(gimli::DW_AT_name) {
216 Ok(Some(attr)) => {
217 let name = self
218 .dwarf
219 .attr_string(unit, attr)
220 .map_err(|e| Error::Dwarf(format!("Failed to read name: {}", e)))?;
221 Ok(Some(name.to_string_lossy().to_string()))
222 }
223 Ok(None) => Ok(None),
224 Err(e) => Err(Error::Dwarf(format!("Failed to read name attribute: {}", e))),
225 }
226 }
227
228 fn get_source_location(
229 &self,
230 _unit: &Unit<DwarfSlice<'a>>,
231 entry: &DebuggingInformationEntry<DwarfSlice<'a>>,
232 ) -> Result<Option<SourceLocation>> {
233 let file_index = match entry.attr_value(gimli::DW_AT_decl_file) {
234 Ok(Some(AttributeValue::FileIndex(idx))) => idx,
235 Ok(Some(AttributeValue::Udata(idx))) => idx,
236 _ => return Ok(None),
237 };
238
239 let line = match entry.attr_value(gimli::DW_AT_decl_line) {
240 Ok(Some(AttributeValue::Udata(l))) => l,
241 _ => return Ok(None),
242 };
243
244 Ok(Some(SourceLocation { file: format!("file#{}", file_index), line }))
247 }
248}