#![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::HeaderError;
use header::{ExecMode, Header};
use sections::{SectionError, SectionHdr, SectionIndex, SectionTable};
pub use utils::*;
#[cfg(feature = "alloc")]
use crate::sections::SectionFlag;
pub type Result<T> = core::result::Result<T, Error>;
pub const HEADER_SIZE: usize = core::mem::size_of::<Header>();
pub const SECTION_HDR_SIZE: usize = core::mem::size_of::<SectionHdr>();
pub const SECTION_INDEX_SIZE: usize = core::mem::size_of::<SectionIndex>();
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Error {
SectionError(SectionError),
HeaderError(HeaderError),
NotValidExecutable,
ExecutableCorrupted,
VersionIncorrect([u16; 3], [u16; 3]),
UnknownCharacter,
NoSections,
}
#[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> {
let header_raw = &buf[0..HEADER_SIZE]; let header = unsafe { *(header_raw.as_ptr() as *const Header) };
if header.validate().is_err() {
return Err(Error::NotValidExecutable);
}
if header.sections == 0 {
return Err(Error::NoSections);
}
let offset = HEADER_SIZE + (header.sections as usize - 1) * SECTION_INDEX_SIZE;
let index_content = &buf[offset..offset + SECTION_INDEX_SIZE];
let index = unsafe { *(index_content.as_ptr() as *const SectionIndex) };
let len = (index.base + index.name_len) as usize + SECTION_HDR_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) -> Result<()> {
let minimal = self.header.min;
let maximum = self.header.max;
for (&min, &max) in minimal.iter().zip(maximum.iter()) {
if min > max {
return Err(Error::VersionIncorrect(minimal, maximum));
}
}
let min_base = HEADER_SIZE + self.header.sections as usize * SECTION_HDR_SIZE;
for (index, section_index) in self.sections().enumerate() {
let section = self.sections().get_hdr_secindex(section_index);
let base_off = section.base as usize;
let len = section.size as usize;
let entry_sec = self.header.entry_sec as usize;
if base_off < min_base {
return Err(Error::SectionError(SectionError::BaseError(
base_off as u32,
)));
}
if len == 0 {
return Err(Error::SectionError(SectionError::LengthError));
}
if index == entry_sec {
let entry_off = self.header.entry_off as usize;
if entry_off > len {
return Err(Error::SectionError(SectionError::EntryOffsetOutOfRange(
entry_off as u32,
len as u32,
)));
}
}
}
Ok(())
}
pub fn get_section_content(&self, secname: &str) -> Option<&'a [u8]> {
for section_index in self.sections() {
let table = self.sections();
let name = table.get_name_secindex(section_index);
let section = table.get_hdr_secindex(section_index);
if secname == name {
let base = section.base as usize;
let length = section.size 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) -> SectionTable<'_> {
SectionTable::new(self.buf, self.total_sections)
}
}
#[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: &'a str,
is_loadable: bool,
is_execable: bool,
entry: Option<u32>,
) -> Result<()> {
if entry.is_some() && !(is_execable && is_loadable) {
return Err(Error::ExecutableCorrupted);
}
let flag = match (is_loadable, is_execable) {
(true, true) => SectionFlag::LOADABLE | SectionFlag::EXECABLE,
(true, false) => SectionFlag::LOADABLE,
(false, true) => SectionFlag::EXECABLE,
(false, false) => SectionFlag::empty(),
};
let section = InnerSections {
secinfo: SectionHdr {
flag,
_pad1: [0; 3],
base: 0, size: data.len() as u32,
_pad2: [0; 4],
},
secindex: SectionIndex {
base: 0, name_len: name.len() as u32,
},
name,
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>> {
if self.sections.is_empty() {
return Err(Error::NoSections);
}
for (&min, &max) in self.min.iter().zip(self.max.iter()) {
if min > max {
return Err(Error::VersionIncorrect(self.min, self.max));
}
}
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 secindex = section.secindex;
secindex.base = (HEADER_SIZE + self.sections.len() * SECTION_INDEX_SIZE + cnt) as u32;
data.extend_from_slice(&secindex.to_array());
cnt += SECTION_HDR_SIZE + secindex.name_len as usize;
}
for section in &self.sections {
let mut secinfo = section.secinfo;
secinfo.base = (HEADER_SIZE + self.sections.len() * SECTION_INDEX_SIZE + cnt) as u32;
data.extend_from_slice(&secinfo.to_array());
data.extend_from_slice(section.name.as_bytes());
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: SectionHdr,
pub secindex: SectionIndex,
pub name: &'a str,
pub data: &'a [u8],
}