Skip to main content

gimli/read/
lookup.rs

1use core::marker::PhantomData;
2
3use crate::common::{DebugInfoOffset, Format};
4use crate::read::{Error, Reader, ReaderOffset, Result, UnitOffset};
5
6// The various "Accelerated Access" sections (DWARF standard v4 Section 6.1) all have
7// similar structures. They consist of a header with metadata and an offset into the
8// .debug_info section for the entire compilation unit, and a series
9// of following entries that list addresses (for .debug_aranges) or names
10// (for .debug_pubnames and .debug_pubtypes) that are covered.
11//
12// Because these three tables all have similar structures, we abstract out some of
13// the parsing mechanics.
14
15pub trait LookupParser<R: Reader> {
16    /// The type of the produced header.
17    type Header;
18    /// The type of the produced entry.
19    type Entry;
20
21    /// Parse a header from `input`. Returns a tuple of `input` sliced to contain just the entries
22    /// corresponding to this header (without the header itself), and the parsed representation of
23    /// the header itself.
24    fn parse_header(input: &mut R) -> Result<(R, Self::Header)>;
25
26    /// Parse a single entry from `input`. Returns either a parsed representation of the entry
27    /// or None if `input` is exhausted.
28    fn parse_entry(input: &mut R, header: &Self::Header) -> Result<Option<Self::Entry>>;
29}
30
31#[derive(Clone, Debug)]
32pub struct DebugLookup<R, Parser>
33where
34    R: Reader,
35    Parser: LookupParser<R>,
36{
37    input_buffer: R,
38    phantom: PhantomData<Parser>,
39}
40
41impl<R, Parser> From<R> for DebugLookup<R, Parser>
42where
43    R: Reader,
44    Parser: LookupParser<R>,
45{
46    fn from(input_buffer: R) -> Self {
47        DebugLookup {
48            input_buffer,
49            phantom: PhantomData,
50        }
51    }
52}
53
54impl<R, Parser> DebugLookup<R, Parser>
55where
56    R: Reader,
57    Parser: LookupParser<R>,
58{
59    pub fn items(&self) -> LookupEntryIter<R, Parser> {
60        LookupEntryIter {
61            current_set: None,
62            remaining_input: self.input_buffer.clone(),
63        }
64    }
65
66    pub fn reader(&self) -> &R {
67        &self.input_buffer
68    }
69}
70
71#[derive(Clone, Debug)]
72pub struct LookupEntryIter<R, Parser>
73where
74    R: Reader,
75    Parser: LookupParser<R>,
76{
77    current_set: Option<(R, Parser::Header)>, // Only none at the very beginning and end.
78    remaining_input: R,
79}
80
81impl<R, Parser> LookupEntryIter<R, Parser>
82where
83    R: Reader,
84    Parser: LookupParser<R>,
85{
86    /// Advance the iterator and return the next entry.
87    ///
88    /// Returns the newly parsed entry as `Ok(Some(Parser::Entry))`. Returns
89    /// `Ok(None)` when iteration is complete and all entries have already been
90    /// parsed and yielded. If an error occurs while parsing the next entry,
91    /// then this error is returned as `Err(e)`, and all subsequent calls return
92    /// `Ok(None)`.
93    pub fn next(&mut self) -> Result<Option<Parser::Entry>> {
94        loop {
95            if let Some((ref mut input, ref header)) = self.current_set
96                && !input.is_empty()
97            {
98                match Parser::parse_entry(input, header) {
99                    Ok(Some(entry)) => return Ok(Some(entry)),
100                    Ok(None) => {}
101                    Err(e) => {
102                        input.empty();
103                        self.remaining_input.empty();
104                        return Err(e);
105                    }
106                }
107            }
108            if self.remaining_input.is_empty() {
109                self.current_set = None;
110                return Ok(None);
111            }
112            match Parser::parse_header(&mut self.remaining_input) {
113                Ok(set) => {
114                    self.current_set = Some(set);
115                }
116                Err(e) => {
117                    self.current_set = None;
118                    self.remaining_input.empty();
119                    return Err(e);
120                }
121            }
122        }
123    }
124}
125
126#[derive(Debug, Clone, PartialEq, Eq)]
127pub struct PubStuffHeader<T = usize> {
128    format: Format,
129    length: T,
130    version: u16,
131    unit_offset: DebugInfoOffset<T>,
132    unit_length: T,
133}
134
135pub trait PubStuffEntry<R: Reader> {
136    fn new(
137        die_offset: UnitOffset<R::Offset>,
138        name: R,
139        unit_header_offset: DebugInfoOffset<R::Offset>,
140    ) -> Self;
141}
142
143#[derive(Clone, Debug)]
144pub struct PubStuffParser<R, Entry>
145where
146    R: Reader,
147    Entry: PubStuffEntry<R>,
148{
149    // This struct is never instantiated.
150    phantom: PhantomData<(R, Entry)>,
151}
152
153impl<R, Entry> LookupParser<R> for PubStuffParser<R, Entry>
154where
155    R: Reader,
156    Entry: PubStuffEntry<R>,
157{
158    type Header = PubStuffHeader<R::Offset>;
159    type Entry = Entry;
160
161    /// Parse an pubthings set header. Returns a tuple of the
162    /// pubthings to be parsed for this set, and the newly created PubThingHeader struct.
163    fn parse_header(input: &mut R) -> Result<(R, Self::Header)> {
164        let (length, format) = input.read_initial_length()?;
165        let mut rest = input.split(length)?;
166
167        let version = rest.read_u16()?;
168        if version != 2 {
169            return Err(Error::UnknownVersion(u64::from(version)));
170        }
171
172        let unit_offset = rest.read_offset(format).map(DebugInfoOffset)?;
173        let unit_length = rest.read_length(format)?;
174
175        let header = PubStuffHeader {
176            format,
177            length,
178            version,
179            unit_offset,
180            unit_length,
181        };
182        Ok((rest, header))
183    }
184
185    /// Parse a single pubthing. Return `None` for the null pubthing, `Some` for an actual pubthing.
186    fn parse_entry(input: &mut R, header: &Self::Header) -> Result<Option<Self::Entry>> {
187        let offset = input.read_offset(header.format)?;
188        if offset.into_u64() == 0 {
189            input.empty();
190            Ok(None)
191        } else {
192            let name = input.read_null_terminated_slice()?;
193            Ok(Some(Self::Entry::new(
194                UnitOffset(offset),
195                name,
196                header.unit_offset,
197            )))
198        }
199    }
200}