Skip to content

Commit

Permalink
WIP single-step evaluator
Browse files Browse the repository at this point in the history
  • Loading branch information
xorpse committed Mar 9, 2024
1 parent 5767eed commit 37c8b66
Show file tree
Hide file tree
Showing 3 changed files with 97 additions and 51 deletions.
142 changes: 92 additions & 50 deletions fugue-high/src/eval/mod.rs
Original file line number Diff line number Diff line change
@@ -1,91 +1,133 @@
use std::borrow::Cow;
use std::marker::PhantomData;

use fugue_bv::BitVec;

use fugue_ir::disassembly::{IRBuilderArena, Opcode, PCodeData};
use fugue_ir::disassembly::{Opcode, PCodeData};
use fugue_ir::il::Location;
use fugue_ir::{Address, VarnodeData};
use fugue_ir::{Address, AddressSpace, Translator, VarnodeData};

use thiserror::Error;

use crate::lifter::{Lifter, PCode};
use crate::lifter::Lifter;

#[derive(Debug, Error)]
pub enum EvaluatorError {
#[error("invalid address: {0:x}")]
Address(BitVec),
#[error("{0}")]
Lift(fugue_ir::error::Error),
#[error("unsupported opcode: {0:?}")]
Unsupported(Opcode),
}

pub trait EvaluatorContext<'ir> {
pub trait EvaluatorContext {
fn read_vnd(&mut self, var: &VarnodeData) -> Result<BitVec, EvaluatorError>;
fn write_vnd(&mut self, var: &VarnodeData, val: &BitVec) -> Result<BitVec, EvaluatorError>;

fn fetch(&mut self, addr: Address) -> Result<Vec<PCodeData<'ir>>, EvaluatorError>;
fn write_vnd(&mut self, var: &VarnodeData, val: &BitVec) -> Result<(), EvaluatorError>;
}

pub struct DummyContext<'a, 'ir> {
lifter: Lifter<'a>,
irb: &'ir IRBuilderArena,
}
pub struct DummyContext;

impl<'a, 'ir> EvaluatorContext<'ir> for DummyContext<'a, 'ir> {
impl EvaluatorContext for DummyContext {
fn read_vnd(&mut self, var: &VarnodeData) -> Result<BitVec, EvaluatorError> {
todo!()
}

fn write_vnd(&mut self, var: &VarnodeData, val: &BitVec) -> Result<BitVec, EvaluatorError> {
todo!()
let spc = var.space();
if spc.is_constant() {
Ok(BitVec::from_u64(var.offset(), var.size() * 8))
} else if spc.is_register() {
todo!("read a register")
} else if spc.is_unique() {
todo!("read a temporary")
} else {
todo!("read memory")
}
}

fn fetch(&mut self, addr: Address) -> Result<Vec<PCodeData<'ir>>, EvaluatorError> {
self.lifter
.lift(self.irb, addr, &[])
.map_err(EvaluatorError::Lift)
fn write_vnd(&mut self, var: &VarnodeData, val: &BitVec) -> Result<(), EvaluatorError> {
let spc = var.space();
if spc.is_register() {
todo!("write a register: {val}")
} else if spc.is_unique() {
todo!("write a temporary: {val}")
} else if spc.is_default() {
todo!("write memory: {val}")
} else {
panic!("cannot write to constant Varnode")
}
}
}

pub struct Evaluator<'a, 'ir, C>
pub struct Evaluator<'a, 'b, C>
where
C: EvaluatorContext<'ir>,
C: EvaluatorContext,
{
context: &'a mut C,
step_state: Option<StepState>,
_marker: PhantomData<fn() -> &'ir [PCodeData<'ir>]>,
context: &'b mut C,
default_space: &'a AddressSpace,
translator: &'a Translator,
}

struct StepState {
location: Location,
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum EvaluatorTarget {
Branch(Location),
Fall,
}

impl<'a, 'ir, C> Evaluator<'a, 'ir, C>
fn bv2addr(bv: BitVec) -> Result<Address, EvaluatorError> {
bv.to_u64()
.map(Address::from)
.ok_or_else(|| EvaluatorError::Address(bv))
}

impl<'a, 'b, C> Evaluator<'a, 'b, C>
where
C: EvaluatorContext<'ir>,
C: EvaluatorContext,
{
pub fn new(context: &'a mut C) -> Self {
pub fn new(lifter: &'a Lifter, context: &'b mut C) -> Self {
let translator = lifter.translator();
let spaces = translator.manager();
Self {
context,
step_state: None,
default_space: spaces.default_space_ref(),
translator,
}
}

fn fetch_next(data: PCodeData) {}
pub fn step(
&mut self,
operation: &PCodeData,
) -> Result<EvaluatorTarget, EvaluatorError> {
match operation.opcode {
Opcode::Copy => {
let val = self.context.read_vnd(&operation.inputs[0])?;
self.assign(operation.output.as_ref().unwrap(), val)?;
}
Opcode::Load => {
let dst = operation.output.as_ref().unwrap();
let src = &operation.inputs[1];
let lsz = dst.size();

let loc = bv2addr(self.context.read_vnd(src)?)?;
let mem = VarnodeData::new(self.default_space, loc.offset(), lsz);

let val = self.context.read_vnd(&mem)?;

self.assign(dst, val)?;
}
Opcode::Store => {
let dst = &operation.inputs[1];
let src = &operation.inputs[2];
let ssz = src.size();

let loc = bv2addr(self.context.read_vnd(dst)?)?;
let mem = VarnodeData::new(self.default_space, loc.offset(), ssz);

let val = self.context.read_vnd(&src)?;

self.assign(&mem, val)?;
}
op => { return Err(EvaluatorError::Unsupported(op)) },
}

fn fetch_operation(
context: &mut C,
step_state: &mut Option<StepState>,
location: Location,
) -> Result<Opcode, EvaluatorError> {
todo!()
Ok(EvaluatorTarget::Fall)
}

pub fn step_from(&mut self, location: impl Into<Location>) -> Result<(), EvaluatorError> {
let location = location.into();

// 1. obtain next operation

// 2. evaluate the operation

Ok(())
fn assign(&mut self, var: &VarnodeData, val: BitVec) -> Result<(), EvaluatorError> {
self.context.write_vnd(var, &val.cast(var.size() * 8))
}
}
2 changes: 1 addition & 1 deletion fugue-high/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
pub mod arch;
// pub mod eval;
pub mod eval;
pub mod icfg;
pub mod language;
pub mod lifter;
Expand Down
4 changes: 4 additions & 0 deletions fugue-high/src/lifter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,10 @@ impl<'a> Lifter<'a> {
})
}

pub fn translator(&self) -> &Translator {
self.0.borrow_translator()
}

pub fn reset(&mut self) {
let translator = *self.0.borrow_translator();
let mut ctx = translator.context_database();
Expand Down

0 comments on commit 37c8b66

Please sign in to comment.