Skip to content

Commit

Permalink
refactor; implement branching operations in evaluator
Browse files Browse the repository at this point in the history
  • Loading branch information
xorpse committed Mar 13, 2024
1 parent cf8703d commit c141551
Show file tree
Hide file tree
Showing 5 changed files with 230 additions and 62 deletions.
3 changes: 2 additions & 1 deletion fugue-high/src/arch/arm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ pub use yaxpeax_arm::armv7::{
DecodeError as ARMDecoderError, InstDecoder as ARMInstDecoder, Instruction as ARMInstruction,
};

use crate::lifter::{InsnLifter, LiftedInsn, LiftedInsnProperties, Lifter, PCode};
use crate::ir::PCode;
use crate::lifter::{InsnLifter, LiftedInsn, LiftedInsnProperties, Lifter};

pub struct ARMInsnLifter {
decoder: ARMInstDecoder,
Expand Down
77 changes: 74 additions & 3 deletions fugue-high/src/eval/mod.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use fugue_bv::BitVec;

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

use thiserror::Error;

use crate::ir::Location;
use crate::lifter::Lifter;

#[derive(Debug, Error)]
Expand Down Expand Up @@ -65,10 +65,12 @@ where
translator: &'a Translator,
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum EvaluatorTarget {
Branch(Location),
Call(Location),
Fall,
Return(Location),
}

fn bv2addr(bv: BitVec) -> Result<Address, EvaluatorError> {
Expand All @@ -95,7 +97,11 @@ where
}
}

pub fn step(&mut self, operation: &PCodeData) -> Result<EvaluatorTarget, EvaluatorError> {
pub fn step(
&mut self,
loc: Location,
operation: &PCodeData,
) -> Result<EvaluatorTarget, EvaluatorError> {
match operation.opcode {
Opcode::Copy => {
let val = self.context.read_vnd(&operation.inputs[0])?;
Expand Down Expand Up @@ -240,12 +246,72 @@ where
Opcode::PopCount => self.lift_unsigned_int1(operation, |val| {
Ok(BitVec::from_u32(val.count_ones(), val.bits()))
})?,
Opcode::Subpiece => self.subpiece(operation)?,
Opcode::Branch => {
let locn =
Location::absolute_from(loc.address(), operation.inputs[0], loc.position());
return Ok(EvaluatorTarget::Branch(locn));
}
Opcode::CBranch => {
if self.read_bool(&operation.inputs[1])? {
let locn =
Location::absolute_from(loc.address(), operation.inputs[0], loc.position());
return Ok(EvaluatorTarget::Branch(locn));
}
}
Opcode::IBranch => {
let addr = self.read_addr(&operation.inputs[0])?;
return Ok(EvaluatorTarget::Branch(addr.into()));
}
Opcode::Call => {
let locn =
Location::absolute_from(loc.address(), operation.inputs[0], loc.position());
return Ok(EvaluatorTarget::Call(locn));
}
Opcode::ICall => {
let addr = self.read_addr(&operation.inputs[0])?;
return Ok(EvaluatorTarget::Call(addr.into()));
}
Opcode::Return => {
let addr = self.read_addr(&operation.inputs[0])?;
return Ok(EvaluatorTarget::Return(addr.into()));
}
op => return Err(EvaluatorError::Unsupported(op)),
}

Ok(EvaluatorTarget::Fall)
}

fn subpiece(&mut self, operation: &PCodeData) -> Result<(), EvaluatorError> {
let src = self.context.read_vnd(&operation.inputs[0])?;
let src_size = src.bits();

let off = operation.inputs[1].offset() as usize * 8;

let dst = operation.output.as_ref().unwrap();
let dst_size = dst.size() * 8;

let trun_size = src_size.saturating_sub(off);
let trun = if dst_size > trun_size {
// extract high + expand
if trun_size >= src_size {
src
} else {
src >> (src_size - trun_size) as u32
}
.unsigned()
.cast(trun_size)
.cast(dst_size)
} else {
// extract
if off > 0 { src >> off as u32 } else { src }
.unsigned()
.cast(dst_size)
};

self.assign(dst, trun)
}

fn lift_signed_int2<F>(&mut self, operation: &PCodeData, op: F) -> Result<(), EvaluatorError>
where
F: FnOnce(BitVec, BitVec) -> Result<BitVec, EvaluatorError>,
Expand Down Expand Up @@ -337,6 +403,11 @@ where
self.assign(dst, val.cast(dst.size() * 8))
}

fn read_bool(&mut self, var: &VarnodeData) -> Result<bool, EvaluatorError> {
let val = self.context.read_vnd(var)?;
Ok(!val.is_zero())
}

fn read_addr(&mut self, var: &VarnodeData) -> Result<Address, EvaluatorError> {
bv2addr(self.context.read_vnd(var)?)
}
Expand Down
151 changes: 151 additions & 0 deletions fugue-high/src/ir.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
use std::fmt;
use std::ops::Add;

use fugue_ir::disassembly::lift::{ArenaString, ArenaVec};
use fugue_ir::disassembly::PCodeData;
use fugue_ir::{Address, VarnodeData};

#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Location {
pub address: Address,
pub position: u32,
}

