Skip to main content

proka_exec/utils/
parser.rs

1//! The parser of proka executable.
2use crate::header::Header;
3use crate::sections::{SectionError, SectionTable, SectionIndex};
4use crate::{Result, Error, SECTION_HDR_SIZE, SECTION_INDEX_SIZE, HEADER_SIZE};
5
6/// The parser of the proka executable.
7///
8/// # Usage
9/// To use this parser, you must put an slice into the initializations.
10///
11/// If the content of the proka executable is in memory, the best way
12/// is to use `core::slice::from_raw_parts`.
13#[derive(Debug, Clone, Copy)]
14pub struct Parser<'a> {
15    buf: &'a [u8],
16    header: Header,
17    total_sections: u16,
18}
19
20impl<'a> Parser<'a> {
21    /// Initialize the parser by passing a slice.
22    ///
23    /// This is the recommended way to initialize this parser, because it will
24    /// help you do all checks and return error if something wrong, so you can
25    /// leave everything about parsing to us :)
26    ///
27    /// # Note
28    /// If this crate is used on the kernel-side, you must first map the memory
29    /// that the slice points to before invoking this function.
30    pub fn init(buf: &'a [u8]) -> Result<Self> {
31        let header_raw = &buf[0..HEADER_SIZE]; // Header length
32        let header = unsafe { *(header_raw.as_ptr() as *const Header) };
33
34        // Check: Validate is this correct executable
35        if header.validate().is_err() {
36            return Err(Error::NotValidExecutable);
37        }
38
39        // Check: Is section count = 0?
40        if header.sections == 0 {
41            return Err(Error::NoSections);
42        }
43
44        // Check: Is the buffer contains all sections
45        let offset = HEADER_SIZE + (header.sections as usize - 1) * SECTION_INDEX_SIZE;
46        let index_content = &buf[offset..offset + SECTION_INDEX_SIZE];
47        let index = unsafe { *(index_content.as_ptr() as *const SectionIndex) };
48        let len = (index.base + index.name_len) as usize + SECTION_HDR_SIZE;
49        if buf.len() < len {
50            return Err(Error::ExecutableCorrupted);
51        }
52
53        // SAFETY: Already check all staff and able to do initialization
54        unsafe { Ok(Self::init_unchecked(buf)) }
55    }
56
57    /// Initialize the parser by passing a slice without checking.
58    ///
59    /// # Safety
60    /// You must ensure these if you invoke this function:
61    ///
62    ///  - The slice's content is a valid proka executable (match the magic);
63    ///  - The slice must contain the header and all section tables.
64    ///
65    /// # Note
66    /// Use this function to initialize is **NOT** recommended, because it might
67    /// cause some problems while parsing this header.
68    pub unsafe fn init_unchecked(buf: &'a [u8]) -> Self {
69        let header_raw = &buf[0..HEADER_SIZE];
70        let header = unsafe { *(header_raw.as_ptr() as *const Header) };
71
72        Self {
73            buf,
74            header,
75            total_sections: header.sections,
76        }
77    }
78
79    /// Do more validation after initialization.
80    ///
81    /// # Content
82    /// This will validates:
83    ///
84    ///  - Is the header min >= max;
85    ///  - Is each section's base correct;
86    ///  - Is the section's length not zeroed;
87    ///  - Is section base out of length;
88    ///  - Is entry_off is over than section length.
89    pub fn validate(&self) -> Result<()> {
90        // Check: Is header's min > max
91        let minimal = self.header.min;
92        let maximum = self.header.max;
93        for (&min, &max) in minimal.iter().zip(maximum.iter()) {
94            if min > max {
95                return Err(Error::VersionIncorrect(minimal, maximum));
96            }
97        }
98
99        // Check: Is each section's base and length correct (section check)
100        let min_base = HEADER_SIZE + self.header.sections as usize * SECTION_HDR_SIZE;
101        for (index, section_index) in self.sections().enumerate() {
102            let section = self.sections().get_hdr_secindex(section_index);
103            let base_off = section.base as usize;
104            let len = section.size as usize;
105            let entry_sec = self.header.entry_sec as usize;
106
107            // Check: Is section base in metadata range
108            if base_off < min_base {
109                return Err(Error::SectionError(SectionError::BaseError(
110                    base_off as u32,
111                )));
112            }
113
114            // Check: Is section length not zeroed
115            if len == 0 {
116                return Err(Error::SectionError(SectionError::LengthError));
117            }
118
119            // Check: Is section entry_off out of range
120            if index == entry_sec {
121                let entry_off = self.header.entry_off as usize;
122                if entry_off > len {
123                    return Err(Error::SectionError(SectionError::EntryOffsetOutOfRange(
124                        entry_off as u32,
125                        len as u32,
126                    )));
127                }
128            }
129        }
130
131        // All's fine :)
132        Ok(())
133    }
134
135    /// Get the content from specified sections.
136    ///
137    /// # Arguments
138    ///  - `secname`: The name of the section
139    ///
140    /// # Returns
141    /// `Option<&'static [u8]>`: The content of this section, return `None` if this section not exist.
142    pub fn get_section_content(&self, secname: &str) -> Option<&'a [u8]> {
143        // Iterate all sections...
144        for section_index in self.sections() {
145            let table = self.sections();
146            let name = table.get_name_secindex(section_index);
147            let section = table.get_hdr_secindex(section_index);
148            if secname == name {
149                // Get its base and length
150                let base = section.base as usize;
151                let length = section.size as usize;
152                let content = &self.buf[base..base + length];
153                return Some(content);
154            }
155        }
156
157        None
158    }
159
160    /// Get the header in this buffer.
161    #[inline]
162    pub fn header(&self) -> Header {
163        self.header
164    }
165
166    /// Get each section table.
167    pub fn sections(&self) -> SectionTable<'_> {
168        SectionTable::new(self.buf, self.total_sections)
169    }
170}