diff --git a/fugue-high/src/arch/arm.rs b/fugue-high/src/arch/arm.rs index 1ddbcc1..ebf0c5a 100644 --- a/fugue-high/src/arch/arm.rs +++ b/fugue-high/src/arch/arm.rs @@ -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, diff --git a/fugue-high/src/eval/mod.rs b/fugue-high/src/eval/mod.rs index 65b88a0..a770bf3 100644 --- a/fugue-high/src/eval/mod.rs +++ b/fugue-high/src/eval/mod.rs @@ -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)] @@ -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 { @@ -95,7 +97,11 @@ where } } - pub fn step(&mut self, operation: &PCodeData) -> Result { + pub fn step( + &mut self, + loc: Location, + operation: &PCodeData, + ) -> Result { match operation.opcode { Opcode::Copy => { let val = self.context.read_vnd(&operation.inputs[0])?; @@ -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(&mut self, operation: &PCodeData, op: F) -> Result<(), EvaluatorError> where F: FnOnce(BitVec, BitVec) -> Result, @@ -337,6 +403,11 @@ where self.assign(dst, val.cast(dst.size() * 8)) } + fn read_bool(&mut self, var: &VarnodeData) -> Result { + let val = self.context.read_vnd(var)?; + Ok(!val.is_zero()) + } + fn read_addr(&mut self, var: &VarnodeData) -> Result { bv2addr(self.context.read_vnd(var)?) } diff --git a/fugue-high/src/ir.rs b/fugue-high/src/ir.rs new file mode 100644 index 0000000..898d08a --- /dev/null +++ b/fugue-high/src/ir.rs @@ -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 for Location { + type Output = Self; + + fn add(self, rhs: u32) -> Self::Output { + Self { + position: self.position + rhs, + ..self + } + } +} + +impl Add 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
, 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
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 _ + } +} diff --git a/fugue-high/src/lib.rs b/fugue-high/src/lib.rs index c42c5a0..4f4e2de 100644 --- a/fugue-high/src/lib.rs +++ b/fugue-high/src/lib.rs @@ -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; diff --git a/fugue-high/src/lifter.rs b/fugue-high/src/lifter.rs index 5441033..8d3a6c9 100644 --- a/fugue-high/src/lifter.rs +++ b/fugue-high/src/lifter.rs @@ -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> { @@ -373,4 +318,3 @@ impl<'a> InsnLifter<'a> for DefaultInsnLifter { }) } } -