impl Default for Location {
fn default() -> Self {
Address::from(0u32).into()
}
}

impl fmt::Display for Location {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}.{}", self.address, self.position)
}
}

impl Add<u32> for Location {
type Output = Self;

fn add(self, rhs: u32) -> Self::Output {
Self {
position: self.position + rhs,
..self
}
}
}

impl Add<usize> for Location {
type Output = Self;

fn add(self, rhs: usize) -> Self::Output {
Self {
position: self.position + rhs as u32,
..self
}
}
}

impl Location {
pub fn new(address: impl Into<Address>, position: u32) -> Location {
Self {
address: address.into(),
position,
}
}

pub fn address(&self) -> Address {
self.address
}

pub fn position(&self) -> u32 {
self.position
}

pub(super) fn absolute_from(base: Address, address: VarnodeData, position: u32) -> Self {
if !address.space().is_constant() {
return Self::new(address.offset(), 0); // position);
}

let offset = address.offset() as i64;
let position = if offset.is_negative() {
position
.checked_sub(offset.abs() as u32)
.expect("negative offset from position in valid range")
} else {
position
.checked_add(offset as u32)
.expect("positive offset from position in valid range")
};

Self {
address: base.into(),
position,
}
}
}

impl From<Address> for Location {
fn from(address: Address) -> Self {
Self {
address,
position: 0,
}
}
}

#[derive(Debug)]
pub struct Insn<'a> {
pub address: Address,
pub mnemonic: ArenaString<'a>,
pub operands: ArenaString<'a>,
pub delay_slots: u8,
pub length: u8,
}

impl<'a> Insn<'a> {
pub fn address(&self) -> Address {
self.address
}

pub fn mnemonic(&self) -> &str {
&self.mnemonic
}

pub fn operands(&self) -> &str {
&self.operands
}

pub fn delay_slots(&self) -> usize {
self.delay_slots as _
}

pub fn len(&self) -> usize {
self.length as _
}
}

#[derive(Debug)]
pub struct PCode<'a> {
pub address: Address,
pub operations: ArenaVec<'a, PCodeData<'a>>,
pub delay_slots: u8,
pub length: u8,
}

impl<'a> PCode<'a> {
pub fn address(&self) -> Address {
self.address
}

pub fn operations(&self) -> &[PCodeData<'a>] {
&self.operations
}

pub fn delay_slots(&self) -> usize {
self.delay_slots as _
}

pub fn len(&self) -> usize {
self.length as _
}
}
1 change: 1 addition & 0 deletions fugue-high/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
pub mod arch;
pub mod eval;
pub mod icfg;
pub mod ir;
pub mod language;
pub mod lifter;
pub mod util;
60 changes: 2 additions & 58 deletions fugue-high/src/lifter.rs
Original file line number Diff line number Diff line change
@@ -1,69 +1,14 @@
use std::cell::{Cell, Ref, RefCell};
use std::mem;

use fugue_ir::disassembly::lift::{ArenaString, ArenaVec};
use fugue_ir::disassembly::lift::ArenaVec;
use fugue_ir::disassembly::{ContextDatabase, IRBuilderArena, PCodeData, ParserContext};
use fugue_ir::error::Error;
use fugue_ir::{Address, Translator};

use ouroboros::self_referencing;

#[derive(Debug)]
pub struct Insn<'a> {
pub address: Address,
pub mnemonic: ArenaString<'a>,
pub operands: ArenaString<'a>,
pub delay_slots: u8,
pub length: u8,
}

impl<'a> Insn<'a> {
pub fn address(&self) -> Address {
self.address
}

pub fn mnemonic(&self) -> &str {
&self.mnemonic
}

pub fn operands(&self) -> &str {
&self.operands
}

pub fn delay_slots(&self) -> usize {
self.delay_slots as _
}

pub fn len(&self) -> usize {
self.length as _
}
}

#[derive(Debug)]
pub struct PCode<'a> {
pub address: Address,
pub operations: ArenaVec<'a, PCodeData<'a>>,
pub delay_slots: u8,
pub length: u8,
}

impl<'a> PCode<'a> {
pub fn address(&self) -> Address {
self.address
}

pub fn operations(&self) -> &[PCodeData<'a>] {
&self.operations
}

pub fn delay_slots(&self) -> usize {
self.delay_slots as _
}

pub fn len(&self) -> usize {
self.length as _
}
}
use crate::ir::{Insn, PCode};

#[self_referencing]
struct LifterInner<'a> {
Expand Down Expand Up @@ -373,4 +318,3 @@ impl<'a> InsnLifter<'a> for DefaultInsnLifter {
})
}
}

0 comments on commit c141551

Please sign in to comment.