Skip to content

Commit

Permalink
add basic state impl
Browse files Browse the repository at this point in the history
  • Loading branch information
xorpse committed Mar 13, 2024
1 parent c141551 commit 1f19e41
Show file tree
Hide file tree
Showing 3 changed files with 286 additions and 7 deletions.
2 changes: 2 additions & 0 deletions fugue-high/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ version = "0.3.0"
edition = "2021"

[dependencies]
anyhow = "1"
bitflags = "2"
fugue-arch = { version = "0.3", path = "../fugue-arch" }
fugue-bv = { version = "0.3", path = "../fugue-bv" }
fugue-bytes = { version = "0.3", path = "../fugue-bytes" }
fugue-ir = { version = "0.3", path = "../fugue-ir" }
nom = "7"
ouroboros = "0.18"
Expand Down
207 changes: 207 additions & 0 deletions fugue-high/src/eval/fixed_state.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
use fugue_bv::BitVec;
use fugue_bytes::{Endian, Order};

use thiserror::Error;

#[derive(Debug, Error)]
pub enum FixedStateError {
#[error("out-of-bounds read access; {size} bytes at {offset:#x}")]
OOBRead { offset: usize, size: usize },
#[error("out-of-bounds write access; {size} bytes at {offset:#x}")]
OOBWrite { offset: usize, size: usize },
}

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[repr(transparent)]
pub struct FixedState {
pub(crate) backing: Box<[u8]>,
}

impl AsRef<Self> for FixedState {
#[inline(always)]
fn as_ref(&self) -> &Self {
self
}
}

impl AsMut<Self> for FixedState {
#[inline(always)]
fn as_mut(&mut self) -> &mut Self {
self
}
}

impl From<Vec<u8>> for FixedState {
fn from(backing: Vec<u8>) -> Self {
Self {
backing: backing.into_boxed_slice(),
}
}
}

impl FixedState {
pub fn new(size: usize) -> Self {
Self {
backing: vec![0u8; size].into_boxed_slice(),
}
}

#[inline(always)]
pub fn len(&self) -> usize {
self.backing.len()
}

#[inline(always)]
pub fn read_val<O: Order>(
&self,
offset: impl Into<usize>,
size: usize,
) -> Result<BitVec, FixedStateError> {
let view = self.view_bytes(offset, size)?;

Ok(if O::ENDIAN.is_big() {
BitVec::from_le_bytes(view)
} else {
BitVec::from_le_bytes(view)
})
}

#[inline(always)]
pub fn read_val_with(
&self,
offset: impl Into<usize>,
size: usize,
endian: Endian,
) -> Result<BitVec, FixedStateError> {
let view = self.view_bytes(offset, size)?;

Ok(if endian.is_big() {
BitVec::from_le_bytes(view)
} else {
BitVec::from_le_bytes(view)
})
}

#[inline(always)]
pub fn write_val<O: Order>(
&mut self,
offset: impl Into<usize>,
value: &BitVec,
) -> Result<(), FixedStateError> {
debug_assert_eq!(value.bits() % 8, 0);

let size = value.bits() / 8;
let view = self.view_bytes_mut(offset, size)?;

if O::ENDIAN.is_big() {
value.to_be_bytes(view)
} else {
value.to_le_bytes(view)
}

Ok(())
}

#[inline(always)]
pub fn write_val_with(
&mut self,
offset: impl Into<usize>,
value: &BitVec,
endian: Endian,
) -> Result<(), FixedStateError> {
debug_assert_eq!(value.bits() % 8, 0);

let size = value.bits() / 8;
let view = self.view_bytes_mut(offset, size)?;

if endian.is_big() {
value.to_be_bytes(view)
} else {
value.to_le_bytes(view)
}

Ok(())
}

#[inline(always)]
pub fn read_bytes(
&self,
offset: impl Into<usize>,
values: &mut [u8],
) -> Result<(), FixedStateError> {
let offset = offset.into();
let size = values.len();

let end = offset
.checked_add(size)
.ok_or(FixedStateError::OOBRead { offset, size })?;

if end > self.backing.len() {
return Err(FixedStateError::OOBRead { offset, size });
}

values[..].copy_from_slice(&self.backing[offset..end]);

Ok(())
}

#[inline(always)]
pub fn view_bytes(
&self,
offset: impl Into<usize>,
size: usize,
) -> Result<&[u8], FixedStateError> {
let offset = offset.into();

let end = offset
.checked_add(size)
.ok_or(FixedStateError::OOBRead { offset, size })?;

if end > self.backing.len() {
return Err(FixedStateError::OOBRead { offset, size });
}

Ok(&self.backing[offset..end])
}

#[inline(always)]
pub fn view_bytes_mut(
&mut self,
offset: impl Into<usize>,
size: usize,
) -> Result<&mut [u8], FixedStateError> {
let offset = offset.into();

let end = offset
.checked_add(size)
.ok_or(FixedStateError::OOBWrite { offset, size })?;

if end > self.backing.len() {
return Err(FixedStateError::OOBWrite { offset, size });
}

Ok(&mut self.backing[offset..end])
}

#[inline(always)]
pub fn write_bytes(
&mut self,
offset: impl Into<usize>,
values: &[u8],
) -> Result<(), FixedStateError> {
let offset = offset.into();
let size = values.len();

let end = offset
.checked_add(size)
.ok_or(FixedStateError::OOBWrite { offset, size })?;

if end > self.backing.len() {
return Err(FixedStateError::OOBWrite { offset, size });
}

self.backing[offset..end].copy_from_slice(values);

Ok(())
}
}
84 changes: 77 additions & 7 deletions fugue-high/src/eval/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use fugue_bv::BitVec;

