cdefmt_parser/
parser.rs

1//! Contains logic related to finding logs in the elf and parsing them.
2
3use object::{AddressSize, Object, ObjectSection, ObjectSymbol, ReadRef};
4
5use crate::{
6    dwarf::Dwarf,
7    metadata::{parse_metadata, Metadata},
8    r#type::Type,
9    Error, Result,
10};
11
12/// Responsible for parsing logs from the elf.
13pub struct Parser<'elf> {
14    logs_section: &'elf [u8],
15    build_id: &'elf [u8],
16    dwarf: Dwarf<'elf>,
17    address_size: AddressSize,
18    metadata_addresses: Vec<u64>,
19}
20
21impl<'elf> Parser<'elf> {
22    /// Creates a new Parser from elf data.
23    pub fn new<R: ReadRef<'elf>>(data: R) -> Result<Self> {
24        let file = object::File::parse(data)?;
25        let dwarf = Dwarf::new(&file)?;
26        let build_id = file
27            .build_id()?
28            .ok_or(Error::Custom("Unable to find build ID in elf!"))?;
29
30        let address_size = file.architecture().address_size().ok_or(Error::Custom(
31            "Unsupported architecture, no address size information!",
32        ))?;
33
34        let metadata_addresses = file
35            .symbols()
36            .filter(|s| s.name().is_ok_and(|n| n.contains("cdefmt_log_metadata")))
37            .map(|s| s.address())
38            .collect::<Vec<_>>();
39
40        Ok(Parser {
41            logs_section: file
42                .section_by_name(".cdefmt")
43                .ok_or(Error::MissingSection)?
44                .data()?,
45            build_id,
46            dwarf,
47            address_size,
48            metadata_addresses,
49        })
50    }
51
52    /// Returns a specific log's metadata.
53    pub fn get_log_metadata(&self, id: usize) -> Result<Metadata<'elf>> {
54        parse_metadata(self.logs_section, id, self.endian())
55    }
56
57    /// Returns an iterator over all of the log's metadata/type information.
58    pub fn iter_logs<'parser>(&'parser self) -> LogIterator<'parser, 'elf> {
59        LogIterator {
60            parser: self,
61            symbol_addr_iterator: self.metadata_addresses.iter(),
62        }
63    }
64
65    /// Returns the type of the log's arguments.
66    /// Return:
67    /// * Ok(Some(_)) => The type of the arguments.
68    /// * Ok(None)    => Unable to find the type in the elf's dwarf section.
69    /// * Err(_)      => Encountered some error while parsing the dwarf.
70    pub fn get_log_args_type(&self, metadata: &Metadata) -> Result<Option<Type>> {
71        let type_name = format!("cdefmt_log_args_t{}", metadata.counter);
72        self.dwarf.get_type(metadata.file, &type_name)
73    }
74
75    pub fn build_id(&self) -> &'elf [u8] {
76        self.build_id
77    }
78
79    pub fn address_size(&self) -> AddressSize {
80        self.address_size
81    }
82
83    pub fn endian(&self) -> gimli::RunTimeEndian {
84        self.dwarf.endian()
85    }
86}
87
88pub struct LogIterator<'parser, 'elf>
89where
90    'elf: 'parser,
91{
92    parser: &'parser Parser<'elf>,
93    symbol_addr_iterator: std::slice::Iter<'parser, u64>,
94}
95
96impl<'elf> Iterator for LogIterator<'_, 'elf> {
97    type Item = Result<(Metadata<'elf>, Option<Type>)>;
98
99    fn next(&mut self) -> Option<Self::Item> {
100        let metadata = match self.symbol_addr_iterator.next() {
101            Some(&addr) => match self.parser.get_log_metadata(addr as usize) {
102                Ok(m) => m,
103                Err(e) => return Some(Err(e)),
104            },
105            None => return None,
106        };
107
108        let ty = match self.parser.get_log_args_type(&metadata) {
109            Ok(t) => t,
110            Err(e) => return Some(Err(e)),
111        };
112
113        Some(Ok((metadata, ty)))
114    }
115}