Skip to main content

proka_exec/utils/
builder.rs

1//! The builder for the proka executable.
2use super::str_to_array;
3use crate::header::{ExecMode, Header};
4use crate::sections::{SectionFlag, SectionHdr, SectionIndex};
5use crate::{Error, HEADER_SIZE, Result, SECTION_HDR_SIZE, SECTION_INDEX_SIZE};
6#[cfg(feature = "alloc")]
7use alloc::{
8    string::{String, ToString},
9    vec::Vec,
10};
11
12/// The builder of the proka executable.
13#[derive(Debug, Clone)]
14#[cfg(feature = "alloc")]
15pub struct Builder<'a> {
16    min: [u16; 3],
17    max: [u16; 3],
18    entry: (u32, usize), // (offset, index)
19    author: String,
20    name: String,
21    mode: ExecMode,
22    sections: Vec<InnerSections<'a>>,
23}
24
25#[cfg(feature = "alloc")]
26impl Default for Builder<'_> {
27    fn default() -> Self {
28        Self::new()
29    }
30}
31
32#[cfg(feature = "alloc")]
33impl<'a> Builder<'a> {
34    /// Create up a empty builder.
35    pub fn new() -> Self {
36        Self {
37            min: [0; 3],
38            max: [0; 3],
39            entry: (0, 0),
40            author: String::new(),
41            name: String::new(),
42            mode: ExecMode::UserApp,
43            sections: Vec::new(),
44        }
45    }
46
47    /// Set up the author.
48    ///
49    /// # Note
50    /// If the author that you provide is longer than 32,
51    /// it may truncated.
52    pub fn set_author(&mut self, author: &str) {
53        self.author = author.to_string();
54    }
55
56    /// Set up the program name.
57    ///
58    /// # Note
59    /// If the name that you provide is longer than 32,
60    /// it may truncated.
61    pub fn set_name(&mut self, name: &str) {
62        self.name = name.to_string();
63    }
64
65    /// Set the mode of this program.
66    pub fn set_mode(&mut self, mode: ExecMode) {
67        self.mode = mode;
68    }
69
70    /// Set the min version.
71    pub fn set_min(&mut self, min: [u16; 3]) {
72        self.min = min;
73    }
74
75    /// Set the max version.
76    pub fn set_max(&mut self, max: [u16; 3]) {
77        self.max = max;
78    }
79
80    /// Append a section and specify its name.
81    ///
82    /// # Arguments
83    ///  - `data`: The data that you want to append;
84    ///  - `name`: The section name;
85    ///  - `is_loadable`: Assign is this loadable section or not;
86    ///  - `is_execable`: Assign is this executable section or not;
87    ///  - `entry`: The offset of the entry point, pass `None` if no entry point.
88    ///
89    /// # Errors
90    /// This will return error once these happened:
91    ///  - Provide an entry address which is unloadable or unexecable;
92    ///
93    /// # Note
94    ///  - If you try to provide a name which is over than 16 bytes, it may truncated;
95    ///  - If you provide the entry offset for multiple times, once you invoke `build()`, it will
96    ///    use that latest set one.
97    pub fn append(
98        &mut self,
99        data: &'a [u8],
100        name: &'a str,
101        is_loadable: bool,
102        is_execable: bool,
103        entry: Option<u32>,
104    ) -> Result<()> {
105        // Check: Is entry is Some(...) within unloadable & unexecable
106        if entry.is_some() && !(is_execable && is_loadable) {
107            return Err(Error::ExecutableCorrupted);
108        }
109
110        let flag = match (is_loadable, is_execable) {
111            (true, true) => SectionFlag::LOADABLE | SectionFlag::EXECABLE,
112            (true, false) => SectionFlag::LOADABLE,
113            (false, true) => SectionFlag::EXECABLE,
114            (false, false) => SectionFlag::empty(),
115        };
116
117        let section = InnerSections {
118            secinfo: SectionHdr {
119                flag,
120                _pad1: [0; 3],
121                base: 0, // Will replace during building...
122                size: data.len() as u32,
123                _pad2: [0; 4],
124            },
125            secindex: SectionIndex {
126                base: 0, // Will replace during building...
127                name_len: name.len() as u32,
128            },
129            name,
130            data,
131        };
132        self.sections.push(section);
133
134        // Set entry if Some(...)...
135        if let Some(ent_offset) = entry {
136            let sec_index = self.sections.len() - 1;
137            self.entry = (ent_offset, sec_index);
138        }
139        Ok(())
140    }
141
142    /// Build the whole file to a valid exec format.
143    ///
144    /// Will return error if no section was appended.
145    pub fn build(self) -> Result<Vec<u8>> {
146        // Check: Is section list empty
147        if self.sections.is_empty() {
148            return Err(Error::NoSections);
149        }
150
151        // Check: Is min version lower than max version
152        for (&min, &max) in self.min.iter().zip(self.max.iter()) {
153            if min > max {
154                return Err(Error::VersionIncorrect(self.min, self.max));
155            }
156        }
157
158        // Create up a data...
159        let mut data: Vec<u8> = Vec::new();
160
161        // Then create up a header and push into data...
162        // Then create up a header and push into data...
163        {
164            let mut header = Header::default();
165            header.min = self.min;
166            header.max = self.max;
167            header.entry_off = self.entry.0;
168            header.entry_sec = self.entry.1 as u16;
169            header.mode = self.mode;
170            header.author = str_to_array(self.author.as_str());
171            header.name = str_to_array(self.name.as_str());
172            header.sections = self.sections.len() as u16;
173
174            let header_bytes = header.to_array();
175            data.extend_from_slice(&header_bytes);
176        }
177
178        // And section index...
179        let mut cnt = 0;
180        for section in &self.sections {
181            let mut secindex = section.secindex;
182            secindex.base = (HEADER_SIZE + self.sections.len() * SECTION_INDEX_SIZE + cnt) as u32;
183            data.extend_from_slice(&secindex.to_array());
184            cnt += SECTION_HDR_SIZE + secindex.name_len as usize;
185        }
186
187        // And each section info...
188        // Here we didn't empty the `cnt`, so that `cnt` is already store the whole section index.
189        // So that seems no something wrong in this calculation...
190        for section in &self.sections {
191            let mut secinfo = section.secinfo;
192
193            // Update base...
194            // Note: The `cnt` does not empty, which means that is already store the whole section index.
195            secinfo.base = (HEADER_SIZE + self.sections.len() * SECTION_INDEX_SIZE + cnt) as u32;
196
197            // Push...
198            data.extend_from_slice(&secinfo.to_array());
199            data.extend_from_slice(section.name.as_bytes());
200            cnt += section.data.len();
201        }
202
203        // And each section's data...
204        for section in &self.sections {
205            data.extend_from_slice(section.data);
206        }
207
208        // Return
209        Ok(data)
210    }
211}
212
213/// Internal section form.
214#[derive(Debug, Clone, Copy)]
215struct InnerSections<'a> {
216    pub secinfo: SectionHdr,
217    pub secindex: SectionIndex,
218    pub name: &'a str,
219    pub data: &'a [u8],
220}