cdefmt_decoder/
decoder.rs

1//! Contains logic related to finding logs in the elf and parsing them.
2
3use std::collections::HashMap;
4
5use cdefmt_parser::{
6    metadata::Metadata,
7    r#type::{self, Type},
8    Parser,
9};
10use gimli::Reader;
11use object::ReadRef;
12
13use crate::{log::Log, var::Var, Error, Result};
14
15/// Responsible for parsing logs from the elf.
16pub struct Decoder<'elf> {
17    parser: Parser<'elf>,
18    log_cache: HashMap<usize, (Metadata<'elf>, Option<Type>)>,
19}
20
21impl<'elf> Decoder<'elf> {
22    /// Creates a new Parser from elf data.
23    pub fn new<R: ReadRef<'elf>>(data: R) -> Result<Self> {
24        Ok(Decoder {
25            parser: Parser::new(data)?,
26            log_cache: Default::default(),
27        })
28    }
29
30    /// Decodes a raw log
31    pub fn decode_log(&mut self, data: &[u8]) -> Result<Log<'elf>> {
32        let mut data = gimli::EndianSlice::new(data, self.parser.endian());
33        let id = data.read_address(self.parser.address_size().bytes())? as usize;
34
35        if let std::collections::hash_map::Entry::Vacant(e) = self.log_cache.entry(id) {
36            // Parse log metadata and type if we don't have it cached.
37            let metadata = self.parser.get_log_metadata(id)?;
38            let ty = self.parser.get_log_args_type(&metadata)?;
39            e.insert((metadata, ty));
40        };
41
42        // Unwrap safety: made sure that the entry exists right above here.
43        let (metadata, ty) = self.log_cache.get(&id).unwrap();
44
45        let args = if let Some(ty) = ty {
46            Self::decode_log_args(ty, data)?
47        } else {
48            vec![]
49        };
50
51        let log = Log::new(metadata.clone(), args);
52
53        if id == 0 {
54            self.validate_init(&log)?
55        }
56
57        Ok(log)
58    }
59
60    /// Parses all logs in the elf and pre-caches their metadata and type
61    /// information, to speed up future log decoding.
62    /// This can also serve as a way to validate that all the logs encoded into
63    /// the file are valid and can be properly parsed.
64    pub fn precache_log_metadata(&mut self) -> Result<usize> {
65        self.log_cache = self
66            .parser
67            .iter_logs()
68            .map(|l| {
69                let (metadata, ty) = l?;
70                Ok((metadata.id, (metadata, ty)))
71            })
72            .collect::<Result<_>>()?;
73
74        Ok(self.log_cache.len())
75    }
76
77    pub fn get_endianness(&self) -> gimli::RunTimeEndian {
78        self.parser.endian()
79    }
80
81    // Parses the log's arguments.
82    fn decode_log_args<R: Reader>(ty: &Type, mut data: R) -> Result<Vec<Var>> {
83        let members = if let Type::Structure { members, .. } = ty {
84            members
85        } else {
86            return Err(Error::Custom("The log's args aren't a structure!"));
87        };
88
89        // We already read the log_id from the data, skip it.
90        let members = &members[1..];
91
92        // Parse the raw data into `Var` representation.
93        let mut decoded = members
94            .iter()
95            // Filter out the 'dynamic_data' member for now - this contains variable-length data
96            // that needs to be processed after parsing the fixed-size fields, since we need
97            // information from those fields to know how to interpret the dynamic data.
98            // The dynamic_data field is positioned at the end of the structure when present,
99            // but not all logs have it, so we filter by name rather than skipping the last element.
100            .filter(|m| !matches!(m.name.as_str(), "dynamic_data"))
101            .map(|m| Ok(Var::parse(&m.ty, &mut data)?.0))
102            .collect::<Result<Vec<_>>>()?;
103
104        // Decode dynamic members
105        for (i, member) in members.iter().enumerate() {
106            match member.name.as_str() {
107                n if n.contains("dynamic_array") => {
108                    decoded[i] = Self::decode_dynamic_array(member, &decoded[i], &mut data)?
109                }
110                _ => continue,
111            }
112        }
113
114        Ok(decoded)
115    }
116
117    fn validate_init(&self, log: &Log) -> Result<()> {
118        let args = log.get_args();
119
120        if args.is_empty() {
121            return Err(Error::Custom("No build ID argument information!"));
122        }
123
124        if let Var::Array(build_id) = args.first().unwrap() {
125            let build_id = build_id
126                .iter()
127                .map(|b| match b {
128                    Var::U8(b) => Ok(*b),
129                    _ => Err(Error::Custom("Build ID data contains non u8 element!")),
130                })
131                .collect::<Result<Vec<_>>>()?;
132            if self.parser.build_id() != build_id {
133                Err(Error::Custom("Build ID mismatch!"))
134            } else {
135                Ok(())
136            }
137        } else {
138            Err(Error::Custom("Build ID missing or not an array"))
139        }
140    }
141
142    fn decode_dynamic_array<R: Reader>(
143        metadata: &r#type::StructureMember,
144        value: &Var,
145        data: &mut R,
146    ) -> Result<Var> {
147        // The dynamic_array is structured as:
148        // [0] size
149        // [1] type
150
151        // Extract size from value that was previously decoded
152        let size = match value {
153            Var::Structure { members } => &members[0].value,
154            _ => return Err(Error::Custom("Dynamic array metadata is not a struct!")),
155        }
156        .as_u64();
157
158        // Extract type from metadata
159        let arr_ty = match &metadata.ty {
160            Type::Structure { members, .. } => &members[1].ty,
161            _ => return Err(Error::Custom("Dynamic array metadata is not a struct!")),
162        };
163
164        let ty = match arr_ty {
165            Type::Array { ty, .. } => ty,
166            _ => {
167                return Err(Error::Custom(
168                    "Dynamic array type metadata is not an array!",
169                ))
170            }
171        };
172
173        let dyn_ty = Type::Array {
174            ty: ty.clone(),
175            lengths: vec![size / ty.size() as u64],
176        };
177
178        Ok(Var::parse(&dyn_ty, data)?.0)
179    }
180}