use fugue_bytes::Endian;
use fugue_ir::disassembly::{Opcode, PCodeData};
use fugue_ir::{Address, AddressSpace, Translator, VarnodeData};

Expand All @@ -8,6 +9,9 @@ use thiserror::Error;
use crate::ir::Location;
use crate::lifter::Lifter;

pub mod fixed_state;
use self::fixed_state::FixedState;

#[derive(Debug, Error)]
pub enum EvaluatorError {
#[error("invalid address: {0:x}")]
Expand All @@ -16,39 +20,105 @@ pub enum EvaluatorError {
DivideByZero,
#[error("{0}")]
Lift(fugue_ir::error::Error),
#[error("{0}")]
State(anyhow::Error),
#[error("unsupported opcode: {0:?}")]
Unsupported(Opcode),
}

impl EvaluatorError {
pub fn state<E>(e: E) -> Self
where
E: std::error::Error + Send + Sync + 'static,
{
Self::State(anyhow::Error::new(e))
}

pub fn state_with<M>(msg: M) -> Self
where
M: std::fmt::Display + std::fmt::Debug + Send + Sync + 'static,
{
Self::State(anyhow::Error::msg(msg))
}
}

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

pub struct DummyContext;
pub struct DummyContext {
base: Address,
endian: Endian,
memory: fixed_state::FixedState,
registers: fixed_state::FixedState,
temporaries: fixed_state::FixedState,
}

impl DummyContext {
pub fn new(lifter: &Lifter, base: impl Into<Address>, size: usize) -> Self {
let t = lifter.translator();

Self {
base: base.into(),
endian: if t.is_big_endian() {
Endian::Big
} else {
Endian::Little
},
memory: FixedState::new(size),
registers: FixedState::new(t.register_space_size()),
temporaries: FixedState::new(t.unique_space_size()),
}
}

fn translate(&self, addr: u64) -> Result<usize, EvaluatorError> {
let addr = addr
.checked_sub(self.base.into())
.ok_or(EvaluatorError::state_with(
"address translation out-of-bounds",
))?;

Ok(addr as usize)
}
}

impl EvaluatorContext for DummyContext {
fn read_vnd(&mut self, var: &VarnodeData) -> Result<BitVec, EvaluatorError> {
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")
self.registers
.read_val_with(var.offset() as usize, var.size(), self.endian)
.map_err(EvaluatorError::state)
} else if spc.is_unique() {
todo!("read a temporary")
self.temporaries
.read_val_with(var.offset() as usize, var.size(), self.endian)
.map_err(EvaluatorError::state)
} else {
todo!("read memory")
let addr = self.translate(var.offset())?;
self.memory
.read_val_with(addr, var.size(), self.endian)
.map_err(EvaluatorError::state)
}
}

fn write_vnd(&mut self, var: &VarnodeData, val: &BitVec) -> Result<(), EvaluatorError> {
let spc = var.space();
if spc.is_register() {
todo!("write a register: {val}")
self.registers
.write_val_with(var.offset() as usize, val, self.endian)
.map_err(EvaluatorError::state)
} else if spc.is_unique() {
todo!("write a temporary: {val}")
self.temporaries
.write_val_with(var.offset() as usize, val, self.endian)
.map_err(EvaluatorError::state)
} else if spc.is_default() {
todo!("write memory: {val}")
let addr = self.translate(var.offset())?;
self.memory
.write_val_with(addr, val, self.endian)
.map_err(EvaluatorError::state)
} else {
panic!("cannot write to constant Varnode")
}
Expand Down

0 comments on commit 1f19e41

Please sign in to comment.