pub mod error;
pub mod item;
mod state;
pub use item::Item;
use crate::binary::{self, Binary};
use crate::config::{self, AddressMode, Features, Version};
use crate::instruction;
use crate::packet::payload::{InstructionTrace, Payload};
use crate::packet::sync;
use crate::packet::unit::IOptions;
use crate::types::{self, stack, trap};
use error::Error;
use instruction::info::Info;
use stack::ReturnStack;
pub struct Tracer<B, S = stack::NoStack, I = Option<instruction::Kind>>
where
B: Binary<I>,
S: ReturnStack,
I: Info,
{
state: state::State<S, I>,
iter_state: IterationState,
previous: Option<Event>,
binary: B,
address_mode: AddressMode,
phantom: core::marker::PhantomData<I>,
}
impl<B: Binary<I>, S: ReturnStack, I: Info + Clone + Default> Tracer<B, S, I> {
pub fn features(&self) -> Features {
self.state.features()
}
pub fn process_payload<D>(
&mut self,
payload: &Payload<impl IOptions, D>,
) -> Result<(), Error<B::Error>> {
match payload {
Payload::InstructionTrace(p) => self.process_te_inst(p),
_ => Ok(()),
}
}
pub fn process_te_inst<D>(
&mut self,
payload: &InstructionTrace<impl IOptions, D>,
) -> Result<(), Error<B::Error>> {
use state::StopCondition;
if let InstructionTrace::Synchronization(sync) = payload {
self.process_sync(sync)
} else {
let previous = self.previous.take();
let updiscon_prev = self.state.previous_insn().is_uninferable_discon();
let mut initer = self.state.initializer(&mut self.binary)?;
initer.set_stack_depth(payload.implicit_return_depth());
if let InstructionTrace::Branch(branch) = payload {
initer
.get_branch_map_mut()
.append(branch.branch_map)
.map_err(Error::CannotAddBranches)?;
}
let condition = if let Some(info) = payload.get_address_info() {
let notify = info.notify;
self.previous = Some(Event::Address { notify });
match self.address_mode {
AddressMode::Full => initer.set_address(0u64.wrapping_add_signed(info.address)),
AddressMode::Delta => initer.set_rel_address(info.address),
}
StopCondition::Address {
notify,
not_updiscon: !info.updiscon,
}
} else {
StopCondition::LastBranch
};
if !updiscon_prev && previous == Some(Event::Address { notify: false }) {
initer.set_inferred();
}
initer.set_condition(condition);
Ok(())
}
}
pub fn process_sync<D>(
&mut self,
sync: &sync::Synchronization<impl IOptions, D>,
) -> Result<(), Error<B::Error>> {
use sync::Synchronization;
let previous = self.previous.take();
match sync {
Synchronization::Start(start) => {
let is_tracing = self.iter_state.is_tracing();
let mut initer = self.sync_init(start.address, !is_tracing, !start.branch)?;
if is_tracing && previous != Some(Event::Trap { thaddr: false }) {
initer.set_condition(state::StopCondition::Sync {
context: start.ctx.into(),
});
} else {
initer.set_context(start.ctx.into());
initer.reset_to_address()?;
self.iter_state = IterationState::ContextItem {
pc: None,
context: start.ctx.into(),
follow_up: true,
};
}
}
Synchronization::Trap(trap) => {
let thaddr = trap.thaddr;
self.previous = Some(Event::Trap { thaddr });
let epc = if trap.info.is_exception()
&& previous != Some(Event::Trap { thaddr: false })
{
let epc = (!trap.thaddr).then_some(trap.address);
self.state.exception_address(&mut self.binary, epc)?
} else {
self.state.current_pc()
};
if !thaddr {
let mut initer = self.state.initializer(&mut self.binary)?;
initer.set_stack_depth(None);
initer.set_address(trap.address);
initer.reset_to_address()?;
} else {
let mut initer = self.sync_init(trap.address, false, !trap.branch)?;
initer.set_context(trap.ctx.into());
initer.reset_to_address()?;
}
self.iter_state = IterationState::TrapItem {
epc,
info: trap.info,
context: trap.ctx.into(),
follow_up: thaddr,
};
}
Synchronization::Context(ctx) => {
let mut initer = self.state.initializer(&mut self.binary)?;
initer.set_stack_depth(None);
initer.set_context(ctx.into());
self.iter_state = IterationState::ContextItem {
pc: None,
context: ctx.into(),
follow_up: false,
};
}
Synchronization::Support(sup) => {
self.process_support(sup)?;
}
}
Ok(())
}
pub fn process_support<D>(
&mut self,
support: &sync::Support<impl IOptions, D>,
) -> Result<(), Error<B::Error>> {
use sync::QualStatus;
self.previous = None;
let mut initer = self.state.initializer(&mut self.binary)?;
support
.ioptions
.update_features(initer.get_features_mut())
.map_err(Error::UnsupportedFeature)?;
if let Some(mode) = support.ioptions.address_mode() {
self.address_mode = mode;
}
initer.set_stack_depth(None);
if support.qual_status != QualStatus::NoChange {
self.iter_state = IterationState::Depleting;
if support.qual_status == QualStatus::EndedNtr && initer.update_inferred() {
initer.set_condition(state::StopCondition::NotInferred);
}
}
Ok(())
}
fn sync_init(
&mut self,
address: u64,
reset_branch_map: bool,
branch_taken: bool,
) -> Result<state::Initializer<'_, S, B, I>, Error<B::Error>> {
use instruction::info::Info;
let insn = self
.binary
.get_insn(address)
.map_err(|e| Error::CannotGetInstruction(e, address))?;
let mut initer = self.state.initializer(&mut self.binary)?;
initer.set_address(address);
let branch_map = initer.get_branch_map_mut();
if reset_branch_map {
*branch_map = Default::default();
}
if insn.is_branch() {
branch_map
.push_branch_taken(branch_taken)
.map_err(Error::CannotAddBranches)?;
}
initer.set_stack_depth(None);
Ok(initer)
}
}
impl<B: Binary<I>, S: ReturnStack, I: Info + Clone + Default> Iterator for Tracer<B, S, I> {
type Item = Result<Item<I>, Error<B::Error>>;
fn next(&mut self) -> Option<Self::Item> {
match self.iter_state {
IterationState::SingleItem => {
self.iter_state = IterationState::FollowExec;
Some(Ok(Item::new(
self.state.current_pc(),
self.state.current_insn().into(),
)))
}
IterationState::TrapItem {
epc,
info,
context,
follow_up,
} => {
let pc = (!follow_up).then_some(epc);
self.iter_state = IterationState::ContextItem {
pc,
context,
follow_up,
};
Some(Ok(Item::new(epc, info.into())))
}
IterationState::ContextItem {
pc,
context,
follow_up,
} => {
self.iter_state = if follow_up {
IterationState::SingleItem
} else {
IterationState::FollowExec
};
let pc = pc.unwrap_or(self.state.current_pc());
Some(Ok(Item::new(pc, context.into())))
}
IterationState::FollowExec | IterationState::Depleting => {
let res = self
.state
.next_item(&mut self.binary)
.transpose()?
.map(|(p, i, c)| {
if let Some(ctx) = c {
self.iter_state = IterationState::SingleItem;
Item::new(p, ctx.into())
} else {
Item::new(p, i.into())
}
});
Some(res)
}
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match self.iter_state {
IterationState::TrapItem { follow_up, .. } => {
let n = if follow_up { 2 } else { 1 };
(n, None)
}
IterationState::ContextItem { follow_up, .. } => {
let n = if follow_up { 2 } else { 1 };
(n, None)
}
IterationState::SingleItem => (1, Some(1)),
IterationState::FollowExec => (0, None),
IterationState::Depleting => (0, None),
}
}
}
pub fn builder() -> Builder<binary::Empty> {
Default::default()
}
#[derive(Copy, Clone)]
pub struct Builder<B = binary::Empty> {
binary: B,
max_stack_depth: usize,
features: Features,
address_mode: AddressMode,
address_width: core::num::NonZeroU8,
version: Version,
}
impl Builder<binary::Empty> {
pub fn new() -> Self {
Default::default()
}
}
impl<B> Builder<B> {
pub fn with_params(self, config: &config::Parameters) -> Self {
let max_stack_depth = if config.return_stack_size_p > 0 {
1 << config.return_stack_size_p
} else if config.call_counter_size_p > 0 {
1 << config.call_counter_size_p
} else {
0
};
Self {
max_stack_depth,
address_width: config.iaddress_width_p,
features: Features {
sequentially_inferred_jumps: config.sijump_p,
..self.features
},
..self
}
}
pub fn with_binary<C>(self, binary: C) -> Builder<C> {
Builder {
binary,
max_stack_depth: self.max_stack_depth,
address_mode: self.address_mode,
address_width: self.address_width,
features: self.features,
version: self.version,
}
}
pub fn with_address_mode(self, mode: AddressMode) -> Self {
Self {
address_mode: mode,
..self
}
}
pub fn with_implicit_return(self, implicit_returns: bool) -> Self {
Self {
features: Features {
implicit_returns,
..self.features
},
..self
}
}
pub fn with_version(self, version: Version) -> Self {
Self { version, ..self }
}
pub fn build<S, I>(self) -> Result<Tracer<B, S, I>, Error<B::Error>>
where
B: Binary<I>,
S: ReturnStack,
I: Info + Clone + Default,
{
let state = state::State::new(
S::new(self.max_stack_depth)
.ok_or(Error::CannotConstructIrStack(self.max_stack_depth))?,
self.address_width,
self.features,
);
Ok(Tracer {
state,
iter_state: Default::default(),
previous: Default::default(),
binary: self.binary,
address_mode: self.address_mode,
phantom: Default::default(),
})
}
}
impl<B: Default> Default for Builder<B> {
fn default() -> Self {
Self {
binary: Default::default(),
max_stack_depth: Default::default(),
features: Default::default(),
address_mode: Default::default(),
address_width: core::num::NonZeroU8::MIN,
version: Default::default(),
}
.with_params(&Default::default())
}
}
#[derive(Copy, Clone, Debug)]
enum IterationState {
SingleItem,
TrapItem {
epc: u64,
info: trap::Info,
context: types::Context,
follow_up: bool,
},
ContextItem {
pc: Option<u64>,
context: types::Context,
follow_up: bool,
},
FollowExec,
Depleting,
}
impl Default for IterationState {
fn default() -> Self {
Self::Depleting
}
}
impl IterationState {
pub fn is_tracing(&self) -> bool {
!matches!(self, Self::Depleting)
}
}
#[derive(Copy, Clone, Debug, PartialEq)]
enum Event {
Address {
notify: bool,
},
Trap {
thaddr: bool,
},
}