#![no_std]
extern crate alloc;
use alloc::vec;
use alloc::vec::Vec;
use thiserror::Error;
mod actions;
mod extab;
mod mem_utils;
pub use actions::{ExAction, ExActionData, ExceptionAction, PCAction, Relocation};
pub use extab::{ExceptionTableData};
#[derive(Error, Debug)]
pub enum ExtabDecodeError {
#[error("Data array should at least be 8 bytes long. Given array is {0} bytes long.")]
ArrayTooSmall(u32),
#[error("Invalid action value {0} at offset 0x{1:X}")]
InvalidActionValue(u32, u32),
#[error("Table is 8 bytes long but terminator is not zero.")]
InvalidSmallTableTerminator,
#[error("Action at offset 0x{0:X} goes up to index {1}, but table data is only {2} bytes long")]
InvalidActionOffset(u32, u32, u32),
#[error("Internal error")]
Internal,
}
struct ExtabDecoder {
extab_data: ExceptionTableData,
offset: i32,
data: Vec<u8>,
length: i32,
}
impl ExtabDecoder {
fn new() -> Self {
Self {
extab_data: ExceptionTableData::new(),
offset: 0,
data: vec![],
length: 0,
}
}
fn parse_exception_table(&mut self, bytes: &[u8]) -> Result<(), ExtabDecodeError> {
self.offset = 0;
self.data = Vec::from(bytes);
self.length = self.data.len() as i32;
if self.length < 8 {
return Err(ExtabDecodeError::ArrayTooSmall(self.length as u32));
}
self.extab_data.flag_val = mem_utils::read_uint16(&self.data, &mut self.offset, true);
self.extab_data.calculate_flag_values();
self.extab_data.et_field = mem_utils::read_uint16(&self.data, &mut self.offset, true);
let terminator = mem_utils::read_uint32(&self.data, &mut self.offset, false);
if self.length == 8 && terminator != 0 {
return Err(ExtabDecodeError::InvalidSmallTableTerminator);
}
while mem_utils::read_uint32(&self.data, &mut self.offset, false) != 0 {
let mut pcaction = PCAction::new();
pcaction.start_pc = mem_utils::read_uint32(&self.data, &mut self.offset, true);
let range_size: u32 =
(mem_utils::read_uint16(&self.data, &mut self.offset, true) as u32) * 4; pcaction.end_pc = pcaction.start_pc + range_size;
pcaction.action_offset =
mem_utils::read_uint16(&self.data, &mut self.offset, true) as u32;
self.extab_data.pc_actions.push(pcaction);
}
self.offset += 4;
while self.offset < self.length {
self.parse_action_entry()?;
}
Ok(())
}
fn parse_action_entry(&mut self) -> Result<(), ExtabDecodeError> {
let mut exaction = ExceptionAction::new();
exaction.action_offset = self.offset as u32;
let action_type_byte = mem_utils::read_byte(&self.data, &mut self.offset, true);
exaction.has_end_bit = (action_type_byte & 0x80) != 0;
let action_type_value: u32 = (action_type_byte & 0x7F) as u32;
let result = ExAction::from_int(action_type_value as i32);
exaction.action_type = match result {
Some(action) => action,
None => {
return Err(ExtabDecodeError::InvalidActionValue(
action_type_value,
exaction.action_offset,
))
}
};
exaction.action_param = mem_utils::read_byte(&self.data, &mut self.offset, true);
let mut size: i32;
match exaction.action_type {
ExAction::EndOfList => {
size = 0;
}
ExAction::Branch => {
size = 2;
}
ExAction::DestroyLocal => {
size = 6;
}
ExAction::DestroyLocalCond => {
size = 10;
}
ExAction::DestroyLocalPointer => {
size = 6;
}
ExAction::DestroyLocalArray => {
size = 10;
}
ExAction::DestroyBase | ExAction::DestroyMember => {
size = 10;
}
ExAction::DestroyMemberCond => {
size = 14;
}
ExAction::DestroyMemberArray => {
size = 18;
}
ExAction::DeletePointer => {
size = 6;
}
ExAction::DeletePointerCond => {
size = 10;
}
ExAction::CatchBlock => {
size = 10;
}
ExAction::ActiveCatchBlock => {
size = 2;
}
ExAction::Terminate => {
size = 0;
}
ExAction::Specification => {
size = 10;
let length = mem_utils::read_uint16(&self.data, &mut self.offset, false) as i32;
size += length * 4;
}
ExAction::CatchBlock32 => {
size = 14;
}
}
let start_index: usize = self.offset as usize;
let end_index: usize = start_index + (size as usize);
let extab_length: usize = self.data.len();
if size > 0 && (start_index >= extab_length || end_index > extab_length) {
return Err(ExtabDecodeError::InvalidActionOffset(
exaction.action_offset as u32,
end_index as u32,
extab_length as u32
));
}
exaction.bytes = self.data[start_index..end_index].into();
self.offset += size;
if exaction.has_dtor_ref() {
let (offset, addr) = match exaction.get_dtor_relocation() {
Some(val) => val,
None => {
return Err(ExtabDecodeError::Internal);
}
};
let reloc_offset: u32 = (start_index as u32) + offset;
let reloc = Relocation { offset: reloc_offset, address: addr };
self.extab_data.relocations.push(reloc);
}
self.extab_data.exception_actions.push(exaction);
Ok(())
}
}
pub fn decode_extab(data: &[u8]) -> Result<ExceptionTableData, ExtabDecodeError> {
let mut decoder = ExtabDecoder::new();
decoder.parse_exception_table(data)?;
Ok(decoder.extab_data)
}