use thiserror::Error;
pub mod v1;
pub mod v2;
pub mod v3;
#[macro_export]
macro_rules! read_binary {
($data: expr, $le: expr, $ty: ident, $offset: expr) => {{
let data_offset = $offset;
let mut data_bytes: [u8; core::mem::size_of::<$ty>()] = [0; core::mem::size_of::<$ty>()];
data_bytes.copy_from_slice(&$data[data_offset..data_offset + core::mem::size_of::<$ty>()]);
if $le {
$ty::from_le_bytes(data_bytes)
} else {
$ty::from_be_bytes(data_bytes)
}
}};
}
#[macro_export]
macro_rules! read_struct {
($struct: ident, $data: expr, $le: expr, $x: ident, $ty: ident) => {{ read_binary!($data, $le, $ty, core::mem::offset_of!($struct, $x)) }};
}
pub type SFrameResult<T> = core::result::Result<T, SFrameError>;
#[derive(Debug, Clone, Copy)]
#[allow(dead_code)]
pub enum SFrameSection<'a> {
V1(v1::SFrameSection<'a>),
V2(v2::SFrameSection<'a>),
V3(v3::SFrameSection<'a>),
}
const SFRAME_MAGIC: u16 = 0xdee2;
#[repr(C, packed)]
struct RawSFramePreamble {
magic: u16,
version: u8,
flags: u8,
}
impl<'a> SFrameSection<'a> {
pub fn to_string(&self) -> SFrameResult<String> {
match self {
SFrameSection::V1(sframe_section) => sframe_section.to_string(),
SFrameSection::V2(sframe_section) => sframe_section.to_string(),
SFrameSection::V3(sframe_section) => sframe_section.to_string(),
}
}
pub fn from(data: &'a [u8], section_base: u64) -> SFrameResult<SFrameSection<'a>> {
if data.len() < core::mem::size_of::<RawSFramePreamble>() {
return Err(SFrameError::UnexpectedEndOfData);
}
let magic_offset = core::mem::offset_of!(RawSFramePreamble, magic);
let mut magic_bytes: [u8; 2] = [0; 2];
magic_bytes.copy_from_slice(&data[magic_offset..magic_offset + 2]);
let magic_le = u16::from_le_bytes(magic_bytes);
if magic_le != SFRAME_MAGIC {
let magic_be = u16::from_be_bytes(magic_bytes);
if magic_be != SFRAME_MAGIC {
return Err(SFrameError::InvalidMagic);
}
}
let version_offset = core::mem::offset_of!(RawSFramePreamble, version);
let version = data[version_offset];
match version {
1 => Ok(SFrameSection::V1(v1::SFrameSection::from(
data,
section_base,
)?)),
2 => Ok(SFrameSection::V2(v2::SFrameSection::from(
data,
section_base,
)?)),
3 => Ok(SFrameSection::V3(v3::SFrameSection::from(
data,
section_base,
)?)),
_ => Err(SFrameError::UnsupportedVersion),
}
}
pub fn find_fde(&self, pc: u64) -> SFrameResult<Option<SFrameFDE>> {
match self {
SFrameSection::V1(sframe_section) => {
Ok(sframe_section.find_fde(pc)?.map(SFrameFDE::V1))
}
SFrameSection::V2(sframe_section) => {
Ok(sframe_section.find_fde(pc)?.map(SFrameFDE::V2))
}
SFrameSection::V3(sframe_section) => {
Ok(sframe_section.find_fde(pc)?.map(SFrameFDE::V3))
}
}
}
}
#[derive(Debug, Clone, Copy)]
#[allow(dead_code)]
pub enum SFrameFDE {
V1(v1::SFrameFDE),
V2(v2::SFrameFDE),
V3(v3::SFrameFDE),
}
impl SFrameFDE {
pub fn find_fre(
&self,
section: &SFrameSection<'_>,
pc: u64,
) -> SFrameResult<Option<SFrameFRE>> {
match (self, section) {
(SFrameFDE::V1(sframe_fde), SFrameSection::V1(sframe_section)) => {
Ok(sframe_fde.find_fre(sframe_section, pc)?.map(SFrameFRE::V1))
}
(SFrameFDE::V2(sframe_fde), SFrameSection::V2(sframe_section)) => {
Ok(sframe_fde.find_fre(sframe_section, pc)?.map(SFrameFRE::V2))
}
(SFrameFDE::V3(sframe_fde), SFrameSection::V3(sframe_section)) => {
Ok(sframe_fde.find_fre(sframe_section, pc)?.map(SFrameFRE::V3))
}
_ => Err(SFrameError::UnsupportedVersion),
}
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub enum SFrameFRE {
V1(v1::SFrameFRE),
V2(v2::SFrameFRE),
V3(v3::SFrameFRE),
}
impl SFrameFRE {
pub fn get_cfa_base_reg_id(&self) -> u8 {
match self {
SFrameFRE::V1(sframe_fre) => sframe_fre.info.get_cfa_base_reg_id(),
SFrameFRE::V2(sframe_fre) => sframe_fre.info.get_cfa_base_reg_id(),
SFrameFRE::V3(sframe_fre) => sframe_fre.info.get_cfa_base_reg_id(),
}
}
pub fn get_cfa_offset(&self, section: &SFrameSection<'_>) -> SFrameResult<Option<i32>> {
match (self, section) {
(SFrameFRE::V1(sframe_fre), SFrameSection::V1(sframe_section)) => {
Ok(sframe_fre.get_cfa_offset(sframe_section))
}
(SFrameFRE::V2(sframe_fre), SFrameSection::V2(sframe_section)) => {
Ok(sframe_fre.get_cfa_offset(sframe_section))
}
(SFrameFRE::V3(sframe_fre), SFrameSection::V3(sframe_section)) => {
Ok(sframe_fre.get_cfa_offset(sframe_section))
}
_ => Err(SFrameError::UnsupportedVersion),
}
}
pub fn get_ra_offset(&self, section: &SFrameSection<'_>) -> SFrameResult<Option<i32>> {
match (self, section) {
(SFrameFRE::V1(sframe_fre), SFrameSection::V1(sframe_section)) => {
Ok(sframe_fre.get_ra_offset(sframe_section))
}
(SFrameFRE::V2(sframe_fre), SFrameSection::V2(sframe_section)) => {
Ok(sframe_fre.get_ra_offset(sframe_section))
}
(SFrameFRE::V3(sframe_fre), SFrameSection::V3(sframe_section)) => {
Ok(sframe_fre.get_ra_offset(sframe_section))
}
_ => Err(SFrameError::UnsupportedVersion),
}
}
pub fn get_fp_offset(&self, section: &SFrameSection<'_>) -> SFrameResult<Option<i32>> {
match (self, section) {
(SFrameFRE::V1(sframe_fre), SFrameSection::V1(sframe_section)) => {
Ok(sframe_fre.get_fp_offset(sframe_section))
}
(SFrameFRE::V2(sframe_fre), SFrameSection::V2(sframe_section)) => {
Ok(sframe_fre.get_fp_offset(sframe_section))
}
(SFrameFRE::V3(sframe_fre), SFrameSection::V3(sframe_section)) => {
Ok(sframe_fre.get_fp_offset(sframe_section))
}
_ => Err(SFrameError::UnsupportedVersion),
}
}
}
#[derive(Error, Debug)]
pub enum SFrameError {
#[error("format error")]
Fmt(#[from] core::fmt::Error),
#[error("unexpected end of data")]
UnexpectedEndOfData,
#[error("invalid magic number")]
InvalidMagic,
#[error("unsupported version")]
UnsupportedVersion,
#[error("unsupported flags")]
UnsupportedFlags,
#[error("unsupported abi")]
UnsupportedABI,
#[error("unsupported fde type")]
UnsupportedFDEType,
#[error("unsupported fre type")]
UnsupportedFREType,
#[error("unsupported fre stack offset size")]
UnsupportedFREStackOffsetSize,
#[error("unsupported fre data word size")]
UnsupportedFREDataWordSize,
}