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}