#![no_std]
#[cfg(feature = "alloc")]
extern crate alloc;
pub mod header;
pub mod sections;
pub mod utils;
#[cfg(feature = "alloc")]
use alloc::{
string::{String, ToString},
vec::Vec,
};
use header::{ExecMode, Header};
use sections::{Section, SectionIter};
pub use utils::*;
pub const HEADER_SIZE: usize = core::mem::size_of::<Header>();
pub const SECTION_SIZE: usize = core::mem::size_of::<Section>();
#[derive(Debug, Clone, Copy)]
pub struct Parser<'a> {
buf: &'a [u8],
header: Header,
total_sections: u16,
}
impl<'a> Parser<'a> {
pub fn init(buf: &'a [u8]) -> Result<Self, Error> {
let header_raw = &buf[0..HEADER_SIZE]; let header = unsafe { *(header_raw.as_ptr() as *const Header) };
if !header.validate() {
return Err(Error::NotValidExecutable);
}
let len = HEADER_SIZE + header.sections as usize * SECTION_SIZE;
if buf.len() < len {
return Err(Error::ExecutableCorrupted);
}
unsafe { Ok(Self::init_unchecked(buf)) }
}
pub unsafe fn init_unchecked(buf: &'a [u8]) -> Self {
let header_raw = &buf[0..HEADER_SIZE];
let header = unsafe { *(header_raw.as_ptr() as *const Header) };
Self {
buf,
header,
total_sections: header.sections,
}
}
pub fn validate(&self) -> bool {
let minimal = self.header.min;
let maximum = self.header.max;
for (&min, &max) in minimal.iter().zip(maximum.iter()) {
if min > max {
return false;
}
}
let min_base = HEADER_SIZE + self.header.sections as usize * SECTION_SIZE;
for (index, section) in self.sections().enumerate() {
let base_off = section.base as usize;
let len = section.length as usize;
let entry_sec = self.header.entry_sec as usize;
if base_off < min_base
|| base_off + len > self.buf.len()
|| len == 0
|| !section.validate()
{
return false;
}
if index == entry_sec {
let entry_off = self.header.entry_off as usize;
if entry_off > len {
return false;
}
}
}
true
}
pub fn get_section_content(&self, secname: &str) -> Option<&'a [u8]> {
for section in self.sections() {
let name = section.name;
if str_to_array(secname) == name {
let base = section.base as usize;
let length = section.length as usize;
let content = &self.buf[base..base + length];
return Some(content);
}
}
None
}
#[inline]
pub fn header(&self) -> Header {
self.header
}
pub fn sections(&self) -> SectionIter<'_> {
SectionIter::new(self.buf, self.total_sections, 0)
}
}
#[derive(Debug, Clone)]
#[cfg(feature = "alloc")]
pub struct Builder<'a> {
min: [u16; 3],
max: [u16; 3],
entry: (u32, usize), author: String,
name: String,
mode: ExecMode,
sections: Vec<InnerSections<'a>>,
}
#[cfg(feature = "alloc")]
impl Default for Builder<'_> {
fn default() -> Self {
Self::new()
}
}
#[cfg(feature = "alloc")]
impl<'a> Builder<'a> {
pub fn new() -> Self {
Self {
min: [0; 3],
max: [0; 3],
entry: (0, 0),
author: String::new(),
name: String::new(),
mode: ExecMode::UserApp,
sections: Vec::new(),
}
}
pub fn set_author(&mut self, author: &str) {
self.author = author.to_string();
}
pub fn set_name(&mut self, name: &str) {
self.name = name.to_string();
}
pub fn set_mode(&mut self, mode: ExecMode) {
self.mode = mode;
}
pub fn set_min(&mut self, min: [u16; 3]) {
self.min = min;
}
pub fn set_max(&mut self, max: [u16; 3]) {
self.max = max;
}
pub fn append(
&mut self,
data: &'a [u8],
name: &str,
is_loadable: bool,
is_execable: bool,
entry: Option<u32>,
) -> Result<(), Error> {
if entry.is_some() && !(is_execable && is_loadable) {
return Err(Error::ExecutableCorrupted);
}
let section = InnerSections {
secinfo: Section {
name: str_to_array(name),
is_loadable,
is_execable,
base: 0, length: data.len() as u32,
_reserved: [0; 6],
},
data,
};
self.sections.push(section);
if let Some(ent_offset) = entry {
let sec_index = self.sections.len() - 1;
self.entry = (ent_offset, sec_index);
}
Ok(())
}
pub fn build(self) -> Result<Vec<u8>, Error> {
if self.sections.is_empty() {
return Err(Error::NoSections);
}
let mut data: Vec<u8> = Vec::new();
{
let header = Header {
min: self.min,
max: self.max,
entry_off: self.entry.0,
entry_sec: self.entry.1 as u16,
mode: self.mode,
author: str_to_array(self.author.as_str()),
name: str_to_array(self.name.as_str()),
sections: self.sections.len() as u16,
..Default::default()
}
.to_array();
data.extend_from_slice(&header);
}
let mut cnt = 0;
for section in &self.sections {
let mut secinfo = section.secinfo;
secinfo.base = (HEADER_SIZE + self.sections.len() * SECTION_SIZE + cnt) as u32;
data.extend_from_slice(&secinfo.to_array());
cnt += section.data.len();
}
for section in &self.sections {
data.extend_from_slice(section.data);
}
Ok(data)
}
}
#[derive(Debug, Clone, Copy)]
struct InnerSections<'a> {
pub secinfo: Section,
pub data: &'a [u8],
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Error {
NotValidExecutable,
ExecutableCorrupted,
UnknownCharacter,
ArgsTooLong,
NoSections,
}