diff --git a/fugue-high/src/eval/mod.rs b/fugue-high/src/eval/mod.rs index 2053e1e..014e3ca 100644 --- a/fugue-high/src/eval/mod.rs +++ b/fugue-high/src/eval/mod.rs @@ -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; - fn write_vnd(&mut self, var: &VarnodeData, val: &BitVec) -> Result; - - fn fetch(&mut self, addr: Address) -> Result>, 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 { - todo!() - } - - fn write_vnd(&mut self, var: &VarnodeData, val: &BitVec) -> Result { - 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>, 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, - _marker: PhantomData &'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 { + 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 { + 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, - location: Location, - ) -> Result { - todo!() + Ok(EvaluatorTarget::Fall) } - pub fn step_from(&mut self, location: impl Into) -> 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)) } } diff --git a/fugue-high/src/lib.rs b/fugue-high/src/lib.rs index 97b97d4..c42c5a0 100644 --- a/fugue-high/src/lib.rs +++ b/fugue-high/src/lib.rs @@ -1,5 +1,5 @@ pub mod arch; -// pub mod eval; +pub mod eval; pub mod icfg; pub mod language; pub mod lifter; diff --git a/fugue-high/src/lifter.rs b/fugue-high/src/lifter.rs index a70b983..5441033 100644 --- a/fugue-high/src/lifter.rs +++ b/fugue-high/src/lifter.rs @@ -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();