use core::num::NonZeroU8;
use crate::binary::Binary;
use crate::config::Features;
use crate::instruction::{self, Instruction};
use crate::types::{Context, Privilege, branch};
use super::error::Error;
use super::stack::ReturnStack;
use instruction::info::Info;
#[derive(Clone, Debug)]
pub struct State<S: ReturnStack, I: Info> {
pc: u64,
insn: Instruction<I>,
last_pc: u64,
last_insn: Instruction<I>,
address: u64,
branch_map: branch::Map,
stop_condition: StopCondition,
inferred_address: Option<u64>,
privilege: Privilege,
return_stack: S,
stack_depth: Option<usize>,
address_width: NonZeroU8,
features: Features,
}
impl<S: ReturnStack, I: Info + Clone> State<S, I> {
pub fn new(return_stack: S, address_width: NonZeroU8, features: Features) -> Self {
Self {
pc: 0,
insn: Info::ignored(),
last_pc: 0,
last_insn: Info::ignored(),
address: 0,
branch_map: Default::default(),
stop_condition: Default::default(),
inferred_address: Default::default(),
privilege: Default::default(),
return_stack,
stack_depth: Default::default(),
address_width,
features,
}
}
pub fn is_fused(&self) -> bool {
self.stop_condition == StopCondition::Fused
}
pub fn current_pc(&self) -> u64 {
self.pc
}
pub fn current_insn(&self) -> Instruction<I> {
self.insn.clone()
}
pub fn previous_insn(&self) -> &Instruction<I> {
&self.last_insn
}
pub fn next_item<B: Binary<I>>(
&mut self,
binary: &mut B,
) -> Result<Option<ProtoItem<I>>, Error<B::Error>> {
if self.is_fused() {
return Ok(None);
}
if let Some(address) = self.inferred_address {
let (pc, insn, end) = self
.next_pc(binary, address)
.inspect_err(|_| self.stop_condition = StopCondition::Fused)?;
if end {
self.inferred_address = None;
}
Ok(Some((pc, insn, None)))
} else if self.stop_condition == StopCondition::NotInferred {
self.stop_condition = StopCondition::Fused;
Ok(None)
} else {
let (pc, insn, end) = self
.next_pc(binary, self.address)
.inspect_err(|_| self.stop_condition = StopCondition::Fused)?;
let is_branch = self.insn.is_branch();
let branch_limit = if is_branch { 1 } else { 0 };
let hit_address_and_branch =
self.pc == self.address && self.branch_map.count() == branch_limit;
let ctx = match self.stop_condition {
StopCondition::LastBranch if self.branch_map.count() == 1 && is_branch => {
self.stop_condition = StopCondition::Fused;
None
}
StopCondition::Address { notify: true, .. } if hit_address_and_branch => {
self.stop_condition = StopCondition::Fused;
None
}
StopCondition::Address {
notify: false,
not_updiscon: true,
} if hit_address_and_branch
&& !self.last_insn.is_uninferable_discon()
&& self.stack_depth_matches() =>
{
self.inferred_address = Some(self.pc);
self.stop_condition = StopCondition::Fused;
None
}
StopCondition::Sync { context } if hit_address_and_branch => {
self.privilege = context.privilege;
self.stop_condition = StopCondition::Fused;
Some(context)
}
_ if end => {
self.stop_condition = StopCondition::Fused;
if let Some(n) = core::num::NonZeroU8::new(self.branch_map.count())
.filter(|n| n.get() > branch_limit)
{
return Err(Error::UnprocessedBranches(n));
}
None
}
_ => None,
};
Ok(Some((pc, insn, ctx)))
}
}
pub fn exception_address<B: Binary<I>>(
&mut self,
binary: &mut B,
packet_epc: Option<u64>,
) -> Result<u64, Error<B::Error>> {
if self.insn.is_uninferable_discon()
&& let Some(epc) = packet_epc
{
return Ok(epc);
}
if self.insn.is_ecall_or_ebreak() {
return Ok(self.pc);
}
let (pc, insn, end) = self.next_pc(binary, self.pc)?;
if end {
Ok(pc.wrapping_add(insn.size.into()))
} else {
Ok(pc)
}
}
pub fn initializer<'a, B: Binary<I>>(
&'a mut self,
binary: &'a mut B,
) -> Result<Initializer<'a, S, B, I>, Error<B::Error>> {
self.is_fused()
.then_some(Initializer {
state: self,
binary,
})
.ok_or(Error::UnprocessedInstructions)
}
pub fn features(&self) -> Features {
self.features
}
fn next_pc<B: Binary<I>>(
&mut self,
binary: &mut B,
address: u64,
) -> Result<(u64, Instruction<I>, bool), Error<B::Error>> {
let after_pc = self.pc.wrapping_add(self.insn.size.into());
let info = self.insn.info.clone();
let (mut next_pc, end) = self
.inferable_jump_target(&info)
.or_else(|| self.sequential_jump_target(&info).map(|t| (t, false)))
.or_else(|| self.implicit_return_address(&info).map(|t| (t, false)))
.map(Ok)
.or_else(|| {
info.is_uninferable_discon().then(|| {
(!matches!(self.stop_condition, StopCondition::LastBranch))
.then_some((address, true))
.ok_or(Error::UnexpectedUninferableDiscon)
})
})
.or_else(|| self.taken_branch_target(&info).transpose())
.transpose()?
.unwrap_or((after_pc, false));
next_pc &= !(u64::MAX
.checked_shl(self.address_width.get().into())
.unwrap_or(0));
if self.features.implicit_returns && self.insn.is_call() {
self.return_stack.push(after_pc);
}
self.last_pc = self.pc;
self.last_insn = self.insn.clone();
let insn = binary
.get_insn(next_pc)
.map_err(|e| Error::CannotGetInstruction(e, next_pc))?;
self.pc = next_pc;
self.insn = insn.clone();
Ok((next_pc, insn, end))
}
fn inferable_jump_target(&self, insn: &I) -> Option<(u64, bool)> {
insn.inferable_jump_target()
.map(|t| (self.pc.wrapping_add_signed(t.into()), t == 0))
}
fn sequential_jump_target(&self, insn: &I) -> Option<u64> {
if !self.features.sequentially_inferred_jumps {
return None;
}
let (reg, target) = self.last_insn.upper_immediate(self.last_pc)?;
let (dep, off) = insn.uninferable_jump_target()?;
(dep == reg).then_some(target.wrapping_add_signed(off.into()))
}
fn implicit_return_address(&mut self, insn: &I) -> Option<u64> {
if self.features.implicit_returns
&& insn.is_return()
&& self.stack_depth != Some(self.return_stack.depth())
{
self.return_stack.pop()
} else {
None
}
}
fn taken_branch_target<E>(&mut self, insn: &I) -> Result<Option<(u64, bool)>, Error<E>> {
let Some(target) = insn.branch_target() else {
return Ok(None);
};
let res = self
.branch_map
.pop_taken()
.ok_or(Error::UnresolvableBranch)?
.then_some((self.pc.wrapping_add_signed(target.into()), target == 0));
Ok(res)
}
fn stack_depth_matches(&self) -> bool {
self.stack_depth
.map(|d| d == self.return_stack.depth())
.unwrap_or(true)
}
}
type ProtoItem<I> = (u64, Instruction<I>, Option<Context>);
pub struct Initializer<'a, S: ReturnStack, B: Binary<I>, I: Info> {
state: &'a mut State<S, I>,
binary: &'a mut B,
}
impl<S: ReturnStack, B: Binary<I>, I: Info> Initializer<'_, S, B, I> {
pub fn set_address(&mut self, address: u64) {
self.state.address = address;
self.state.inferred_address = None;
}
pub fn set_rel_address(&mut self, address: i64) {
self.set_address(self.state.address.wrapping_add_signed(address));
}
pub fn set_inferred(&mut self) {
self.state.inferred_address = Some(self.state.pc);
}
pub fn update_inferred(&mut self) -> bool {
self.state
.inferred_address
.as_mut()
.map(|a| *a = self.state.pc)
.is_some()
}
pub fn get_branch_map_mut(&mut self) -> &mut branch::Map {
&mut self.state.branch_map
}
pub fn set_context(&mut self, context: Context) {
self.state.privilege = context.privilege;
}
pub fn set_stack_depth(&mut self, depth: Option<usize>) {
self.state.stack_depth = depth;
}
pub fn get_features_mut(&mut self) -> &mut Features {
&mut self.state.features
}
pub fn set_condition(self, condition: StopCondition) {
self.state.stop_condition = condition;
}
pub fn reset_to_address(self) -> Result<(), Error<B::Error>> {
let address = self.state.address;
let insn = self
.binary
.get_insn(address)
.map_err(|e| Error::CannotGetInstruction(e, address))?;
self.state.pc = address;
self.state.insn = insn;
self.state.last_pc = address;
self.state.last_insn = Info::ignored();
Ok(())
}
}
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq)]
pub enum StopCondition {
NotInferred,
LastBranch,
Address { notify: bool, not_updiscon: bool },
Sync { context: Context },
#[default]
Fused,
}