1use std::convert::Infallible;
4
5use anyhow::Context as _;
6
7use super::{Parser, Result};
8use crate::{
9 die::utils::get_string_attr, file::DwarfReader, types::DieTypeDefinition, Die, DwarfDb,
10};
11
12pub fn data_offset() -> DataOffset {
14 DataOffset
15}
16
17pub struct DataOffset;
18
19impl Parser<usize> for DataOffset {
20 fn parse(&self, db: &dyn DwarfDb, entry: Die) -> Result<usize> {
21 Ok(entry.udata_attr(db, gimli::DW_AT_data_member_location)?)
22 }
23}
24
25pub fn attr<T>(attr: gimli::DwAt) -> Attr<T> {
27 Attr::new(attr)
28}
29
30pub fn entry_type() -> Attr<Die> {
31 Attr::new(gimli::DW_AT_type)
32}
33
34pub struct Attr<T> {
35 attr: gimli::DwAt,
36 _marker: std::marker::PhantomData<T>,
37}
38
39impl<T> Attr<T> {
40 pub fn new(attr: gimli::DwAt) -> Self {
41 Attr {
42 attr,
43 _marker: std::marker::PhantomData,
44 }
45 }
46}
47
48impl Parser<usize> for Attr<usize> {
49 fn parse(&self, db: &dyn DwarfDb, entry: Die) -> Result<usize> {
50 Ok(entry.udata_attr(db, self.attr)?)
51 }
52}
53
54impl Parser<u64> for Attr<u64> {
55 fn parse(&self, db: &dyn DwarfDb, entry: Die) -> Result<u64> {
56 let value = entry.get_attr(db, self.attr)?;
57 if let Some(v) = value.udata_value() {
58 Ok(v)
59 } else if let Some(v) = value.sdata_value() {
60 if v < 0 {
61 return Err(anyhow::anyhow!(
62 "Value {} is negative, cannot fit in u64",
63 v
64 ));
65 }
66 Ok(v as u64)
67 } else {
68 Err(anyhow::anyhow!(
69 "Expected {} to be a signed or unsigned data attribute, found: {value:?}",
70 self.attr,
71 ))
72 }
73 }
74}
75
76impl Parser<i64> for Attr<i64> {
77 fn parse(&self, db: &dyn DwarfDb, entry: Die) -> Result<i64> {
78 let value = entry.get_attr(db, self.attr)?;
79 if let Some(v) = value.udata_value() {
80 if v > i64::MAX as u64 {
81 return Err(anyhow::anyhow!("Value {} exceeds i64 maximum", v));
82 }
83 Ok(v as i64)
84 } else if let Some(v) = value.sdata_value() {
85 Ok(v)
86 } else {
87 Err(anyhow::anyhow!(
88 "Expected {} to be a signed or unsigned data attribute, found: {value:?}",
89 self.attr,
90 ))
91 }
92 }
93}
94
95impl Parser<i128> for Attr<i128> {
96 fn parse(&self, db: &dyn DwarfDb, entry: Die) -> Result<i128> {
97 let value = entry.get_attr(db, self.attr)?;
98 if let Some(v) = value.udata_value() {
99 Ok(v as i128)
100 } else if let Some(v) = value.sdata_value() {
101 Ok(v as i128)
102 } else {
103 Err(anyhow::anyhow!(
104 "Expected {} to be a signed or unsigned data attribute, found: {value:?}",
105 self.attr,
106 ))
107 }
108 }
109}
110
111impl Parser<String> for Attr<String> {
112 fn parse(&self, db: &dyn DwarfDb, entry: Die) -> Result<String> {
113 Ok(entry.string_attr(db, self.attr)?)
114 }
115}
116
117impl Parser<Option<String>> for Attr<Option<String>> {
118 fn parse(&self, db: &dyn DwarfDb, entry: Die) -> Result<Option<String>> {
119 entry.with_entry_and_unit(db, |entry, unit_ref| {
120 get_string_attr(entry, self.attr, unit_ref)
121 })?
122 }
123}
124
125impl Parser<gimli::AttributeValue<DwarfReader>> for Attr<gimli::AttributeValue<DwarfReader>> {
126 fn parse(&self, db: &dyn DwarfDb, entry: Die) -> Result<gimli::AttributeValue<DwarfReader>> {
127 Ok(entry.get_attr(db, self.attr)?)
128 }
129}
130
131impl Parser<gimli::DwLang> for Attr<gimli::DwLang> {
132 fn parse(&self, db: &dyn DwarfDb, entry: Die) -> Result<gimli::DwLang> {
133 let value = entry.get_attr(db, self.attr)?;
134 if let gimli::AttributeValue::Language(lang) = value {
135 Ok(lang)
136 } else {
137 Err(anyhow::anyhow!(
138 "Expected {} to be a language attribute, found: {value:?}",
139 self.attr,
140 ))
141 }
142 }
143}
144
145impl Parser<Die> for Attr<Die> {
146 fn parse(&self, db: &dyn DwarfDb, entry: Die) -> Result<Die> {
147 Ok(entry.get_referenced_entry(db, self.attr)?)
148 }
149}
150
151pub struct OptionalAttr<T> {
153 attr: gimli::DwAt,
154 _marker: std::marker::PhantomData<T>,
155}
156
157impl<T> Parser<Option<T>> for OptionalAttr<T>
158where
159 Attr<T>: Parser<T>,
160{
161 fn parse(&self, db: &dyn DwarfDb, entry: Die) -> Result<Option<T>> {
162 Ok(attr(self.attr).parse(db, entry).ok())
163 }
164}
165
166pub fn optional_attr<T>(attr: gimli::DwAt) -> OptionalAttr<T> {
167 OptionalAttr {
168 attr,
169 _marker: std::marker::PhantomData,
170 }
171}
172
173pub fn member(name: &str) -> Member {
175 Member {
176 name: name.to_string(),
177 }
178}
179
180pub struct Member {
181 name: String,
182}
183
184impl Parser<Die> for Member {
185 fn parse(&self, db: &dyn DwarfDb, entry: Die) -> Result<Die> {
186 Ok(entry.get_member(db, &self.name)?)
187 }
188}
189
190pub struct IsMember {
192 pub(super) expected_name: String,
193}
194
195impl Parser<Die> for IsMember {
196 fn parse(&self, db: &dyn DwarfDb, entry: Die) -> Result<Die> {
197 let entry_name = entry.name(db).context("Failed to get entry name")?;
198 if entry_name == self.expected_name {
199 Ok(entry)
200 } else {
201 Err(anyhow::anyhow!(
202 "Expected field '{}', found '{entry_name}'",
203 self.expected_name,
204 ))
205 }
206 }
207}
208
209pub fn is_member(name: &str) -> IsMember {
210 IsMember {
211 expected_name: name.to_string(),
212 }
213}
214
215pub struct IsMemberOffset {
217 expected_name: String,
218}
219
220impl Parser<usize> for IsMemberOffset {
221 fn parse(&self, db: &dyn DwarfDb, entry: Die) -> Result<usize> {
222 let entry_name = entry
223 .name(db)
224 .map_err(|e| crate::Error::from(anyhow::anyhow!("Failed to get entry name: {}", e)))?;
225 if entry_name == self.expected_name {
226 entry
227 .udata_attr(db, gimli::DW_AT_data_member_location)
228 .with_context(|| format!("Failed to get offset for field '{}'", self.expected_name))
229 } else {
230 Err(anyhow::anyhow!(
231 "Expected field '{}', found '{entry_name}'",
232 self.expected_name,
233 ))
234 }
235 }
236}
237
238pub fn is_member_offset(name: &str) -> IsMemberOffset {
239 IsMemberOffset {
240 expected_name: name.to_string(),
241 }
242}
243
244pub fn member_by_tag(tag: gimli::DwTag) -> MemberByTag {
246 MemberByTag { tag }
247}
248
249pub struct MemberByTag {
250 tag: gimli::DwTag,
251}
252
253impl Parser<Die> for MemberByTag {
254 fn parse(&self, db: &dyn DwarfDb, entry: Die) -> Result<Die> {
255 entry
256 .get_member_by_tag(db, self.tag)
257 .with_context(|| format!("Failed to find member with tag '{}'", self.tag,))
258 }
259}
260
261pub fn is_member_tag(tag: gimli::DwTag) -> IsMemberTag {
263 IsMemberTag { expected_tag: tag }
264}
265
266pub struct IsMemberTag {
267 expected_tag: gimli::DwTag,
268}
269
270impl Parser<Die> for IsMemberTag {
271 fn parse(&self, db: &dyn DwarfDb, entry: Die) -> Result<Die> {
272 let entry_tag = entry.tag(db);
273 if entry_tag == self.expected_tag {
274 Ok(entry)
275 } else {
276 Err(anyhow::anyhow!(
277 "Expected entry to have tag '{}', found '{entry_tag}'",
278 self.expected_tag,
279 ))
280 }
281 }
282}
283
284pub struct Generic {
286 expected_name: String,
287}
288
289impl Parser<Die> for Generic {
290 fn parse(&self, db: &dyn DwarfDb, entry: Die) -> Result<Die> {
291 if entry.tag(db) != gimli::DW_TAG_template_type_parameter {
292 return Err(anyhow::anyhow!(
293 "Expected generic type parameter, found tag {}",
294 entry.tag(db)
295 ));
296 }
297 let entry_name = entry
298 .name(db)
299 .map_err(|e| crate::Error::from(anyhow::anyhow!("Failed to get entry name: {}", e)))?;
300 if entry_name == self.expected_name {
301 Ok(entry.get_referenced_entry(db, gimli::DW_AT_type)?)
302 } else {
303 Err(anyhow::anyhow!(
304 "Expected generic '{}', found '{entry_name}'",
305 self.expected_name,
306 ))
307 }
308 }
309}
310
311pub fn generic(name: &str) -> Generic {
312 Generic {
313 expected_name: name.to_string(),
314 }
315}
316
317pub fn resolved_generic(name: &str) -> impl Parser<DieTypeDefinition> {
318 generic(name).then(resolve_type_shallow())
319}
320
321pub struct ResolveType;
323
324impl Parser<DieTypeDefinition> for ResolveType {
325 fn parse(&self, db: &dyn DwarfDb, entry: Die) -> Result<DieTypeDefinition> {
326 Ok(crate::types::resolve_type_offset(db, entry)?)
327 }
328}
329
330pub fn identity() -> Identity {
332 Identity
333}
334
335pub struct Identity;
336
337impl Parser<Die> for Identity {
338 fn parse(&self, _db: &dyn DwarfDb, entry: Die) -> Result<Die> {
339 Ok(entry)
340 }
341}
342
343#[allow(dead_code)]
344pub fn resolve_type() -> ResolveType {
345 ResolveType
346}
347
348pub struct ResolveTypeShallow;
350
351impl Parser<DieTypeDefinition> for ResolveTypeShallow {
352 fn parse(&self, db: &dyn DwarfDb, entry: Die) -> Result<DieTypeDefinition> {
353 Ok(crate::types::shallow_resolve_type(db, entry)?)
354 }
355}
356
357pub fn resolve_type_shallow() -> ResolveTypeShallow {
358 ResolveTypeShallow
359}
360
361pub struct FieldPath {
363 path: Vec<String>,
364}
365
366impl FieldPath {
367 pub fn new(path: Vec<String>) -> Self {
368 Self { path }
369 }
370}
371
372impl Parser<(Die, usize)> for FieldPath {
373 fn parse(&self, db: &dyn DwarfDb, mut entry: Die) -> Result<(Die, usize)> {
374 let mut path_iter = self.path.iter();
375
376 let mut offset = 0;
377 let Some(first_field) = path_iter.next() else {
379 return Ok((entry, 0));
381 };
382
383 if &entry.name(db)? != first_field {
384 return Err(anyhow::anyhow!(
385 "Expected entry name '{}', found '{}'",
386 first_field,
387 entry.name(db)?
388 ));
389 }
390 offset += entry
391 .udata_attr(db, gimli::DW_AT_data_member_location)
392 .with_context(|| format!("Failed to get offset for field '{first_field}'"))?;
393 entry = entry
394 .get_referenced_entry(db, gimli::DW_AT_type)
395 .with_context(|| format!("Failed to resolve type for field '{first_field}'"))?;
396 for field_name in path_iter {
397 entry = entry
398 .get_member(db, field_name)
399 .with_context(|| format!("Failed to navigate to field '{field_name}'"))?;
400 offset += entry
401 .udata_attr(db, gimli::DW_AT_data_member_location)
402 .with_context(|| format!("Failed to get offset for field '{field_name}'"))?;
403 entry = entry
404 .get_referenced_entry(db, gimli::DW_AT_type)
405 .with_context(|| format!("Failed to resolve type for field '{field_name}'"))?;
406 }
407
408 Ok((entry, offset))
409 }
410}
411
412pub fn field_path_offset(path: Vec<&str>) -> impl Parser<usize> {
414 FieldPath::new(path.into_iter().map(|s| s.to_string()).collect()).map(|(_, offset)| offset)
415}
416
417pub fn rust_cu() -> impl Parser<bool> {
418 attr(gimli::DW_AT_language).map(|lang| matches!(lang, gimli::DW_LANG_Rust))
419}
420
421pub fn name() -> impl Parser<Option<String>> {
422 attr(gimli::DW_AT_name)
423}
424
425pub fn tag() -> impl Parser<gimli::DwTag> {
426 super::from_fn(|db: &dyn DwarfDb, entry: Die| Ok::<_, Infallible>(entry.tag(db)))
427}
428
429pub fn offset() -> impl Parser<usize> {
430 super::from_fn(|_: &dyn DwarfDb, entry: Die| Ok::<_, Infallible>(entry.offset()))
431}