├── .github └── workflows │ └── build-and-test.yml ├── .gitignore ├── Cargo.toml ├── LICENSE ├── MAINTAINERS.md ├── README.md ├── fuguex-concrete ├── Cargo.toml └── src │ ├── driver.rs │ ├── hooks.rs │ ├── interpreter.rs │ ├── lib.rs │ ├── microx.rs │ └── tracker.rs ├── fuguex-core ├── Cargo.toml └── src │ └── lib.rs ├── fuguex-hooks ├── Cargo.toml └── src │ ├── lib.rs │ └── types.rs ├── fuguex-intrinsics ├── Cargo.toml └── src │ └── lib.rs ├── fuguex-loader ├── Cargo.toml └── src │ └── lib.rs ├── fuguex-machine ├── Cargo.toml └── src │ ├── lib.rs │ ├── machine.rs │ ├── traits.rs │ └── types.rs ├── fuguex-microx ├── Cargo.toml └── src │ ├── lib.rs │ ├── traits.rs │ └── types.rs ├── fuguex-state-derive ├── Cargo.toml └── src │ └── lib.rs └── fuguex-state ├── Cargo.toml └── src ├── chunked.rs ├── flat.rs ├── lib.rs ├── paged.rs ├── pcode.rs ├── register.rs ├── traits.rs └── unique.rs /.github/workflows/build-and-test.yml: -------------------------------------------------------------------------------- 1 | name: build-and-test 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | build: 14 | runs-on: ubuntu-latest 15 | steps: 16 | - name: install dependencies 17 | run: | 18 | sudo apt-get update -y 19 | sudo apt-get install -y build-essential cmake openssh-client 20 | 21 | - uses: webfactory/ssh-agent@v0.5.3 22 | with: 23 | ssh-private-key: ${{ secrets.FUGUE_REGISTRY_KEY }} 24 | 25 | - uses: actions/checkout@v2 26 | with: 27 | submodules: 'recursive' 28 | 29 | - name: add registry token 30 | run: | 31 | mkdir -p ~/.cargo 32 | echo '[registries]' >> ~/.cargo/config.toml 33 | echo 'fugue = { index = "ssh://git@github.com/fugue-re/fugue-oss-registry" }' >> ~/.cargo/config.toml 34 | 35 | - name: build 36 | run: cargo build --verbose 37 | - name: test 38 | run: cargo test --verbose 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.sublime-project 2 | *.sublime-workspace 3 | **/build/** 4 | **/_build/** 5 | **/*.egg-info/* 6 | **/*/__pycache__/* 7 | **/target/** 8 | */**/target/** 9 | */Cargo.lock 10 | **/*.class 11 | .idea 12 | .vscode 13 | **/.vscode 14 | Cargo.lock 15 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "fuguex-concrete", 4 | 5 | "fuguex-hooks", 6 | "fuguex-intrinsics", 7 | "fuguex-loader", 8 | "fuguex-machine", 9 | "fuguex-microx", 10 | "fuguex-state", 11 | "fuguex-state-derive", 12 | 13 | "fuguex-core", 14 | ] 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2022 Fugue Binary Analysis Framework 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | | Github ID | Name | Email | 2 | |----------------------------------------|---------------|------------------| 3 | | [@xorpse](https://github.com/xorpse) | Sam L. Thomas | | 4 | | [@zt-chen](https://github.com/zt-chen) | Zitai Chen | | 5 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

2 | Fugue logo 3 |

4 | 5 | 6 | 7 | [![DOI](https://zenodo.org/badge/386729787.svg)](https://zenodo.org/badge/latestdoi/386729787) 8 | 9 | 10 | # Fugue Binary Analysis Framework 11 | 12 | Fugue is a binary analysis framework in the spirit of [B2R2] and [BAP], with 13 | a focus on providing reusable components to rapidly prototype new binary 14 | analysis tools and techniques. 15 | 16 | This collection of crates, i.e., `fuguex-core` can be used to build 17 | custom interpreters. The `fuguex-concrete` crate provides a basic interpreter 18 | based on Micro execution [microx], that can be customised with user-defined 19 | hooks, intrinsic handlers, and execution strategies. 20 | 21 | [BAP]: https://github.com/BinaryAnalysisPlatform/bap/ 22 | [B2R2]: https://github.com/B2R2-org/B2R2 23 | [microx]: https://patricegodefroid.github.io/public_psfiles/icse2014.pdf 24 | -------------------------------------------------------------------------------- /fuguex-concrete/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fuguex-concrete" 3 | version = "0.2.20" 4 | edition = "2018" 5 | license = "MIT" 6 | 7 | [dependencies] 8 | downcast-rs = "1" 9 | dyn-clone = "1" 10 | fnv = "1" 11 | fugue = { version = "0.2", registry = "fugue" } 12 | fuguex-hooks = { path = "../fuguex-hooks", version = "0.2", registry = "fugue" } 13 | fuguex-intrinsics = { path = "../fuguex-intrinsics", version = "0.2", registry = "fugue" } 14 | fuguex-loader = { path = "../fuguex-loader", version = "0.2", registry = "fugue" } 15 | fuguex-machine = { path = "../fuguex-machine", version = "0.2", registry = "fugue" } 16 | fuguex-microx = { path = "../fuguex-microx", version = "0.1", registry = "fugue" } 17 | fuguex-state = { path = "../fuguex-state", version = "0.2", registry = "fugue" } 18 | log = "0.4" 19 | parking_lot = "0.11" 20 | rand = { version = "0.8", features = ["small_rng"] } 21 | thiserror = "1" 22 | -------------------------------------------------------------------------------- /fuguex-concrete/src/driver.rs: -------------------------------------------------------------------------------- 1 | use fugue::bytes::Order; 2 | use fugue::ir::il::pcode::Operand; 3 | use fugue::ir::Address; 4 | use fuguex_hooks::types::{Error, HookCBranchAction, HookOutcome, HookStepAction}; 5 | use fuguex_machine::StepState; 6 | use fuguex_state::pcode::{Error as PCodeError, PCodeState}; 7 | use fuguex_state::{AsState, StateOps}; 8 | 9 | use crate::hooks::{ClonableHookConcrete, HookConcrete}; 10 | 11 | use rand::rngs::SmallRng; 12 | use rand::{Rng, SeedableRng}; 13 | 14 | use std::collections::VecDeque; 15 | use std::iter::FromIterator; 16 | use std::marker::PhantomData; 17 | 18 | pub struct PathWalker { 19 | next: Address, 20 | walk: VecDeque<(Address, Address)>, 21 | marker: PhantomData<(S, O, R)>, 22 | } 23 | 24 | impl Default for PathWalker { 25 | fn default() -> Self { 26 | Self { 27 | next: Address::from_value(0u64), 28 | walk: VecDeque::default(), 29 | marker: PhantomData, 30 | } 31 | } 32 | } 33 | 34 | impl Clone for PathWalker { 35 | fn clone(&self) -> Self { 36 | Self { 37 | next: self.next.clone(), 38 | walk: self.walk.clone(), 39 | marker: PhantomData, 40 | } 41 | } 42 | } 43 | 44 | impl FromIterator<(Address, Address)> for PathWalker { 45 | fn from_iter>(iter: T) -> Self { 46 | Self { 47 | walk: iter.into_iter().collect(), 48 | ..Default::default() 49 | } 50 | } 51 | } 52 | 53 | impl PathWalker { 54 | pub fn push(&mut self, from: Address, to: Address) { 55 | self.walk.push_back((from, to)); 56 | } 57 | } 58 | 59 | impl HookConcrete for PathWalker 60 | where 61 | S: AsState> + StateOps + 'static, 62 | O: Order + 'static, 63 | R: 'static, 64 | { 65 | type State = S; 66 | type Error = PCodeError; 67 | type Outcome = R; 68 | 69 | fn hook_architectural_step( 70 | &mut self, 71 | _state: &mut Self::State, 72 | _address: &Address, 73 | operation: &StepState, 74 | ) -> Result>, Error> { 75 | self.next = operation.fallthrough().into(); 76 | Ok(HookStepAction::Pass.into()) 77 | } 78 | 79 | fn hook_cbranch( 80 | &mut self, 81 | state: &mut Self::State, 82 | _destination: &Operand, 83 | condition: &Operand, 84 | ) -> Result>, Error> { 85 | let pcode_state = state.state_ref(); 86 | let address = pcode_state.program_counter_value().map_err(Error::state)?; 87 | let is_taken = pcode_state.get_operand::(condition).map_err(Error::state)? != 0; 88 | 89 | if matches!(self.walk.front(), Some((expected, _)) if *expected == address) { 90 | let (_, next) = self.walk.pop_front().unwrap(); 91 | Ok(if next == self.next { 92 | if is_taken { HookCBranchAction::Flip } else { HookCBranchAction::Pass } 93 | } else { 94 | if is_taken { HookCBranchAction::Pass } else { HookCBranchAction::Flip } 95 | }.into()) 96 | } else { 97 | Ok(HookCBranchAction::Pass.into()) 98 | } 99 | } 100 | } 101 | 102 | impl ClonableHookConcrete for PathWalker 103 | where 104 | S: AsState> + StateOps + 'static, 105 | O: Order + 'static, 106 | R: 'static, 107 | { 108 | } 109 | 110 | pub struct RandomWalker { 111 | rand: R, 112 | marker: PhantomData<(S, E, O)>, 113 | } 114 | 115 | impl Clone for RandomWalker 116 | where 117 | B: Clone + Rng, 118 | { 119 | fn clone(&self) -> Self { 120 | Self { 121 | rand: self.rand.clone(), 122 | marker: PhantomData, 123 | } 124 | } 125 | } 126 | 127 | impl RandomWalker 128 | where 129 | B: Rng, 130 | { 131 | pub fn new(rand: B) -> RandomWalker { 132 | RandomWalker { 133 | rand, 134 | marker: PhantomData, 135 | } 136 | } 137 | } 138 | 139 | impl RandomWalker { 140 | pub fn new_small() -> RandomWalker { 141 | RandomWalker::new(SmallRng::from_entropy()) 142 | } 143 | } 144 | 145 | impl HookConcrete for RandomWalker 146 | where 147 | B: Rng + 'static, 148 | S: StateOps + 'static, 149 | E: std::error::Error + Send + Sync + 'static, 150 | O: 'static, 151 | { 152 | type State = S; 153 | type Error = E; 154 | type Outcome = O; 155 | 156 | fn hook_cbranch( 157 | &mut self, 158 | _state: &mut Self::State, 159 | _destination: &Operand, 160 | _condition: &Operand, 161 | ) -> Result>, Error> { 162 | Ok(if self.rand.gen::() { 163 | HookCBranchAction::Flip 164 | } else { 165 | HookCBranchAction::Pass 166 | } 167 | .into()) 168 | } 169 | } 170 | 171 | impl ClonableHookConcrete for RandomWalker 172 | where 173 | B: Clone + Rng + 'static, 174 | S: StateOps + 'static, 175 | E: std::error::Error + Send + Sync + 'static, 176 | O: 'static, 177 | { 178 | } 179 | -------------------------------------------------------------------------------- /fuguex-concrete/src/hooks.rs: -------------------------------------------------------------------------------- 1 | use fugue::ir::il::ecode::Location; 2 | use fugue::ir::il::pcode::{Operand, PCodeOp, Register}; 3 | use fugue::ir::Address; 4 | use fuguex_machine::StepState; 5 | use fuguex_microx::types::HookInvalidAccessAction; 6 | use fuguex_state::StateOps; 7 | 8 | use downcast_rs::{impl_downcast, Downcast}; 9 | use dyn_clone::{clone_trait_object, DynClone}; 10 | 11 | use fuguex_hooks::{ 12 | Error, HookAction, HookCBranchAction, HookCallAction, HookOutcome, HookStepAction, 13 | }; 14 | use fuguex_microx::ViolationSource; 15 | 16 | #[allow(unused)] 17 | pub trait HookConcrete: Downcast { 18 | type State: StateOps; 19 | type Error: std::error::Error + Send + Sync + 'static; 20 | type Outcome; 21 | 22 | fn hook_memory_read( 23 | &mut self, 24 | state: &mut Self::State, 25 | address: &Address, 26 | size: usize, 27 | ) -> Result>, Error> { 28 | Ok(HookAction::Pass.into()) 29 | } 30 | 31 | fn hook_memory_write( 32 | &mut self, 33 | state: &mut Self::State, 34 | address: &Address, 35 | size: usize, 36 | value: &[::Value], 37 | ) -> Result>, Error> { 38 | Ok(HookAction::Pass.into()) 39 | } 40 | 41 | fn hook_invalid_memory_access( 42 | &mut self, 43 | state: &mut Self::State, 44 | address: &Address, 45 | size: usize, 46 | source: ViolationSource, 47 | ) -> Result< 48 | HookOutcome::Value>>, 49 | Error, 50 | > { 51 | Ok(HookInvalidAccessAction::Pass.into()) 52 | } 53 | 54 | fn hook_register_read( 55 | &mut self, 56 | state: &mut Self::State, 57 | register: &Register, 58 | ) -> Result>, Error> { 59 | Ok(HookAction::Pass.into()) 60 | } 61 | 62 | fn hook_register_write( 63 | &mut self, 64 | state: &mut Self::State, 65 | register: &Register, 66 | value: &[::Value], 67 | ) -> Result>, Error> { 68 | Ok(HookAction::Pass.into()) 69 | } 70 | 71 | fn hook_operand_read( 72 | &mut self, 73 | state: &mut Self::State, 74 | operand: &Operand, 75 | ) -> Result>, Error> { 76 | match operand { 77 | Operand::Address { 78 | value: address, 79 | size, 80 | } => self.hook_memory_read(state, &address, *size), 81 | Operand::Register { .. } => { 82 | self.hook_register_read(state, &operand.register().unwrap()) 83 | } 84 | _ => Ok(HookAction::Pass.into()), 85 | } 86 | } 87 | 88 | fn hook_operand_write( 89 | &mut self, 90 | state: &mut Self::State, 91 | operand: &Operand, 92 | value: &[::Value], 93 | ) -> Result>, Error> { 94 | match operand { 95 | Operand::Address { 96 | value: address, 97 | size, 98 | } => self.hook_memory_write(state, &address, *size, value), 99 | Operand::Register { .. } => { 100 | self.hook_register_write(state, &operand.register().unwrap(), value) 101 | } 102 | _ => Ok(HookAction::Pass.into()), 103 | } 104 | } 105 | 106 | fn hook_call( 107 | &mut self, 108 | state: &mut Self::State, 109 | destination: &Address, 110 | ) -> Result>, Error> { 111 | Ok(HookCallAction::Pass.into()) 112 | } 113 | 114 | fn hook_cbranch( 115 | &mut self, 116 | state: &mut Self::State, 117 | destination: &Operand, 118 | condition: &Operand, 119 | ) -> Result>, Error> { 120 | Ok(HookCBranchAction::Pass.into()) 121 | } 122 | 123 | fn hook_operation_step( 124 | &mut self, 125 | state: &mut Self::State, 126 | location: &Location, 127 | operation: &PCodeOp, 128 | ) -> Result>, Error> { 129 | Ok(HookStepAction::Pass.into()) 130 | } 131 | 132 | fn hook_architectural_step( 133 | &mut self, 134 | state: &mut Self::State, 135 | address: &Address, 136 | operation: &StepState, 137 | ) -> Result>, Error> { 138 | Ok(HookStepAction::Pass.into()) 139 | } 140 | } 141 | 142 | pub trait ClonableHookConcrete: DynClone + HookConcrete {} 143 | clone_trait_object!( 144 | ClonableHookConcrete 145 | where State: fuguex_state::StateOps, 146 | Error: std::error::Error + Send + Sync + 'static, 147 | ); 148 | 149 | impl_downcast!( 150 | ClonableHookConcrete assoc State, Outcome, Error 151 | where 152 | State: StateOps, 153 | Error: std::error::Error + Send + Sync 154 | ); 155 | -------------------------------------------------------------------------------- /fuguex-concrete/src/interpreter.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::sync::Arc; 3 | 4 | use fnv::FnvHashMap as Map; 5 | use parking_lot::{RwLock, RwLockReadGuard}; 6 | 7 | use fugue::bytes::traits::ByteCast; 8 | use fugue::bytes::Order; 9 | 10 | use fugue::bv::BitVec; 11 | use fugue::db::Database; 12 | use fugue::fp::{self, float_format_from_size, Float, FloatFormat, FloatFormatOps}; 13 | 14 | use fugue::ir::disassembly::ContextDatabase; 15 | use fugue::ir::il::pcode::{Operand, PCodeOp}; 16 | use fugue::ir::il::Location; 17 | use fugue::ir::{ 18 | self, Address, AddressSpace, AddressSpaceId, AddressValue, IntoAddress, Translator, 19 | }; 20 | 21 | use crate::hooks::ClonableHookConcrete; 22 | use fuguex_hooks::types::{HookCBranchAction, HookCallAction}; 23 | 24 | use fuguex_intrinsics::{IntrinsicAction, IntrinsicHandler}; 25 | 26 | use fuguex_loader::LoaderMapping; 27 | 28 | use fuguex_machine::types::{Branch, OrOutcome, Outcome, StepState}; 29 | use fuguex_machine::Interpreter; 30 | 31 | use fuguex_microx::ViolationSource; 32 | 33 | use fuguex_state::pcode::{self, PCodeState}; 34 | use fuguex_state::pcode::{ 35 | MAX_POINTER_SIZE, POINTER_16_SIZE, POINTER_32_SIZE, POINTER_64_SIZE, POINTER_8_SIZE, 36 | }; 37 | use fuguex_state::register::ReturnLocation; 38 | use fuguex_state::traits::State; 39 | 40 | use thiserror::Error; 41 | 42 | #[derive(Debug, Error)] 43 | pub enum Error { 44 | #[error("division by zero")] 45 | DivisionByZero, 46 | #[error(transparent)] 47 | Hook(fuguex_hooks::types::Error), 48 | #[error(transparent)] 49 | Intrinsic(fuguex_intrinsics::Error), 50 | #[error("error lifting instruction at {0}: {1}")] 51 | Lift(Address, #[source] ir::error::Error), 52 | #[error(transparent)] 53 | State(#[from] pcode::Error), 54 | #[error("incompatible operand sizes of {0} bytes and {1} bytes")] 55 | IncompatibleOperands(usize, usize), 56 | #[error("unsupported address size of {} bits", .0 * 8)] 57 | UnsupportedAddressSize(usize), 58 | #[error("unsupported branch destination in space `{}`", .0.index())] 59 | UnsupportedBranchDestination(AddressSpaceId), 60 | #[error(transparent)] 61 | UnsupportedFloatFormat(#[from] fp::Error), 62 | #[error("unsupported operand size of {0} bytes; maximum supported is {1} bytes")] 63 | UnsupportedOperandSize(usize, usize), 64 | } 65 | 66 | pub type ConcreteState = PCodeState; 67 | 68 | #[derive(Clone)] 69 | pub struct ConcreteContext { 70 | database: Option>, 71 | translator: Arc, 72 | translator_context: ContextDatabase, 73 | translator_cache: Arc>>, 74 | hook_names: Map, 75 | hooks: Vec< 76 | Box, Error = pcode::Error, Outcome = R>>, 77 | >, 78 | intrinsics: IntrinsicHandler>, 79 | state: ConcreteState, 80 | marker: PhantomData, 81 | } 82 | 83 | trait ToSignedBytes { 84 | fn expand_as( 85 | self, 86 | ctxt: &mut ConcreteContext, 87 | dest: &Operand, 88 | signed: bool, 89 | ) -> Result<(), Error>; 90 | } 91 | 92 | impl ToSignedBytes for bool { 93 | fn expand_as( 94 | self, 95 | ctxt: &mut ConcreteContext, 96 | dest: &Operand, 97 | _signed: bool, 98 | ) -> Result<(), Error> { 99 | let mut buf = [0u8; 1]; 100 | self.into_bytes::(&mut buf); 101 | 102 | ctxt.write_operand(dest, &buf[..])?; 103 | 104 | Ok(()) 105 | } 106 | } 107 | 108 | impl ToSignedBytes for BitVec { 109 | fn expand_as( 110 | self, 111 | ctxt: &mut ConcreteContext, 112 | dest: &Operand, 113 | signed: bool, 114 | ) -> Result<(), Error> { 115 | let size = dest.size(); 116 | let dbits = size << 3; 117 | let target = if signed { self.signed() } else { self }; 118 | let target = if target.bits() != dbits { 119 | target.cast(dbits) 120 | } else { 121 | target 122 | }; 123 | 124 | if size > OPERAND_SIZE { 125 | return Err(Error::UnsupportedOperandSize(size, OPERAND_SIZE)); 126 | } 127 | 128 | let mut buf = [0u8; OPERAND_SIZE]; 129 | target.into_bytes::(&mut buf[..size]); 130 | 131 | ctxt.write_operand(dest, &buf[..size])?; 132 | 133 | Ok(()) 134 | } 135 | } 136 | 137 | impl 138 | ConcreteContext 139 | { 140 | pub fn new(translator: Translator, state: ConcreteState) -> Self { 141 | Self { 142 | database: None, 143 | translator_context: translator.context_database(), 144 | translator_cache: Arc::new(RwLock::new(Map::default())), 145 | translator: Arc::new(translator), 146 | hook_names: Map::default(), 147 | hooks: Vec::default(), 148 | intrinsics: IntrinsicHandler::default(), 149 | state, 150 | marker: PhantomData, 151 | } 152 | } 153 | 154 | pub fn from_loader(loader: impl LoaderMapping>) -> Self { 155 | let database = loader.database(); 156 | let translator = loader.translator(); 157 | let state = loader.into_state(); 158 | 159 | Self { 160 | database, 161 | translator_context: translator.context_database(), 162 | translator_cache: Arc::new(RwLock::new(Map::default())), 163 | translator, 164 | hook_names: Map::default(), 165 | hooks: Vec::default(), 166 | intrinsics: IntrinsicHandler::default(), 167 | state, 168 | marker: PhantomData, 169 | } 170 | } 171 | 172 | pub fn add_hook(&mut self, name: S, hook: H) 173 | where 174 | S: AsRef, 175 | H: ClonableHookConcrete, Error = pcode::Error, Outcome = R> 176 | + 'static, 177 | { 178 | let index = self.hooks.len(); 179 | self.hook_names.insert(name.as_ref().into(), index); 180 | self.hooks.push(Box::new(hook)); 181 | } 182 | 183 | pub fn find_hook(&self, name: S) -> Option<&H> 184 | where 185 | S: AsRef, 186 | H: ClonableHookConcrete, Error = pcode::Error, Outcome = R> 187 | + 'static, 188 | { 189 | self.hook_names 190 | .get(name.as_ref()) 191 | .copied() 192 | .and_then(|i| self.hooks[i].downcast_ref::()) 193 | } 194 | 195 | pub fn find_hook_mut(&mut self, name: S) -> Option<&mut H> 196 | where 197 | S: AsRef, 198 | H: ClonableHookConcrete, Error = pcode::Error, Outcome = R> 199 | + 'static, 200 | { 201 | self.hook_names 202 | .get(name.as_ref()) 203 | .copied() 204 | .and_then(move |i| self.hooks[i].downcast_mut::()) 205 | } 206 | 207 | pub fn database(&self) -> Option<&Database> { 208 | self.database.as_deref() 209 | } 210 | 211 | pub fn state(&self) -> &ConcreteState { 212 | &self.state 213 | } 214 | 215 | pub fn state_mut(&mut self) -> &mut ConcreteState { 216 | &mut self.state 217 | } 218 | 219 | pub fn lifted_cache(&self) -> RwLockReadGuard> { 220 | self.translator_cache.read() 221 | } 222 | 223 | fn read_operand_with( 224 | &mut self, 225 | operand: &Operand, 226 | buf: &mut [u8], 227 | kind: ViolationSource, 228 | f: F, 229 | ) -> Result 230 | where 231 | F: Fn(&mut [u8]) -> U, 232 | { 233 | for hook in self.hooks.iter_mut() { 234 | hook.hook_operand_read(&mut self.state, operand) 235 | .map_err(Error::Hook)?; 236 | } 237 | 238 | let res = self.state.with_operand_values(operand, |values| { 239 | buf.copy_from_slice(values); 240 | f(buf) 241 | }); 242 | 243 | if let Err(pcode::Error::Memory(ref e)) = res { 244 | let mut state_change = None; 245 | let (address, size) = e.access(); 246 | 247 | debug_assert_eq!(size, buf.len()); 248 | 249 | for hook in self.hooks.iter_mut() { 250 | let result = hook 251 | .hook_invalid_memory_access(&mut self.state, &address, size, kind) 252 | .map_err(Error::Hook)?; 253 | if result.state_changed || result.action.is_value() { 254 | state_change = Some(result); 255 | } 256 | } 257 | 258 | if let Some(state_change) = state_change { 259 | if let Some(values) = state_change.action.into_value() { 260 | for (d, s) in buf.iter_mut().zip(values.into_iter()) { 261 | *d = s; 262 | } 263 | Ok(f(buf)) 264 | } else { 265 | self.state.with_operand_values(operand, |values| { 266 | buf.copy_from_slice(values); 267 | f(buf) 268 | }) 269 | } 270 | } else { 271 | // no state change, redo error 272 | res 273 | } 274 | .map_err(Error::State) 275 | } else { 276 | res.map_err(Error::State) 277 | } 278 | } 279 | 280 | fn read_operand( 281 | &mut self, 282 | operand: &Operand, 283 | buf: &mut [u8], 284 | kind: ViolationSource, 285 | ) -> Result<(), Error> { 286 | self.read_operand_with(operand, buf, kind, |_| ()) 287 | } 288 | 289 | fn write_operand(&mut self, operand: &Operand, buf: &[u8]) -> Result<(), Error> { 290 | let res = self 291 | .state 292 | .with_operand_values_mut(operand, |values| values.copy_from_slice(&buf)); 293 | 294 | if let Err(pcode::Error::Memory(ref e)) = res { 295 | let mut state_changed = false; 296 | let mut skipped = false; 297 | let (address, size) = e.access(); 298 | 299 | debug_assert_eq!(size, buf.len()); 300 | 301 | for hook in self.hooks.iter_mut() { 302 | let res = hook 303 | .hook_invalid_memory_access( 304 | &mut self.state, 305 | &address, 306 | size, 307 | ViolationSource::Write, 308 | ) 309 | .map_err(Error::Hook)?; 310 | state_changed |= res.state_changed; 311 | skipped |= res.action.is_skip(); 312 | } 313 | 314 | if state_changed { 315 | let res = self.state 316 | .with_operand_values_mut(operand, |values| values.copy_from_slice(&buf)); 317 | if res.is_err() && skipped { 318 | Ok(()) 319 | } else { 320 | res 321 | } 322 | } else if skipped { 323 | Ok(()) 324 | } else { 325 | // no state change, redo error 326 | res 327 | } 328 | .map_err(Error::State)? 329 | } else { 330 | res.map_err(Error::State)? 331 | } 332 | 333 | for hook in self.hooks.iter_mut() { 334 | hook.hook_operand_write(&mut self.state, operand, &buf) 335 | .map_err(Error::Hook)?; 336 | } 337 | 338 | Ok(()) 339 | } 340 | 341 | fn lift_int1( 342 | &mut self, 343 | op: CO, 344 | dest: &Operand, 345 | rhs: &Operand, 346 | signed: bool, 347 | ) -> Result, Error> 348 | where 349 | CO: FnOnce(BitVec) -> Result, 350 | COO: ToSignedBytes, 351 | { 352 | let rsize = rhs.size(); 353 | if rsize > OPERAND_SIZE { 354 | return Err(Error::UnsupportedOperandSize(rsize, OPERAND_SIZE)); 355 | } 356 | 357 | let mut rbuf = [0u8; OPERAND_SIZE]; 358 | 359 | self.read_operand(rhs, &mut rbuf[..rsize], ViolationSource::Read)?; 360 | 361 | op(BitVec::from_bytes::(&rbuf[..rsize], signed))?.expand_as(self, dest, signed)?; 362 | 363 | Ok(Outcome::Branch(Branch::Next)) 364 | } 365 | 366 | fn lift_int2( 367 | &mut self, 368 | op: CO, 369 | dest: &Operand, 370 | lhs: &Operand, 371 | rhs: &Operand, 372 | signed: bool, 373 | ) -> Result, Error> 374 | where 375 | CO: FnOnce(BitVec, BitVec) -> Result, 376 | COO: ToSignedBytes, 377 | { 378 | let mut lbuf = [0u8; OPERAND_SIZE]; 379 | let mut rbuf = [0u8; OPERAND_SIZE]; 380 | 381 | let lsize = lhs.size(); 382 | let rsize = rhs.size(); 383 | 384 | if lsize > OPERAND_SIZE { 385 | return Err(Error::UnsupportedOperandSize(lsize, OPERAND_SIZE)); 386 | } 387 | 388 | if rsize > OPERAND_SIZE { 389 | return Err(Error::UnsupportedOperandSize(rsize, OPERAND_SIZE)); 390 | } 391 | 392 | self.read_operand(lhs, &mut lbuf[..lsize], ViolationSource::Read)?; 393 | self.read_operand(rhs, &mut rbuf[..rsize], ViolationSource::Read)?; 394 | 395 | let lhs_val = BitVec::from_bytes::(&lbuf[..lsize], signed); 396 | let mut rhs_val = BitVec::from_bytes::(&rbuf[..rsize], signed); 397 | 398 | if lhs.size() != rhs.size() { 399 | rhs_val = rhs_val.cast(lhs_val.bits()); 400 | } 401 | 402 | op(lhs_val, rhs_val)?.expand_as(self, dest, signed)?; 403 | 404 | Ok(Outcome::Branch(Branch::Next)) 405 | } 406 | 407 | fn lift_bool1(&mut self, op: CO, dest: &Operand, rhs: &Operand) -> Result, Error> 408 | where 409 | CO: FnOnce(bool) -> Result, 410 | { 411 | let mut rbuf = [0u8; 1]; 412 | 413 | self.read_operand(rhs, &mut rbuf, ViolationSource::Read)?; 414 | 415 | op(bool::from_bytes::(&rbuf))?.expand_as(self, dest, false)?; 416 | 417 | Ok(Outcome::Branch(Branch::Next)) 418 | } 419 | 420 | fn lift_bool2( 421 | &mut self, 422 | op: CO, 423 | dest: &Operand, 424 | lhs: &Operand, 425 | rhs: &Operand, 426 | ) -> Result, Error> 427 | where 428 | CO: FnOnce(bool, bool) -> Result, 429 | { 430 | let mut lbuf = [0u8; 1]; 431 | let mut rbuf = [0u8; 1]; 432 | 433 | self.read_operand(lhs, &mut lbuf, ViolationSource::Read)?; 434 | self.read_operand(rhs, &mut rbuf, ViolationSource::Read)?; 435 | 436 | op(bool::from_bytes::(&lbuf), bool::from_bytes::(&rbuf))? 437 | .expand_as(self, dest, false)?; 438 | 439 | Ok(Outcome::Branch(Branch::Next)) 440 | } 441 | 442 | fn lift_float1( 443 | &mut self, 444 | op: CO, 445 | dest: &Operand, 446 | rhs: &Operand, 447 | ) -> Result, Error> 448 | where 449 | CO: FnOnce(Float, &FloatFormat) -> Result, 450 | COO: ToSignedBytes, 451 | { 452 | let rsize = rhs.size(); 453 | if rsize > OPERAND_SIZE { 454 | return Err(Error::UnsupportedOperandSize(rsize, OPERAND_SIZE)); 455 | } 456 | 457 | let format = float_format_from_size(rsize)?; 458 | let mut rbuf = [0u8; OPERAND_SIZE]; 459 | 460 | self.read_operand(rhs, &mut rbuf[..rsize], ViolationSource::Read)?; 461 | 462 | let rhs_val = format.from_bitvec(&BitVec::from_bytes::(&rbuf[..rsize], false)); 463 | 464 | op(rhs_val, &format)?.expand_as(self, dest, true)?; 465 | 466 | Ok(Outcome::Branch(Branch::Next)) 467 | } 468 | 469 | fn lift_float2( 470 | &mut self, 471 | op: CO, 472 | dest: &Operand, 473 | lhs: &Operand, 474 | rhs: &Operand, 475 | ) -> Result, Error> 476 | where 477 | CO: FnOnce(Float, Float, &FloatFormat) -> Result, 478 | COO: ToSignedBytes, 479 | { 480 | let mut lbuf = [0u8; OPERAND_SIZE]; 481 | let mut rbuf = [0u8; OPERAND_SIZE]; 482 | 483 | let lsize = lhs.size(); 484 | let rsize = rhs.size(); 485 | 486 | if lsize > OPERAND_SIZE { 487 | return Err(Error::UnsupportedOperandSize(lsize, OPERAND_SIZE)); 488 | } 489 | 490 | if rsize > OPERAND_SIZE { 491 | return Err(Error::UnsupportedOperandSize(rsize, OPERAND_SIZE)); 492 | } 493 | 494 | if lsize != rsize { 495 | return Err(Error::IncompatibleOperands(lsize, rsize)); 496 | } 497 | 498 | let format = float_format_from_size(rsize)?; 499 | 500 | self.read_operand(lhs, &mut lbuf[..lsize], ViolationSource::Read)?; 501 | self.read_operand(rhs, &mut rbuf[..rsize], ViolationSource::Read)?; 502 | 503 | let lhs_val = format.from_bitvec(&BitVec::from_bytes::(&lbuf[..lsize], false)); 504 | let rhs_val = format.from_bitvec(&BitVec::from_bytes::(&rbuf[..rsize], false)); 505 | 506 | op(lhs_val, rhs_val, &format)?.expand_as(self, dest, true)?; 507 | 508 | Ok(Outcome::Branch(Branch::Next)) 509 | } 510 | 511 | fn with_return_location(&self, f: F) -> Result 512 | where 513 | F: FnOnce(&Operand) -> Result, 514 | { 515 | match &*self.state.registers().return_location() { 516 | ReturnLocation::Register(ref operand) => f(&operand.clone()), 517 | ReturnLocation::Relative(ref operand, offset) => { 518 | let address = self.state.get_address(operand).map_err(Error::State)?; 519 | let operand = Operand::Address { 520 | value: Address::new(&*self.state.memory_space(), u64::from(address + *offset)), 521 | size: self.state.memory_space().address_size(), 522 | }; 523 | f(&operand) 524 | } 525 | } 526 | } 527 | 528 | fn skip_return(&mut self) -> Result { 529 | // NOTE: for x86, etc. we need to clean-up the stack 530 | // arguments; currently, this is the responsibility of 531 | // hooks that issue a `HookCallAction::Skip`. 532 | let address = self.with_return_location(|operand| { 533 | self.state.get_address(operand).map_err(Error::State) 534 | })?; 535 | 536 | // Next we pop the return address (if needed) 537 | let extra_pop = self.state.convention().default_prototype().extra_pop(); 538 | 539 | if extra_pop > 0 { 540 | let stack_pointer = self.state.registers().stack_pointer().clone(); 541 | let address = self 542 | .state 543 | .get_address(&stack_pointer) 544 | .map_err(Error::State)?; 545 | 546 | self.set_address_value(&stack_pointer, address + extra_pop)?; 547 | } 548 | 549 | Ok(AddressValue::new( 550 | self.state.memory_space(), 551 | u64::from(address), 552 | )) 553 | } 554 | 555 | #[inline] 556 | fn get_address_value( 557 | &mut self, 558 | pointer: &Operand, 559 | source: ViolationSource, 560 | ) -> Result { 561 | let mut buf = [0u8; MAX_POINTER_SIZE]; 562 | let psize = pointer.size(); 563 | 564 | for hook in self.hooks.iter_mut() { 565 | hook.hook_operand_read(&mut self.state, pointer) 566 | .map_err(Error::Hook)?; 567 | } 568 | 569 | let address = if psize == POINTER_64_SIZE { 570 | self.read_operand_with(pointer, &mut buf[..psize], source, |buf| { 571 | u64::from_bytes::(buf) 572 | })? 573 | } else if psize == POINTER_32_SIZE { 574 | self.read_operand_with(pointer, &mut buf[..psize], source, |buf| { 575 | u32::from_bytes::(buf) as u64 576 | })? 577 | } else if psize == POINTER_16_SIZE { 578 | self.read_operand_with(pointer, &mut buf[..psize], source, |buf| { 579 | u16::from_bytes::(buf) as u64 580 | })? 581 | } else if psize == POINTER_8_SIZE { 582 | self.read_operand_with(pointer, &mut buf[..psize], source, |buf| { 583 | u8::from_bytes::(buf) as u64 584 | })? 585 | } else { 586 | return Err(Error::UnsupportedAddressSize(pointer.size())); 587 | }; 588 | 589 | Ok(address) 590 | } 591 | 592 | #[inline] 593 | fn set_address_value( 594 | &mut self, 595 | pointer: &Operand, 596 | value: A, 597 | ) -> Result<(), Error> 598 | where A: IntoAddress { 599 | let mut buf = [0u8; MAX_POINTER_SIZE]; 600 | let psize = pointer.size(); 601 | 602 | let address = value.into_address(self.state.memory_space_ref()); 603 | 604 | if psize == POINTER_64_SIZE { 605 | u64::from(address).into_bytes::(&mut buf[..psize]); 606 | self.write_operand(pointer, &buf[..psize]) 607 | } else if psize == POINTER_32_SIZE { 608 | u32::from(address).into_bytes::(&mut buf[..psize]); 609 | self.write_operand(pointer, &buf[..psize]) 610 | } else if psize == POINTER_16_SIZE { 611 | u16::from(address).into_bytes::(&mut buf[..psize]); 612 | self.write_operand(pointer, &buf[..psize]) 613 | } else if psize == POINTER_8_SIZE { 614 | u8::from(address).into_bytes::(&mut buf[..psize]); 615 | self.write_operand(pointer, &buf[..psize]) 616 | } else { 617 | Err(Error::UnsupportedAddressSize(psize)) 618 | } 619 | } 620 | 621 | #[inline] 622 | fn copy_operand(&mut self, source: &Operand, destination: &Operand) -> Result<(), Error> { 623 | let size = source.size(); 624 | if size > OPERAND_SIZE { 625 | return Err(Error::UnsupportedOperandSize(size, OPERAND_SIZE)); 626 | } 627 | 628 | let mut buf = [0u8; OPERAND_SIZE]; 629 | 630 | self.read_operand(source, &mut buf[..size], ViolationSource::Read)?; 631 | self.write_operand(destination, &buf[..size])?; 632 | 633 | Ok(()) 634 | } 635 | } 636 | 637 | impl Interpreter 638 | for ConcreteContext 639 | { 640 | type State = PCodeState; 641 | type Error = Error; 642 | type Outcome = R; 643 | 644 | fn fork(&self) -> Self { 645 | Self { 646 | database: self.database.clone(), 647 | translator: self.translator.clone(), 648 | translator_context: self.translator.context_database(), 649 | translator_cache: self.translator_cache.clone(), 650 | hook_names: self.hook_names.clone(), 651 | hooks: self.hooks.clone(), 652 | intrinsics: self.intrinsics.clone(), 653 | state: self.state.fork(), 654 | marker: self.marker, 655 | } 656 | } 657 | 658 | fn restore(&mut self, other: &Self) { 659 | self.hooks = other.hooks.clone(); 660 | self.hook_names = other.hook_names.clone(); 661 | self.state.restore(&other.state); 662 | } 663 | 664 | fn copy(&mut self, source: &Operand, destination: &Operand) -> Result, Error> { 665 | self.copy_operand(source, destination)?; 666 | Ok(Outcome::Branch(Branch::Next)) 667 | } 668 | 669 | fn load( 670 | &mut self, 671 | source: &Operand, 672 | destination: &Operand, 673 | space: AddressSpaceId, 674 | ) -> Result, Error> { 675 | let offset = self.get_address_value(source, ViolationSource::ReadVia)?; 676 | 677 | let space = self.translator.manager().space_by_id(space); 678 | let space_size = space.address_size(); 679 | let space_word_size = space.word_size() as u64; 680 | 681 | debug_assert_eq!(space_size, source.size()); 682 | 683 | let addr_val = offset.wrapping_mul(space_word_size) 684 | & 1u64 685 | .checked_shl(space_size.checked_shl(3).unwrap_or(0) as u32) 686 | .unwrap_or(0) 687 | .wrapping_sub(1); 688 | 689 | let address = Operand::Address { 690 | value: Address::new(space, addr_val), 691 | size: destination.size(), 692 | }; 693 | 694 | self.copy_operand(&address, destination)?; 695 | 696 | Ok(Outcome::Branch(Branch::Next)) 697 | } 698 | 699 | fn store( 700 | &mut self, 701 | source: &Operand, 702 | destination: &Operand, 703 | space: AddressSpaceId, 704 | ) -> Result, Error> { 705 | // Same semantics as copy and load, just with different address spaces 706 | let offset = self.get_address_value(destination, ViolationSource::WriteVia)?; 707 | 708 | let space = self.translator.manager().space_by_id(space); 709 | let space_size = space.address_size(); 710 | let space_word_size = space.word_size() as u64; 711 | 712 | debug_assert_eq!(space_size, destination.size()); 713 | 714 | // NOTE: 715 | // It is possible for the addressable unit of an address space to be 716 | // bigger than a single byte. If the wordsize attribute of the space 717 | // given by the ID is bigger than one, the offset into the space 718 | // obtained from input1 must be multiplied by this value in order to 719 | // obtain the correct byte offset into the space. 720 | 721 | let addr_val = offset.wrapping_mul(space_word_size) 722 | & 1u64 723 | .checked_shl(space_size.checked_shl(3).unwrap_or(0) as u32) 724 | .unwrap_or(0) 725 | .wrapping_sub(1); 726 | 727 | let address = Operand::Address { 728 | value: Address::new(space, addr_val), 729 | size: source.size(), 730 | }; 731 | 732 | self.copy_operand(source, &address)?; 733 | 734 | Ok(Outcome::Branch(Branch::Next)) 735 | } 736 | 737 | fn branch(&mut self, destination: &Operand) -> Result, Error> { 738 | // destination operand does not store branch target, it is branch target 739 | match destination { 740 | Operand::Constant { value, .. } => { 741 | let action = Branch::Local(*value as isize); 742 | Ok(Outcome::Branch(action)) 743 | } 744 | Operand::Address { value, .. } => { 745 | let address_value = value.into_address_value(self.state.memory_space_ref()); 746 | let action = Branch::Global(address_value); 747 | Ok(Outcome::Branch(action)) 748 | } 749 | Operand::Register { .. } => { 750 | return Err(Error::UnsupportedBranchDestination( 751 | self.translator.manager().register_space_id(), 752 | )) 753 | } 754 | Operand::Variable { space, .. } => { 755 | return Err(Error::UnsupportedBranchDestination(*space)) 756 | } 757 | } 758 | } 759 | 760 | fn cbranch(&mut self, destination: &Operand, condition: &Operand) -> Result, Error> { 761 | assert!(condition.size() == 1); 762 | 763 | let mut flip = false; 764 | 765 | // Invoke hook 766 | for hook in self.hooks.iter_mut() { 767 | match hook 768 | .hook_cbranch(&mut self.state, destination, condition) 769 | .map_err(Error::Hook)? 770 | .action 771 | { 772 | HookCBranchAction::Pass => (), 773 | HookCBranchAction::Flip => { flip = true; }, 774 | HookCBranchAction::Halt(r) => return Ok(Outcome::Halt(r)) 775 | } 776 | } 777 | 778 | // The hook may change the condition value, so we need to read it here 779 | let mut buf = [0u8; 1]; 780 | self.read_operand(condition, &mut buf[..], ViolationSource::Read)?; 781 | 782 | let condition_value = { 783 | let v = bool::from_bytes::(&buf); 784 | if flip { 785 | let nv = !v; 786 | log::trace!("flipped branch condition {} to {}", condition, nv); 787 | self.state.set_operand(condition, nv)?; 788 | nv 789 | } else { 790 | v 791 | } 792 | }; 793 | 794 | if condition_value { 795 | self.branch(destination) 796 | } else { 797 | Ok(Outcome::Branch(Branch::Next)) 798 | } 799 | } 800 | 801 | fn ibranch(&mut self, destination: &Operand) -> Result, Error> { 802 | if destination == &*self.state.registers().program_counter() { 803 | return self.icall(destination); 804 | } 805 | 806 | let address = AddressValue::new( 807 | self.state.memory_space(), 808 | self.get_address_value(destination, ViolationSource::ReadVia)?, 809 | ); 810 | Ok(Outcome::Branch(Branch::Global(address))) 811 | } 812 | 813 | fn call(&mut self, destination: &Operand) -> Result, Error> { 814 | match destination { 815 | Operand::Address { value, .. } => { 816 | let mut skip = false; 817 | let address_value = value.into_address_value(self.state.memory_space_ref()); 818 | for hook in self.hooks.iter_mut() { 819 | match hook 820 | .hook_call(&mut self.state, &(&address_value).into()) 821 | .map_err(Error::Hook)? 822 | .action 823 | { 824 | HookCallAction::Pass => (), 825 | HookCallAction::Skip => { 826 | skip = true; 827 | } 828 | HookCallAction::Halt(r) => return Ok(Outcome::Halt(r)), 829 | } 830 | } 831 | 832 | if skip { 833 | Ok(Outcome::Branch(Branch::Global(self.skip_return()?))) 834 | } else { 835 | Ok(Outcome::Branch(Branch::Global(address_value))) 836 | } 837 | } 838 | Operand::Constant { .. } => Err(Error::UnsupportedBranchDestination( 839 | self.translator.manager().constant_space_id(), 840 | )), 841 | Operand::Register { .. } => Err(Error::UnsupportedBranchDestination( 842 | self.translator.manager().register_space_id(), 843 | )), 844 | Operand::Variable { space, .. } => { 845 | Err(Error::UnsupportedBranchDestination(space.clone())) 846 | } 847 | } 848 | } 849 | 850 | fn icall(&mut self, destination: &Operand) -> Result, Error> { 851 | let address_value = AddressValue::new( 852 | self.state.memory_space(), 853 | self.get_address_value(destination, ViolationSource::ReadVia)?, 854 | ); 855 | let address = Address::from(&address_value); 856 | 857 | let mut skip = false; 858 | for hook in self.hooks.iter_mut() { 859 | match hook 860 | .hook_call(&mut self.state, &address) 861 | .map_err(Error::Hook)? 862 | .action 863 | { 864 | HookCallAction::Pass => (), 865 | HookCallAction::Skip => { 866 | skip = true; 867 | } 868 | HookCallAction::Halt(r) => return Ok(Outcome::Halt(r)), 869 | } 870 | } 871 | 872 | if skip { 873 | Ok(Outcome::Branch(Branch::Global(self.skip_return()?))) 874 | } else { 875 | Ok(Outcome::Branch(Branch::Global(address_value))) 876 | } 877 | } 878 | 879 | fn return_(&mut self, destination: &Operand) -> Result, Error> { 880 | let address = AddressValue::new( 881 | self.state.memory_space(), 882 | self.get_address_value(destination, ViolationSource::ReadVia)?, 883 | ); 884 | Ok(Outcome::Branch(Branch::Global(address))) 885 | } 886 | 887 | fn int_eq( 888 | &mut self, 889 | destination: &Operand, 890 | operand1: &Operand, 891 | operand2: &Operand, 892 | ) -> Result, Error> { 893 | self.lift_int2(|u, v| Ok(u == v), destination, operand1, operand2, false) 894 | } 895 | 896 | fn int_not_eq( 897 | &mut self, 898 | destination: &Operand, 899 | operand1: &Operand, 900 | operand2: &Operand, 901 | ) -> Result, Self::Error> { 902 | self.lift_int2(|u, v| Ok(u != v), destination, operand1, operand2, false) 903 | } 904 | 905 | fn int_less( 906 | &mut self, 907 | destination: &Operand, 908 | operand1: &Operand, 909 | operand2: &Operand, 910 | ) -> Result, Self::Error> { 911 | self.lift_int2(|u, v| Ok(u < v), destination, operand1, operand2, false) 912 | } 913 | 914 | fn int_less_eq( 915 | &mut self, 916 | destination: &Operand, 917 | operand1: &Operand, 918 | operand2: &Operand, 919 | ) -> Result, Self::Error> { 920 | self.lift_int2(|u, v| Ok(u <= v), destination, operand1, operand2, false) 921 | } 922 | 923 | fn int_sless( 924 | &mut self, 925 | destination: &Operand, 926 | operand1: &Operand, 927 | operand2: &Operand, 928 | ) -> Result, Self::Error> { 929 | self.lift_int2(|u, v| Ok(u < v), destination, operand1, operand2, true) 930 | } 931 | 932 | fn int_sless_eq( 933 | &mut self, 934 | destination: &Operand, 935 | operand1: &Operand, 936 | operand2: &Operand, 937 | ) -> Result, Self::Error> { 938 | self.lift_int2(|u, v| Ok(u <= v), destination, operand1, operand2, true) 939 | } 940 | 941 | fn int_zext( 942 | &mut self, 943 | destination: &Operand, 944 | operand: &Operand, 945 | ) -> Result, Self::Error> { 946 | self.lift_int1(|u| Ok(u), destination, operand, false) 947 | } 948 | 949 | fn int_sext( 950 | &mut self, 951 | destination: &Operand, 952 | operand: &Operand, 953 | ) -> Result, Self::Error> { 954 | self.lift_int1(|u| Ok(u), destination, operand, true) 955 | } 956 | 957 | fn int_add( 958 | &mut self, 959 | destination: &Operand, 960 | operand1: &Operand, 961 | operand2: &Operand, 962 | ) -> Result, Self::Error> { 963 | self.lift_int2(|u, v| Ok(u + v), destination, operand1, operand2, false) 964 | } 965 | 966 | fn int_sub( 967 | &mut self, 968 | destination: &Operand, 969 | operand1: &Operand, 970 | operand2: &Operand, 971 | ) -> Result, Self::Error> { 972 | self.lift_int2(|u, v| Ok(u - v), destination, operand1, operand2, false) 973 | } 974 | 975 | fn int_carry( 976 | &mut self, 977 | destination: &Operand, 978 | operand1: &Operand, 979 | operand2: &Operand, 980 | ) -> Result, Self::Error> { 981 | self.lift_int2( 982 | |u, v| Ok(u.carry(&v)), 983 | destination, 984 | operand1, 985 | operand2, 986 | false, 987 | ) 988 | } 989 | 990 | fn int_scarry( 991 | &mut self, 992 | destination: &Operand, 993 | operand1: &Operand, 994 | operand2: &Operand, 995 | ) -> Result, Self::Error> { 996 | self.lift_int2( 997 | |u, v| Ok(u.signed_carry(&v)), 998 | destination, 999 | operand1, 1000 | operand2, 1001 | true, 1002 | ) 1003 | } 1004 | 1005 | fn int_sborrow( 1006 | &mut self, 1007 | destination: &Operand, 1008 | operand1: &Operand, 1009 | operand2: &Operand, 1010 | ) -> Result, Self::Error> { 1011 | self.lift_int2( 1012 | |u, v| Ok(u.signed_borrow(&v)), 1013 | destination, 1014 | operand1, 1015 | operand2, 1016 | true, 1017 | ) 1018 | } 1019 | 1020 | fn int_neg( 1021 | &mut self, 1022 | destination: &Operand, 1023 | operand: &Operand, 1024 | ) -> Result, Self::Error> { 1025 | self.lift_int1(|u| Ok(-u), destination, operand, true) 1026 | } 1027 | 1028 | fn int_not( 1029 | &mut self, 1030 | destination: &Operand, 1031 | operand: &Operand, 1032 | ) -> Result, Self::Error> { 1033 | self.lift_int1(|u| Ok(!u), destination, operand, false) 1034 | } 1035 | 1036 | fn int_xor( 1037 | &mut self, 1038 | destination: &Operand, 1039 | operand1: &Operand, 1040 | operand2: &Operand, 1041 | ) -> Result, Self::Error> { 1042 | self.lift_int2(|u, v| Ok(u ^ v), destination, operand1, operand2, false) 1043 | } 1044 | 1045 | fn int_and( 1046 | &mut self, 1047 | destination: &Operand, 1048 | operand1: &Operand, 1049 | operand2: &Operand, 1050 | ) -> Result, Self::Error> { 1051 | self.lift_int2(|u, v| Ok(u & v), destination, operand1, operand2, false) 1052 | } 1053 | 1054 | fn int_or( 1055 | &mut self, 1056 | destination: &Operand, 1057 | operand1: &Operand, 1058 | operand2: &Operand, 1059 | ) -> Result, Self::Error> { 1060 | self.lift_int2(|u, v| Ok(u | v), destination, operand1, operand2, false) 1061 | } 1062 | 1063 | fn int_left_shift( 1064 | &mut self, 1065 | destination: &Operand, 1066 | operand1: &Operand, 1067 | operand2: &Operand, 1068 | ) -> Result, Self::Error> { 1069 | self.lift_int2(|u, v| Ok(u << v), destination, operand1, operand2, false) 1070 | } 1071 | 1072 | fn int_right_shift( 1073 | &mut self, 1074 | destination: &Operand, 1075 | operand1: &Operand, 1076 | operand2: &Operand, 1077 | ) -> Result, Self::Error> { 1078 | self.lift_int2(|u, v| Ok(u >> v), destination, operand1, operand2, false) 1079 | } 1080 | 1081 | fn int_sright_shift( 1082 | &mut self, 1083 | destination: &Operand, 1084 | operand1: &Operand, 1085 | operand2: &Operand, 1086 | ) -> Result, Self::Error> { 1087 | self.lift_int2(|u, v| Ok(u >> v), destination, operand1, operand2, true) 1088 | } 1089 | 1090 | fn int_mul( 1091 | &mut self, 1092 | destination: &Operand, 1093 | operand1: &Operand, 1094 | operand2: &Operand, 1095 | ) -> Result, Self::Error> { 1096 | self.lift_int2(|u, v| Ok(u * v), destination, operand1, operand2, false) 1097 | } 1098 | 1099 | fn int_div( 1100 | &mut self, 1101 | destination: &Operand, 1102 | operand1: &Operand, 1103 | operand2: &Operand, 1104 | ) -> Result, Self::Error> { 1105 | self.lift_int2( 1106 | |u, v| { 1107 | if v.is_zero() { 1108 | Err(Error::DivisionByZero) 1109 | } else { 1110 | Ok(u / v) 1111 | } 1112 | }, 1113 | destination, 1114 | operand1, 1115 | operand2, 1116 | false, 1117 | ) 1118 | } 1119 | 1120 | fn int_sdiv( 1121 | &mut self, 1122 | destination: &Operand, 1123 | operand1: &Operand, 1124 | operand2: &Operand, 1125 | ) -> Result, Self::Error> { 1126 | self.lift_int2( 1127 | |u, v| { 1128 | if v.is_zero() { 1129 | Err(Error::DivisionByZero) 1130 | } else { 1131 | Ok(u / v) 1132 | } 1133 | }, 1134 | destination, 1135 | operand1, 1136 | operand2, 1137 | true, 1138 | ) 1139 | } 1140 | 1141 | fn int_rem( 1142 | &mut self, 1143 | destination: &Operand, 1144 | operand1: &Operand, 1145 | operand2: &Operand, 1146 | ) -> Result, Self::Error> { 1147 | self.lift_int2( 1148 | |u, v| { 1149 | if v.is_zero() { 1150 | Err(Error::DivisionByZero) 1151 | } else { 1152 | Ok(u % v) 1153 | } 1154 | }, 1155 | destination, 1156 | operand1, 1157 | operand2, 1158 | false, 1159 | ) 1160 | } 1161 | 1162 | fn int_srem( 1163 | &mut self, 1164 | destination: &Operand, 1165 | operand1: &Operand, 1166 | operand2: &Operand, 1167 | ) -> Result, Self::Error> { 1168 | self.lift_int2( 1169 | |u, v| { 1170 | if v.is_zero() { 1171 | Err(Error::DivisionByZero) 1172 | } else { 1173 | Ok(u % v) 1174 | } 1175 | }, 1176 | destination, 1177 | operand1, 1178 | operand2, 1179 | true, 1180 | ) 1181 | } 1182 | 1183 | fn bool_not( 1184 | &mut self, 1185 | destination: &Operand, 1186 | operand: &Operand, 1187 | ) -> Result, Self::Error> { 1188 | self.lift_bool1(|u| Ok(!u), destination, operand) 1189 | } 1190 | 1191 | fn bool_xor( 1192 | &mut self, 1193 | destination: &Operand, 1194 | operand1: &Operand, 1195 | operand2: &Operand, 1196 | ) -> Result, Self::Error> { 1197 | self.lift_bool2(|u, v| Ok(u ^ v), destination, operand1, operand2) 1198 | } 1199 | 1200 | fn bool_and( 1201 | &mut self, 1202 | destination: &Operand, 1203 | operand1: &Operand, 1204 | operand2: &Operand, 1205 | ) -> Result, Self::Error> { 1206 | self.lift_bool2(|u, v| Ok(u & v), destination, operand1, operand2) 1207 | } 1208 | 1209 | fn bool_or( 1210 | &mut self, 1211 | destination: &Operand, 1212 | operand1: &Operand, 1213 | operand2: &Operand, 1214 | ) -> Result, Self::Error> { 1215 | self.lift_bool2(|u, v| Ok(u | v), destination, operand1, operand2) 1216 | } 1217 | 1218 | fn float_eq( 1219 | &mut self, 1220 | destination: &Operand, 1221 | operand1: &Operand, 1222 | operand2: &Operand, 1223 | ) -> Result, Self::Error> { 1224 | self.lift_float2(|u, v, _fmt| Ok(u == v), destination, operand1, operand2) 1225 | } 1226 | 1227 | fn float_not_eq( 1228 | &mut self, 1229 | destination: &Operand, 1230 | operand1: &Operand, 1231 | operand2: &Operand, 1232 | ) -> Result, Self::Error> { 1233 | self.lift_float2(|u, v, _fmt| Ok(u != v), destination, operand1, operand2) 1234 | } 1235 | 1236 | fn float_less( 1237 | &mut self, 1238 | destination: &Operand, 1239 | operand1: &Operand, 1240 | operand2: &Operand, 1241 | ) -> Result, Self::Error> { 1242 | self.lift_float2(|u, v, _fmt| Ok(u < v), destination, operand1, operand2) 1243 | } 1244 | 1245 | fn float_less_eq( 1246 | &mut self, 1247 | destination: &Operand, 1248 | operand1: &Operand, 1249 | operand2: &Operand, 1250 | ) -> Result, Self::Error> { 1251 | self.lift_float2(|u, v, _fmt| Ok(u <= v), destination, operand1, operand2) 1252 | } 1253 | 1254 | fn float_is_nan( 1255 | &mut self, 1256 | destination: &Operand, 1257 | operand: &Operand, 1258 | ) -> Result, Self::Error> { 1259 | self.lift_float1(|u, _fmt| Ok(u.is_nan()), destination, operand) 1260 | } 1261 | 1262 | fn float_add( 1263 | &mut self, 1264 | destination: &Operand, 1265 | operand1: &Operand, 1266 | operand2: &Operand, 1267 | ) -> Result, Self::Error> { 1268 | self.lift_float2( 1269 | |u, v, fmt| Ok(fmt.into_bitvec(u + v, fmt.bits())), 1270 | destination, 1271 | operand1, 1272 | operand2, 1273 | ) 1274 | } 1275 | 1276 | fn float_div( 1277 | &mut self, 1278 | destination: &Operand, 1279 | operand1: &Operand, 1280 | operand2: &Operand, 1281 | ) -> Result, Self::Error> { 1282 | self.lift_float2( 1283 | |u, v, fmt| Ok(fmt.into_bitvec(u / v, fmt.bits())), 1284 | destination, 1285 | operand1, 1286 | operand2, 1287 | ) 1288 | } 1289 | 1290 | fn float_mul( 1291 | &mut self, 1292 | destination: &Operand, 1293 | operand1: &Operand, 1294 | operand2: &Operand, 1295 | ) -> Result, Self::Error> { 1296 | self.lift_float2( 1297 | |u, v, fmt| Ok(fmt.into_bitvec(u * v, fmt.bits())), 1298 | destination, 1299 | operand1, 1300 | operand2, 1301 | ) 1302 | } 1303 | 1304 | fn float_sub( 1305 | &mut self, 1306 | destination: &Operand, 1307 | operand1: &Operand, 1308 | operand2: &Operand, 1309 | ) -> Result, Self::Error> { 1310 | self.lift_float2( 1311 | |u, v, fmt| Ok(fmt.into_bitvec(u - v, fmt.bits())), 1312 | destination, 1313 | operand1, 1314 | operand2, 1315 | ) 1316 | } 1317 | 1318 | fn float_neg( 1319 | &mut self, 1320 | destination: &Operand, 1321 | operand: &Operand, 1322 | ) -> Result, Self::Error> { 1323 | self.lift_float1( 1324 | |u, fmt| Ok(fmt.into_bitvec(-u, fmt.bits())), 1325 | destination, 1326 | operand, 1327 | ) 1328 | } 1329 | 1330 | fn float_abs( 1331 | &mut self, 1332 | destination: &Operand, 1333 | operand: &Operand, 1334 | ) -> Result, Self::Error> { 1335 | self.lift_float1( 1336 | |u, fmt| Ok(fmt.into_bitvec(u.abs(), fmt.bits())), 1337 | destination, 1338 | operand, 1339 | ) 1340 | } 1341 | 1342 | fn float_sqrt( 1343 | &mut self, 1344 | destination: &Operand, 1345 | operand: &Operand, 1346 | ) -> Result, Self::Error> { 1347 | self.lift_float1( 1348 | |u, fmt| Ok(fmt.into_bitvec(u.sqrt(), fmt.bits())), 1349 | destination, 1350 | operand, 1351 | ) 1352 | } 1353 | 1354 | fn float_of_int( 1355 | &mut self, 1356 | destination: &Operand, 1357 | operand: &Operand, 1358 | ) -> Result, Self::Error> { 1359 | let fmt = float_format_from_size(destination.size())?; 1360 | self.lift_int1( 1361 | |u| { 1362 | let ival = u.as_bigint().into_owned(); 1363 | let fval = Float::from_bigint(fmt.frac_size, fmt.exp_size, ival); 1364 | Ok(fmt.into_bitvec(fval, fmt.bits())) 1365 | }, 1366 | destination, 1367 | operand, 1368 | true, 1369 | ) 1370 | } 1371 | 1372 | fn float_of_float( 1373 | &mut self, 1374 | destination: &Operand, 1375 | operand: &Operand, 1376 | ) -> Result, Self::Error> { 1377 | let fmt = float_format_from_size(destination.size())?; 1378 | self.lift_float1( 1379 | |rhs, _fmt| Ok(fmt.into_bitvec(rhs, fmt.bits())), 1380 | destination, 1381 | operand, 1382 | ) 1383 | } 1384 | 1385 | fn float_truncate( 1386 | &mut self, 1387 | destination: &Operand, 1388 | operand: &Operand, 1389 | ) -> Result, Self::Error> { 1390 | self.lift_float1( 1391 | |u, _fmt| Ok(u.trunc_into_bitvec(destination.size() * 8)), 1392 | destination, 1393 | operand, 1394 | ) 1395 | } 1396 | 1397 | fn float_ceiling( 1398 | &mut self, 1399 | destination: &Operand, 1400 | operand: &Operand, 1401 | ) -> Result, Self::Error> { 1402 | self.lift_float1( 1403 | |u, fmt| Ok(fmt.into_bitvec(u.ceil(), fmt.bits())), 1404 | destination, 1405 | operand, 1406 | ) 1407 | } 1408 | 1409 | fn float_floor( 1410 | &mut self, 1411 | destination: &Operand, 1412 | operand: &Operand, 1413 | ) -> Result, Self::Error> { 1414 | self.lift_float1( 1415 | |u, fmt| Ok(fmt.into_bitvec(u.floor(), fmt.bits())), 1416 | destination, 1417 | operand, 1418 | ) 1419 | } 1420 | 1421 | fn float_round( 1422 | &mut self, 1423 | destination: &Operand, 1424 | operand: &Operand, 1425 | ) -> Result, Self::Error> { 1426 | self.lift_float1( 1427 | |u, fmt| Ok(fmt.into_bitvec(u.round(), fmt.bits())), 1428 | destination, 1429 | operand, 1430 | ) 1431 | } 1432 | 1433 | fn subpiece( 1434 | &mut self, 1435 | destination: &Operand, 1436 | operand: &Operand, 1437 | amount: &Operand, 1438 | ) -> Result, Error> { 1439 | let amount_size = amount.size(); 1440 | if amount_size > OPERAND_SIZE { 1441 | return Err(Error::UnsupportedOperandSize(amount_size, OPERAND_SIZE)); 1442 | } 1443 | 1444 | let input_size = operand.size(); 1445 | if input_size > OPERAND_SIZE { 1446 | return Err(Error::UnsupportedOperandSize(input_size, OPERAND_SIZE)); 1447 | } 1448 | 1449 | let destination_size = destination.size(); 1450 | if destination_size > OPERAND_SIZE { 1451 | return Err(Error::UnsupportedOperandSize( 1452 | destination_size, 1453 | OPERAND_SIZE, 1454 | )); 1455 | } 1456 | 1457 | let mut buf = [0u8; OPERAND_SIZE]; 1458 | 1459 | let amount = self.read_operand_with( 1460 | amount, 1461 | &mut buf[..amount_size], 1462 | ViolationSource::Read, 1463 | |buf| { 1464 | BitVec::from_bytes::(&buf[..amount_size], false) 1465 | .to_usize() 1466 | .expect("subpiece `amount` can be stored within usize") 1467 | }, 1468 | )?; 1469 | 1470 | let mut input_buf = [0u8; OPERAND_SIZE]; 1471 | let input_view = &mut input_buf[..input_size]; 1472 | 1473 | self.read_operand(operand, input_view, ViolationSource::Read)?; 1474 | 1475 | let mut output_buf = [0u8; OPERAND_SIZE]; 1476 | let output_view = &mut output_buf[..destination_size]; 1477 | 1478 | O::subpiece(output_view, input_view, amount); 1479 | 1480 | self.write_operand(destination, &output_view)?; 1481 | 1482 | Ok(Outcome::Branch(Branch::Next)) 1483 | } 1484 | 1485 | fn pop_count( 1486 | &mut self, 1487 | destination: &Operand, 1488 | operand: &Operand, 1489 | ) -> Result, Self::Error> { 1490 | self.lift_int1( 1491 | |u| Ok(BitVec::from(u.count_ones())), 1492 | destination, 1493 | operand, 1494 | false, 1495 | ) 1496 | } 1497 | 1498 | fn intrinsic( 1499 | &mut self, 1500 | name: &str, 1501 | operands: &[Operand], 1502 | result: Option<&Operand>, 1503 | ) -> Result, Error> { 1504 | // TODO: should we trigger operand read events on intrinsics? 1505 | 1506 | let outcome = self 1507 | .intrinsics 1508 | .handle(name, &mut self.state, operands, result) 1509 | .map_err(Error::Intrinsic)?; 1510 | 1511 | Ok(match outcome { 1512 | IntrinsicAction::Pass => Outcome::Branch(Branch::Next), 1513 | IntrinsicAction::Branch(address) => Outcome::Branch(Branch::Global(address)), 1514 | IntrinsicAction::Halt(reason) => Outcome::Halt(reason), 1515 | }) 1516 | } 1517 | 1518 | fn lift(&mut self, address: A) -> Result, Error> 1519 | where 1520 | A: IntoAddress, 1521 | { 1522 | let address_value = address.into_address_value(self.state.memory_space_ref()); 1523 | let address = Address::from(&address_value); 1524 | 1525 | // begin read lock region 1526 | let rlock = self.translator_cache.read(); 1527 | 1528 | let cached = rlock.get(&address).map(|step_state| step_state.clone()); 1529 | 1530 | drop(rlock); 1531 | // end read lock region 1532 | 1533 | let step_state = if let Some(step_state) = cached { 1534 | step_state.clone() 1535 | } else { 1536 | // NOTE: possible race here, if another thread populates 1537 | // the same address. We don't really care, I suppose. 1538 | 1539 | let view = self 1540 | .state 1541 | .view_values_from(address) 1542 | .map_err(Error::State)?; 1543 | let step_state = StepState::from( 1544 | self.translator 1545 | .lift_pcode(&mut self.translator_context, address_value, view) 1546 | .map_err(|e| Error::Lift(address, e))?, 1547 | ); 1548 | 1549 | self.translator_cache 1550 | .write() 1551 | .insert(address, step_state.clone()); 1552 | 1553 | step_state 1554 | }; 1555 | 1556 | // TODO: handle outcomes 1557 | for hook in self.hooks.iter_mut() { 1558 | hook.hook_architectural_step(&mut self.state, &address, &step_state) 1559 | .map_err(Error::Hook)?; 1560 | } 1561 | 1562 | let program_counter = self.state.registers().program_counter().clone(); 1563 | self.state 1564 | .set_address(&program_counter, address) 1565 | .map_err(Error::State)?; 1566 | 1567 | Ok(step_state.into()) 1568 | } 1569 | 1570 | fn operation(&mut self, location: &Location, step: &PCodeOp) -> Result, Self::Error> { 1571 | // TODO: handle outcomes 1572 | for hook in self.hooks.iter_mut() { 1573 | hook.hook_operation_step(&mut self.state, location, step) 1574 | .map_err(Error::Hook)?; 1575 | } 1576 | 1577 | Ok(().into()) 1578 | } 1579 | 1580 | fn interpreter_space(&self) -> Arc { 1581 | self.state.memory_space() 1582 | } 1583 | } 1584 | -------------------------------------------------------------------------------- /fuguex-concrete/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod driver; 2 | 3 | pub mod hooks; 4 | 5 | pub mod interpreter; 6 | pub use interpreter::*; 7 | 8 | pub mod microx; 9 | 10 | pub mod tracker; 11 | -------------------------------------------------------------------------------- /fuguex-concrete/src/microx.rs: -------------------------------------------------------------------------------- 1 | use fugue::bytes::Order; 2 | use fugue::ir::Address; 3 | use fuguex_hooks::types::{Error, HookOutcome}; 4 | use fuguex_microx::types::{HookInvalidAccessAction, ViolationSource}; 5 | use fuguex_state::{AsState, StateOps}; 6 | use fuguex_state::pcode::{PCodeState, Error as PCodeError}; 7 | use std::marker::PhantomData; 8 | 9 | use crate::hooks::{ClonableHookConcrete, HookConcrete}; 10 | 11 | pub trait MemoryPolicy { 12 | type State; 13 | type Outcome; 14 | type Value; 15 | type Error: std::error::Error + Send + Sync; 16 | 17 | fn handle_read( 18 | &mut self, 19 | state: &mut Self::State, 20 | address: &Address, 21 | size: usize, 22 | ) -> Result>, Self::Error>; 23 | } 24 | 25 | pub struct ZeroPolicy(PhantomData<(S, O, R)>); 26 | 27 | impl Default for ZeroPolicy { 28 | fn default() -> Self { 29 | Self(PhantomData) 30 | } 31 | } 32 | 33 | impl Clone for ZeroPolicy { 34 | fn clone(&self) -> Self { 35 | Self(PhantomData) 36 | } 37 | } 38 | 39 | impl MemoryPolicy for ZeroPolicy 40 | where S: AsState>, 41 | O: Order { 42 | type State = S; 43 | type Outcome = R; 44 | type Value = u8; 45 | type Error = PCodeError; 46 | 47 | fn handle_read(&mut self, _state: &mut Self::State, _address: &Address, size: usize) -> Result>, PCodeError> { 48 | Ok(Some(vec![0u8; size])) 49 | /* 50 | let state = state.state_mut(); 51 | 52 | let addr = u64::from(address); 53 | let base_addr = u64::from(address) & !0xfffu64; 54 | 55 | let diff = addr + (size as u64) - base_addr; 56 | 57 | let r = 0x1000 - (diff % 0x1000); 58 | let d = diff / 0x1000; 59 | 60 | let size = (d * 0x1000 + r) as usize; 61 | 62 | // will be zero 63 | state.memory_mut().static_mapping( 64 | format!("microx-{:x}", base_addr), 65 | base_addr, 66 | size, 67 | ).ok(); // TODO: handle error 68 | */ 69 | } 70 | } 71 | 72 | #[derive(Debug)] 73 | pub struct PolicyHook 74 | where 75 | P: Clone + MemoryPolicy, 76 | { 77 | policy: P, 78 | marker: PhantomData<(S, O)>, 79 | } 80 | 81 | impl Clone for PolicyHook 82 | where 83 | P: Clone + MemoryPolicy, 84 | S: StateOps, 85 | { 86 | fn clone(&self) -> Self { 87 | Self { 88 | policy: self.policy.clone(), 89 | marker: PhantomData, 90 | } 91 | } 92 | } 93 | 94 | impl PolicyHook 95 | where 96 | P: Clone + MemoryPolicy, 97 | S: StateOps, 98 | { 99 | pub fn new(policy: P) -> Self { 100 | Self { 101 | policy, 102 | marker: PhantomData, 103 | } 104 | } 105 | 106 | pub fn read_memory( 107 | &mut self, 108 | state: &mut S, 109 | address: &Address, 110 | size: usize, 111 | ) -> Result>, Error> { 112 | self.policy 113 | .handle_read(state, address, size) 114 | .map_err(Error::state) 115 | } 116 | } 117 | 118 | impl HookConcrete for PolicyHook 119 | where 120 | P: Clone + MemoryPolicy + 'static, 121 | S: StateOps + 'static, 122 | E: std::error::Error + Send + Sync + 'static, 123 | O: 'static, 124 | { 125 | type State = S; 126 | type Error = E; 127 | type Outcome = O; 128 | 129 | #[allow(unused)] 130 | fn hook_invalid_memory_access( 131 | &mut self, 132 | state: &mut Self::State, 133 | address: &Address, 134 | size: usize, 135 | source: ViolationSource, 136 | ) -> Result>, Error> { 137 | if matches!(source, ViolationSource::Read) { 138 | if let Some(bytes) = self.policy.handle_read(state, address, size).map_err(Error::state)? { 139 | Ok(HookInvalidAccessAction::Value(bytes).into()) 140 | } else { 141 | Ok(HookInvalidAccessAction::Pass.into()) 142 | } 143 | } else { 144 | Ok(HookInvalidAccessAction::Skip.into()) 145 | } 146 | } 147 | } 148 | 149 | impl ClonableHookConcrete for PolicyHook 150 | where 151 | P: Clone + MemoryPolicy + 'static, 152 | S: StateOps + 'static, 153 | O: 'static, 154 | { 155 | } 156 | -------------------------------------------------------------------------------- /fuguex-concrete/src/tracker.rs: -------------------------------------------------------------------------------- 1 | use fugue::bytes::Order; 2 | use fugue::ir::il::pcode::Operand; 3 | use fugue::ir::Address; 4 | use fuguex_hooks::types::{Error, HookCBranchAction, HookOutcome, HookStepAction}; 5 | use fuguex_machine::StepState; 6 | use fuguex_state::pcode::{Error as PCodeError, PCodeState}; 7 | use fuguex_state::{AsState, StateOps}; 8 | 9 | use crate::hooks::{ClonableHookConcrete, HookConcrete}; 10 | 11 | use std::marker::PhantomData; 12 | 13 | pub struct BranchTracker { 14 | next: Address, 15 | tracked: Vec<(Address, Address, Address)>, 16 | marker: PhantomData<(S, O, R)>, 17 | } 18 | 19 | impl Default for BranchTracker { 20 | fn default() -> Self { 21 | Self { 22 | next: Address::from_value(0u64), 23 | tracked: Vec::default(), 24 | marker: PhantomData, 25 | } 26 | } 27 | } 28 | 29 | impl Clone for BranchTracker { 30 | fn clone(&self) -> Self { 31 | Self { 32 | next: self.next.clone(), 33 | tracked: self.tracked.clone(), 34 | marker: PhantomData, 35 | } 36 | } 37 | } 38 | 39 | impl HookConcrete for BranchTracker 40 | where 41 | S: AsState> + StateOps + 'static, 42 | O: Order + 'static, 43 | R: 'static, 44 | { 45 | type State = S; 46 | type Error = PCodeError; 47 | type Outcome = R; 48 | 49 | fn hook_architectural_step( 50 | &mut self, 51 | _state: &mut Self::State, 52 | _address: &Address, 53 | operation: &StepState, 54 | ) -> Result>, Error> { 55 | self.next = operation.fallthrough().into(); 56 | Ok(HookStepAction::Pass.into()) 57 | } 58 | 59 | fn hook_cbranch( 60 | &mut self, 61 | state: &mut Self::State, 62 | destination: &Operand, 63 | _condition: &Operand, 64 | ) -> Result>, Error> { 65 | let pcode_state = state.state_ref(); 66 | let address = pcode_state.program_counter_value().map_err(Error::state)?; 67 | let taken = pcode_state.get_address(destination).map_err(Error::state)?; 68 | let not_taken = self.next.clone(); 69 | 70 | self.tracked.push((address, taken, not_taken)); 71 | 72 | Ok(HookCBranchAction::Pass.into()) 73 | } 74 | } 75 | 76 | impl ClonableHookConcrete for BranchTracker 77 | where 78 | S: AsState> + StateOps + 'static, 79 | O: Order + 'static, 80 | R: 'static, 81 | { 82 | } 83 | -------------------------------------------------------------------------------- /fuguex-core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fuguex" 3 | version = "0.2.4" 4 | edition = "2018" 5 | license = "MIT" 6 | 7 | [features] 8 | concrete = ["fuguex-concrete"] 9 | 10 | loader-all = ["fuguex-loader/all"] 11 | loader-ghidra = ["fuguex-loader/ghidra"] 12 | loader-idapro = ["fuguex-loader/idapro"] 13 | loader-radare = ["fuguex-loader/radare"] 14 | 15 | [dependencies] 16 | fuguex-concrete = { optional = true, path = "../fuguex-concrete", version = "0.2", registry = "fugue" } 17 | fuguex-hooks = { path = "../fuguex-hooks", version = "0.2", registry = "fugue" } 18 | fuguex-intrinsics = { path = "../fuguex-intrinsics", version = "0.2", registry = "fugue" } 19 | fuguex-loader = { path = "../fuguex-loader", version = "0.2", registry = "fugue" } 20 | fuguex-machine = { path = "../fuguex-machine", version = "0.2", registry = "fugue" } 21 | fuguex-microx = { path = "../fuguex-microx", version = "0.1", registry = "fugue" } 22 | fuguex-state = { path = "../fuguex-state", version = "0.2", registry = "fugue" } 23 | -------------------------------------------------------------------------------- /fuguex-core/src/lib.rs: -------------------------------------------------------------------------------- 1 | #[cfg(feature = "concrete")] 2 | pub use fuguex_concrete as concrete; 3 | 4 | pub use fuguex_hooks as hooks; 5 | pub use fuguex_intrinsics as intrinsics; 6 | pub use fuguex_loader as loader; 7 | pub use fuguex_machine as machine; 8 | pub use fuguex_state as state; 9 | -------------------------------------------------------------------------------- /fuguex-hooks/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fuguex-hooks" 3 | version = "0.2.6" 4 | edition = "2018" 5 | license = "MIT" 6 | 7 | [dependencies] 8 | anyhow = "1" 9 | fugue = { version = "0.2", registry = "fugue" } 10 | fuguex-state = { path = "../fuguex-state", version = "0.2", registry = "fugue" } 11 | thiserror = "1" 12 | -------------------------------------------------------------------------------- /fuguex-hooks/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use types::*; 2 | pub mod types; 3 | -------------------------------------------------------------------------------- /fuguex-hooks/src/types.rs: -------------------------------------------------------------------------------- 1 | use fugue::ir::il::Location; 2 | use thiserror::Error; 3 | 4 | pub enum HookAction { 5 | Pass, 6 | Halt(R), 7 | } 8 | 9 | pub enum HookCBranchAction { 10 | Pass, 11 | Flip, 12 | Halt(R), 13 | } 14 | 15 | pub enum HookCallAction { 16 | Pass, 17 | Skip, 18 | Halt(R), 19 | } 20 | 21 | pub enum HookStepAction { 22 | Branch(Location), 23 | Pass, 24 | Halt(R), 25 | } 26 | 27 | pub struct HookOutcome { 28 | pub action: A, 29 | pub state_changed: bool, 30 | } 31 | 32 | impl From for HookOutcome { 33 | fn from(action: A) -> Self { 34 | Self { action, state_changed: false } 35 | } 36 | } 37 | 38 | impl HookOutcome { 39 | pub fn state_changed(self, changed: bool) -> Self { 40 | Self { state_changed: changed, ..self } 41 | } 42 | } 43 | 44 | #[derive(Debug, Error)] 45 | pub enum Error { 46 | #[error(transparent)] 47 | State(E), 48 | #[error(transparent)] 49 | Other(#[from] anyhow::Error), 50 | } 51 | 52 | impl Error where E: std::error::Error + Send + Sync + 'static { 53 | pub fn state(error: E) -> Self { 54 | Self::State(error) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /fuguex-intrinsics/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fuguex-intrinsics" 3 | version = "0.2.2" 4 | authors = ["Sam Thomas "] 5 | edition = "2018" 6 | license = "MIT" 7 | 8 | [dependencies] 9 | anyhow = "1" 10 | dyn-clone = "1" 11 | fugue = { version = "0.2", registry = "fugue" } 12 | fuguex-state = { path = "../fuguex-state", version = "0.2", registry = "fugue" } 13 | thiserror = "1" 14 | -------------------------------------------------------------------------------- /fuguex-intrinsics/src/lib.rs: -------------------------------------------------------------------------------- 1 | use fugue::ir::il::pcode::Operand; 2 | use fugue::ir::AddressValue; 3 | 4 | use fuguex_state::State; 5 | 6 | use std::collections::HashMap; 7 | 8 | use thiserror::Error; 9 | 10 | #[derive(Debug, Error)] 11 | pub enum Error { 12 | #[error(transparent)] 13 | State(E), 14 | #[error(transparent)] 15 | Other(#[from] anyhow::Error), 16 | } 17 | 18 | impl Error 19 | where 20 | E: std::error::Error + 'static, 21 | { 22 | pub fn state(error: E) -> Self { 23 | Self::State(error) 24 | } 25 | } 26 | 27 | #[derive(Clone)] 28 | pub enum IntrinsicAction { 29 | Pass, 30 | Branch(AddressValue), 31 | Halt(O), 32 | } 33 | 34 | pub trait IntrinsicBehaviour: dyn_clone::DynClone { 35 | type Outcome; 36 | type State: State; 37 | 38 | fn intrinsic(&self) -> &str; 39 | 40 | fn initialise( 41 | &mut self, 42 | #[allow(unused)] state: &Self::State, 43 | ) -> Result<(), Error<::Error>> { 44 | Ok(()) 45 | } 46 | 47 | fn handle_intrinsic( 48 | &mut self, 49 | state: &mut Self::State, 50 | inputs: &[Operand], 51 | output: Option<&Operand>, 52 | ) -> Result, Error<::Error>>; 53 | } 54 | dyn_clone::clone_trait_object!( IntrinsicBehaviour where State: fuguex_state::State); 55 | 56 | #[derive(Clone)] 57 | pub struct IntrinsicHandler { 58 | handlers: HashMap>>, 59 | default_action: IntrinsicAction, 60 | } 61 | 62 | impl Default for IntrinsicHandler { 63 | fn default() -> Self { 64 | Self::new() 65 | } 66 | } 67 | 68 | impl IntrinsicHandler { 69 | pub fn new() -> Self { 70 | Self::new_with(IntrinsicAction::Pass) 71 | } 72 | 73 | pub fn new_with(default_action: IntrinsicAction) -> Self { 74 | Self { 75 | handlers: HashMap::new(), 76 | default_action, 77 | } 78 | } 79 | 80 | pub fn register + 'static>( 81 | &mut self, 82 | behaviour: IN, 83 | state: &S, 84 | ) -> Result<(), Error> { 85 | let mut behaviour = behaviour; 86 | behaviour.initialise(state)?; 87 | 88 | let name = behaviour.intrinsic().to_owned(); 89 | 90 | self.handlers.insert(name, Box::new(behaviour)); 91 | 92 | Ok(()) 93 | } 94 | 95 | pub fn handle( 96 | &mut self, 97 | name: &str, 98 | state: &mut S, 99 | inputs: &[Operand], 100 | output: Option<&Operand>, 101 | ) -> Result, Error> { 102 | if let Some(handler) = self.handlers.get_mut(name) { 103 | handler.handle_intrinsic(state, inputs, output) 104 | } else { 105 | Ok(self.default_action.clone()) 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /fuguex-loader/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fuguex-loader" 3 | version = "0.2.4" 4 | edition = "2018" 5 | 6 | [features] 7 | idapro = ["fugue-idapro"] 8 | ghidra = ["fugue-ghidra"] 9 | radare = ["fugue-radare"] 10 | all = ["idapro", "ghidra", "radare"] 11 | default = [] 12 | 13 | [dependencies] 14 | either = "1" 15 | fugue = { version = "0.2", registry = "fugue" } 16 | fugue-ghidra = { version = "0.2", registry = "fugue", optional = true } 17 | fugue-idapro = { version = "0.2", registry = "fugue", optional = true } 18 | fugue-radare = { version = "0.2", registry = "fugue", optional = true } 19 | fuguex-state = { path = "../fuguex-state", version = "0.2", registry = "fugue" } 20 | thiserror = "1" 21 | -------------------------------------------------------------------------------- /fuguex-loader/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub use either::Either; 2 | 3 | use fugue::bytes::Order; 4 | use fugue::db::{Database, DatabaseImporter, Segment}; 5 | use fugue::ir::{LanguageDB, Translator}; 6 | use fugue::ir::convention::Convention; 7 | 8 | use fuguex_state::flat::FlatState; 9 | use fuguex_state::paged::{PagedState, Segment as LoadedSegment}; 10 | use fuguex_state::pcode::PCodeState; 11 | 12 | #[cfg(feature = "idapro")] 13 | use fugue_idapro as idapro; 14 | #[cfg(feature = "ghidra")] 15 | use fugue_ghidra as ghidra; 16 | #[cfg(feature = "radare")] 17 | use fugue_radare as radare; 18 | 19 | use std::path::Path; 20 | use std::sync::Arc; 21 | 22 | use thiserror::Error; 23 | 24 | #[derive(Debug, Error)] 25 | pub enum Error { 26 | #[error("database import: {0}")] 27 | Import(#[from] fugue::db::Error), 28 | } 29 | 30 | pub trait LoaderMapping { 31 | fn database(&self) -> Option> { 32 | None 33 | } 34 | 35 | fn translator(&self) -> Arc; 36 | fn into_state(self) -> S; 37 | } 38 | 39 | #[derive(Clone)] 40 | pub struct MappedDatabase { 41 | database: Arc, 42 | state: S, 43 | translator: Arc, 44 | } 45 | 46 | impl MappedDatabase> { 47 | pub fn from_database_with(database: Database, mut segment_filter: F) -> Self 48 | where F: FnMut(&Segment) -> bool { 49 | let translator = database.default_translator(); 50 | let space = translator.manager().default_space(); 51 | let mut backing = Vec::default(); 52 | let ivt = database.segments().iter().filter(|(_, v)| segment_filter(v)).map(|(k, v)| { 53 | let kv = (translator.address(*k.start()).into()..translator.address(1 + *k.end()).into(), 54 | LoadedSegment::new(v.name(), backing.len())); 55 | 56 | backing.extend_from_slice(v.bytes()); 57 | 58 | let diff = (1 + *k.end() - k.start()) as usize; 59 | if v.bytes().len() < diff { 60 | let to_add = diff - v.bytes().len(); 61 | backing.resize_with(backing.len() + to_add, Default::default); 62 | } 63 | 64 | kv 65 | }).collect::>(); 66 | 67 | let flat = FlatState::from_vec(space, backing); 68 | let state = PagedState::from_parts(ivt.into_iter(), flat); 69 | 70 | Self { 71 | database: Arc::new(database), 72 | translator: Arc::new(translator), 73 | state, 74 | } 75 | } 76 | 77 | pub fn from_database(database: Database) -> Self { 78 | Self::from_database_with(database, |_| true) 79 | } 80 | 81 | pub fn from_path_with(path: P, language_db: &LanguageDB, segment_filter: F) -> Result 82 | where P: AsRef, 83 | F: FnMut(&Segment) -> bool { 84 | #[allow(unused_mut)] 85 | let mut dbi = DatabaseImporter::new(path); 86 | 87 | #[cfg(feature = "idapro")] 88 | dbi.register_backend(idapro::IDA::new().unwrap_or_default()); 89 | 90 | #[cfg(feature = "ghidra")] 91 | dbi.register_backend(ghidra::Ghidra::new().unwrap_or_default()); 92 | 93 | #[cfg(feature = "radare")] 94 | dbi.register_backend(radare::Radare::new().unwrap_or_default()); 95 | 96 | let db = dbi.import(language_db)?; 97 | 98 | Ok(Self::from_database_with(db, segment_filter)) 99 | } 100 | 101 | pub fn from_path

(path: P, language_db: &LanguageDB) -> Result 102 | where P: AsRef { 103 | Self::from_path_with(path, language_db, |_| true) 104 | } 105 | 106 | pub fn pcode_state(self, convention: &Convention) -> MappedDatabase> { 107 | MappedDatabase { 108 | state: PCodeState::new(self.state, &self.translator, convention), 109 | database: self.database, 110 | translator: self.translator, 111 | } 112 | } 113 | 114 | pub fn pcode_state_with>(self, convention: C) -> Either>, Self> { 115 | let convention = convention.as_ref(); 116 | if let Some(convention) = self.translator.compiler_conventions().get(convention) { 117 | Either::Left(MappedDatabase { 118 | state: PCodeState::new(self.state, &self.translator, convention), 119 | database: self.database, 120 | translator: self.translator, 121 | }) 122 | } else { 123 | Either::Right(self) 124 | } 125 | } 126 | } 127 | 128 | impl LoaderMapping for MappedDatabase { 129 | fn database(&self) -> Option> { 130 | Some(self.database.clone()) 131 | } 132 | 133 | fn translator(&self) -> Arc { 134 | self.translator.clone() 135 | } 136 | 137 | fn into_state(self) -> S { 138 | self.state 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /fuguex-machine/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fuguex-machine" 3 | version = "0.2.10" 4 | edition = "2018" 5 | license = "MIT" 6 | 7 | [dependencies] 8 | fugue = { version = "0.2", registry = "fugue" } 9 | fuguex-state = { path = "../fuguex-state", version = "0.2", registry = "fugue" } 10 | serde = { version = "1", features = ["derive"] } 11 | thiserror = "1" 12 | -------------------------------------------------------------------------------- /fuguex-machine/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod machine; 2 | pub use machine::Machine; 3 | 4 | pub mod traits; 5 | pub use traits::*; 6 | 7 | pub mod types; 8 | pub use types::*; 9 | -------------------------------------------------------------------------------- /fuguex-machine/src/machine.rs: -------------------------------------------------------------------------------- 1 | use fugue::ir::{AddressValue, IntoAddress}; 2 | use fugue::ir::il::Location; 3 | use fugue::ir::il::pcode::{PCode, PCodeOp}; 4 | 5 | use crate::traits::Interpreter; 6 | use crate::types::{Bound, Branch, BranchOutcome, OrOutcome, Outcome, StepOutcome, StepState}; 7 | 8 | #[derive(Clone)] 9 | pub struct Machine { 10 | interpreter: I, 11 | step_state: StepState, 12 | ignore_errors: bool, 13 | } 14 | 15 | impl From for Machine where I: Interpreter { 16 | fn from(interpreter: I) -> Self { 17 | // NO-OP to avoid wrapping in an option in absence of a Default 18 | // for PCode 19 | let step_state = StepState::from(PCode::nop( 20 | AddressValue::new(interpreter.interpreter_space(), 0), 21 | 0, 22 | )); 23 | 24 | Self { 25 | interpreter, 26 | step_state, 27 | ignore_errors: false, 28 | } 29 | } 30 | } 31 | 32 | impl Machine where I: Interpreter { 33 | #[inline(always)] 34 | pub fn new(interpreter: I) -> Self { 35 | Self::from(interpreter) 36 | } 37 | 38 | #[inline(always)] 39 | pub fn new_with(interpreter: I, ignore_errors: bool) -> Self { 40 | let mut machine = Self::new(interpreter); 41 | machine.ignore_errors = ignore_errors; 42 | machine 43 | } 44 | 45 | #[deprecated(since = "2.10", note = "use Machine::ignore_errors")] 46 | pub fn set_ignore_errors(&mut self, ignore_errors: bool) { 47 | self.ignore_errors = ignore_errors; 48 | } 49 | 50 | pub fn ignore_errors(&mut self, ignore_errors: bool) { 51 | self.ignore_errors = ignore_errors; 52 | } 53 | 54 | pub fn step(&mut self, location: L) -> Result, I::Error> 55 | where L: Into { 56 | 57 | let location = location.into(); 58 | let address = location.address(); 59 | 60 | match self.interpreter.lift(&*address)? { 61 | OrOutcome::Branch(location) => return self.step(location), 62 | OrOutcome::Continue(step_state) => { 63 | self.step_state = step_state.with_location(&location); 64 | }, 65 | OrOutcome::Halt(outcome) => return Ok(StepOutcome::Halt(outcome)), 66 | } 67 | 68 | while let Some(op) = self.step_state.current() { 69 | match self.interpreter.operation(&self.step_state.location(), op)? { 70 | OrOutcome::Branch(location) => return self.step(location), 71 | OrOutcome::Halt(outcome) => return Ok(StepOutcome::Halt(outcome)), 72 | OrOutcome::Continue(_) => (), 73 | }; 74 | 75 | let action_res = match op { 76 | PCodeOp::Copy { ref source, ref destination } => { 77 | self.interpreter.copy(source, destination) 78 | }, 79 | PCodeOp::Load { ref source, ref destination, space } => { 80 | self.interpreter.load(source, destination, space.clone()) 81 | }, 82 | PCodeOp::Store { ref source, ref destination, space } => { 83 | self.interpreter.store(source, destination, space.clone()) 84 | }, 85 | PCodeOp::Branch { ref destination } => { 86 | self.interpreter.branch(destination) 87 | }, 88 | PCodeOp::CBranch { ref destination, ref condition } => { 89 | self.interpreter.cbranch(destination, condition) 90 | }, 91 | PCodeOp::IBranch { ref destination } => { 92 | self.interpreter.ibranch(destination) 93 | }, 94 | PCodeOp::Call { ref destination } => { 95 | self.interpreter.call(destination) 96 | }, 97 | PCodeOp::ICall { ref destination } => { 98 | self.interpreter.icall(destination) 99 | }, 100 | PCodeOp::Intrinsic { name, ref operands, ref result } => { 101 | self.interpreter.intrinsic(name, operands.as_ref(), result.as_ref()) 102 | }, 103 | PCodeOp::Return { ref destination } => { 104 | self.interpreter.return_(destination) 105 | }, 106 | 107 | PCodeOp::IntEq { ref result, operands: [ref operand1, ref operand2] } => { 108 | self.interpreter.int_eq(result, operand1, operand2) 109 | }, 110 | PCodeOp::IntNotEq { ref result, operands: [ref operand1, ref operand2] } => { 111 | self.interpreter.int_not_eq(result, operand1, operand2) 112 | }, 113 | PCodeOp::IntLess { ref result, operands: [ref operand1, ref operand2] } => { 114 | self.interpreter.int_less(result, operand1, operand2) 115 | }, 116 | PCodeOp::IntLessEq { ref result, operands: [ref operand1, ref operand2] } => { 117 | self.interpreter.int_less_eq(result, operand1, operand2) 118 | }, 119 | PCodeOp::IntSLess { ref result, operands: [ref operand1, ref operand2] } => { 120 | self.interpreter.int_sless(result, operand1, operand2) 121 | }, 122 | PCodeOp::IntSLessEq { ref result, operands: [ref operand1, ref operand2] } => { 123 | self.interpreter.int_sless_eq(result, operand1, operand2) 124 | }, 125 | 126 | PCodeOp::IntZExt { ref result, ref operand } => { 127 | self.interpreter.int_zext(result, operand) 128 | }, 129 | PCodeOp::IntSExt { ref result, ref operand } => { 130 | self.interpreter.int_sext(result, operand) 131 | }, 132 | 133 | PCodeOp::IntAdd { ref result, operands: [ref operand1, ref operand2] } => { 134 | self.interpreter.int_add(result, operand1, operand2) 135 | }, 136 | PCodeOp::IntSub { ref result, operands: [ref operand1, ref operand2] } => { 137 | self.interpreter.int_sub(result, operand1, operand2) 138 | }, 139 | PCodeOp::IntCarry { ref result, operands: [ref operand1, ref operand2] } => { 140 | self.interpreter.int_carry(result, operand1, operand2) 141 | }, 142 | PCodeOp::IntSCarry { ref result, operands: [ref operand1, ref operand2] } => { 143 | self.interpreter.int_scarry(result, operand1, operand2) 144 | }, 145 | PCodeOp::IntSBorrow { ref result, operands: [ref operand1, ref operand2] } => { 146 | self.interpreter.int_sborrow(result, operand1, operand2) 147 | }, 148 | 149 | PCodeOp::IntNeg { ref result, ref operand } => { 150 | self.interpreter.int_neg(result, operand) 151 | }, 152 | PCodeOp::IntNot { ref result, ref operand } => { 153 | self.interpreter.int_not(result, operand) 154 | }, 155 | 156 | PCodeOp::IntXor { ref result, operands: [ref operand1, ref operand2] } => { 157 | self.interpreter.int_xor(result, operand1, operand2) 158 | }, 159 | PCodeOp::IntAnd { ref result, operands: [ref operand1, ref operand2] } => { 160 | self.interpreter.int_and(result, operand1, operand2) 161 | }, 162 | PCodeOp::IntOr { ref result, operands: [ref operand1, ref operand2] } => { 163 | self.interpreter.int_or(result, operand1, operand2) 164 | }, 165 | PCodeOp::IntLeftShift { ref result, operands: [ref operand1, ref operand2] } => { 166 | self.interpreter.int_left_shift(result, operand1, operand2) 167 | }, 168 | PCodeOp::IntRightShift { ref result, operands: [ref operand1, ref operand2] } => { 169 | self.interpreter.int_right_shift(result, operand1, operand2) 170 | }, 171 | PCodeOp::IntSRightShift { ref result, operands: [ref operand1, ref operand2] } => { 172 | self.interpreter.int_sright_shift(result, operand1, operand2) 173 | }, 174 | 175 | PCodeOp::IntMul { ref result, operands: [ref operand1, ref operand2] } => { 176 | self.interpreter.int_mul(result, operand1, operand2) 177 | }, 178 | PCodeOp::IntDiv { ref result, operands: [ref operand1, ref operand2] } => { 179 | self.interpreter.int_div(result, operand1, operand2) 180 | }, 181 | PCodeOp::IntSDiv { ref result, operands: [ref operand1, ref operand2] } => { 182 | self.interpreter.int_sdiv(result, operand1, operand2) 183 | }, 184 | PCodeOp::IntRem { ref result, operands: [ref operand1, ref operand2] } => { 185 | self.interpreter.int_rem(result, operand1, operand2) 186 | }, 187 | PCodeOp::IntSRem { ref result, operands: [ref operand1, ref operand2] } => { 188 | self.interpreter.int_srem(result, operand1, operand2) 189 | }, 190 | 191 | PCodeOp::BoolNot { ref result, ref operand } => { 192 | self.interpreter.bool_not(result, operand) 193 | }, 194 | PCodeOp::BoolXor { ref result, operands: [ref operand1, ref operand2] } => { 195 | self.interpreter.bool_xor(result, operand1, operand2) 196 | }, 197 | PCodeOp::BoolAnd { ref result, operands: [ref operand1, ref operand2] } => { 198 | self.interpreter.bool_and(result, operand1, operand2) 199 | }, 200 | PCodeOp::BoolOr { ref result, operands: [ref operand1, ref operand2] } => { 201 | self.interpreter.bool_or(result, operand1, operand2) 202 | }, 203 | 204 | PCodeOp::FloatEq { ref result, operands: [ref operand1, ref operand2] } => { 205 | self.interpreter.float_eq(result, operand1, operand2) 206 | }, 207 | PCodeOp::FloatNotEq { ref result, operands: [ref operand1, ref operand2] } => { 208 | self.interpreter.float_not_eq(result, operand1, operand2) 209 | }, 210 | PCodeOp::FloatLess { ref result, operands: [ref operand1, ref operand2] } => { 211 | self.interpreter.float_less(result, operand1, operand2) 212 | }, 213 | PCodeOp::FloatLessEq { ref result, operands: [ref operand1, ref operand2] } => { 214 | self.interpreter.float_less_eq(result, operand1, operand2) 215 | }, 216 | 217 | PCodeOp::FloatIsNaN { ref result, ref operand } => { 218 | self.interpreter.float_is_nan(result, operand) 219 | }, 220 | 221 | PCodeOp::FloatAdd { ref result, operands: [ref operand1, ref operand2] } => { 222 | self.interpreter.float_add(result, operand1, operand2) 223 | }, 224 | PCodeOp::FloatDiv { ref result, operands: [ref operand1, ref operand2] } => { 225 | self.interpreter.float_div(result, operand1, operand2) 226 | }, 227 | PCodeOp::FloatMul { ref result, operands: [ref operand1, ref operand2] } => { 228 | self.interpreter.float_mul(result, operand1, operand2) 229 | }, 230 | PCodeOp::FloatSub { ref result, operands: [ref operand1, ref operand2] } => { 231 | self.interpreter.float_sub(result, operand1, operand2) 232 | }, 233 | 234 | PCodeOp::FloatNeg { ref result, ref operand } => { 235 | self.interpreter.float_neg(result, operand) 236 | }, 237 | PCodeOp::FloatAbs { ref result, ref operand } => { 238 | self.interpreter.float_abs(result, operand) 239 | }, 240 | PCodeOp::FloatSqrt { ref result, ref operand } => { 241 | self.interpreter.float_sqrt(result, operand) 242 | }, 243 | 244 | PCodeOp::FloatOfInt { ref result, ref operand } => { 245 | self.interpreter.float_of_int(result, operand) 246 | }, 247 | PCodeOp::FloatOfFloat { ref result, ref operand } => { 248 | self.interpreter.float_of_float(result, operand) 249 | }, 250 | 251 | PCodeOp::FloatTruncate { ref result, ref operand } => { 252 | self.interpreter.float_truncate(result, operand) 253 | }, 254 | PCodeOp::FloatCeiling { ref result, ref operand } => { 255 | self.interpreter.float_ceiling(result, operand) 256 | }, 257 | PCodeOp::FloatFloor { ref result, ref operand } => { 258 | self.interpreter.float_floor(result, operand) 259 | }, 260 | PCodeOp::FloatRound { ref result, ref operand } => { 261 | self.interpreter.float_round(result, operand) 262 | }, 263 | 264 | PCodeOp::Subpiece { ref result, ref operand, ref amount } => { 265 | self.interpreter.subpiece(result, operand, amount) 266 | }, 267 | PCodeOp::PopCount { ref result, ref operand } => { 268 | self.interpreter.pop_count(result, operand) 269 | }, 270 | 271 | PCodeOp::Skip => { 272 | self.interpreter.skip() 273 | }, 274 | }; 275 | 276 | match action_res { 277 | Err(e) => { 278 | if self.ignore_errors { 279 | if let BranchOutcome::Global(address) = self.step_state.branch(&Branch::Next) { 280 | return Ok(StepOutcome::Branch(address)) 281 | } else { 282 | continue 283 | } 284 | } else { 285 | return Err(e); 286 | } 287 | }, 288 | Ok(action) => { 289 | match action { 290 | Outcome::Halt(outcome) => { 291 | return Ok(StepOutcome::Halt(outcome)) 292 | }, 293 | Outcome::Branch(ref branch) => if let BranchOutcome::Global(address) = self.step_state.branch(branch) { 294 | return Ok(StepOutcome::Branch(address)) 295 | } else { 296 | continue 297 | }, 298 | } 299 | }, 300 | 301 | } 302 | } 303 | 304 | Ok(StepOutcome::Branch(self.step_state.fallthrough())) 305 | } 306 | 307 | pub fn step_until(&mut self, location: L, until: Bound) -> Result<(Bound, StepOutcome), I::Error> 308 | where L: Into, 309 | B: IntoAddress { 310 | 311 | let space = self.interpreter.interpreter_space(); 312 | let mut bound = until.in_space(&*space); 313 | let mut location = location.into(); 314 | 315 | // Check if still within bound 316 | while !bound.reached(&*location.address()) { 317 | bound = bound.deplete(); 318 | // Execute the instruction at the current location 319 | match self.step(location)? { 320 | StepOutcome::Branch(next_address) => { 321 | location = Location::from(next_address); 322 | }, 323 | v => { 324 | return Ok((bound, v)) 325 | } 326 | } 327 | } 328 | 329 | Ok((bound, StepOutcome::Reached)) 330 | } 331 | 332 | pub fn step_state(&self) -> &StepState { 333 | &self.step_state 334 | } 335 | 336 | pub fn step_state_mut(&mut self) -> &mut StepState { 337 | &mut self.step_state 338 | } 339 | 340 | pub fn interpreter(&self) -> &I { 341 | &self.interpreter 342 | } 343 | 344 | pub fn interpreter_mut(&mut self) -> &mut I { 345 | &mut self.interpreter 346 | } 347 | } 348 | -------------------------------------------------------------------------------- /fuguex-machine/src/traits.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use fugue::ir::{AddressSpace, IntoAddress}; 4 | use fugue::ir::il::Location; 5 | use fugue::ir::il::pcode::{Operand, PCodeOp}; 6 | use fugue::ir::space::AddressSpaceId; 7 | 8 | use crate::types::{Branch, Outcome, OrOutcome, StepState}; 9 | 10 | pub trait Interpreter { 11 | type State; 12 | type Error: std::error::Error; 13 | type Outcome; 14 | 15 | fn fork(&self) -> Self; 16 | fn restore(&mut self, other: &Self); 17 | 18 | fn copy(&mut self, source: &Operand, destination: &Operand) -> Result, Self::Error>; 19 | fn load(&mut self, source: &Operand, destination: &Operand, space: AddressSpaceId) -> Result, Self::Error>; 20 | fn store(&mut self, source: &Operand, destination: &Operand, space: AddressSpaceId) -> Result, Self::Error>; 21 | 22 | fn branch(&mut self, destination: &Operand) -> Result, Self::Error>; 23 | fn cbranch(&mut self, destination: &Operand, condition: &Operand) -> Result, Self::Error>; 24 | fn ibranch(&mut self, destination: &Operand) -> Result, Self::Error>; 25 | 26 | fn call(&mut self, destination: &Operand) -> Result, Self::Error>; 27 | fn icall(&mut self, destination: &Operand) -> Result, Self::Error>; 28 | fn intrinsic(&mut self, name: &str, operands: &[Operand], result: Option<&Operand>) -> Result, Self::Error>; 29 | fn return_(&mut self, destination: &Operand) -> Result, Self::Error>; 30 | 31 | fn int_eq(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 32 | fn int_not_eq(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 33 | fn int_less(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 34 | fn int_less_eq(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 35 | fn int_sless(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 36 | fn int_sless_eq(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 37 | 38 | fn int_zext(&mut self, destination: &Operand, operand: &Operand) -> Result, Self::Error>; 39 | fn int_sext(&mut self, destination: &Operand, operand: &Operand) -> Result, Self::Error>; 40 | 41 | fn int_add(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 42 | fn int_sub(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 43 | fn int_carry(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 44 | fn int_scarry(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 45 | fn int_sborrow(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 46 | 47 | fn int_neg(&mut self, destination: &Operand, operand: &Operand) -> Result, Self::Error>; 48 | fn int_not(&mut self, destination: &Operand, operand: &Operand) -> Result, Self::Error>; 49 | 50 | fn int_xor(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 51 | fn int_and(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 52 | fn int_or(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 53 | fn int_left_shift(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 54 | fn int_right_shift(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 55 | fn int_sright_shift(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 56 | 57 | fn int_mul(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 58 | fn int_div(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 59 | fn int_sdiv(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 60 | fn int_rem(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 61 | fn int_srem(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 62 | 63 | fn bool_not(&mut self, destination: &Operand, operand: &Operand) -> Result, Self::Error>; 64 | fn bool_xor(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 65 | fn bool_and(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 66 | fn bool_or(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 67 | 68 | fn float_eq(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 69 | fn float_not_eq(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 70 | fn float_less(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 71 | fn float_less_eq(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 72 | 73 | fn float_is_nan(&mut self, destination: &Operand, operand: &Operand) -> Result, Self::Error>; 74 | 75 | fn float_add(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 76 | fn float_div(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 77 | fn float_mul(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 78 | fn float_sub(&mut self, destination: &Operand, operand1: &Operand, operand2: &Operand) -> Result, Self::Error>; 79 | 80 | fn float_neg(&mut self, destination: &Operand, operand: &Operand) -> Result, Self::Error>; 81 | fn float_abs(&mut self, destination: &Operand, operand: &Operand) -> Result, Self::Error>; 82 | fn float_sqrt(&mut self, destination: &Operand, operand: &Operand) -> Result, Self::Error>; 83 | 84 | fn float_of_int(&mut self, destination: &Operand, operand: &Operand) -> Result, Self::Error>; 85 | fn float_of_float(&mut self, destination: &Operand, operand: &Operand) -> Result, Self::Error>; 86 | 87 | fn float_truncate(&mut self, destination: &Operand, operand: &Operand) -> Result, Self::Error>; 88 | fn float_ceiling(&mut self, destination: &Operand, operand: &Operand) -> Result, Self::Error>; 89 | fn float_floor(&mut self, destination: &Operand, operand: &Operand) -> Result, Self::Error>; 90 | fn float_round(&mut self, destination: &Operand, operand: &Operand) -> Result, Self::Error>; 91 | 92 | fn subpiece(&mut self, destination: &Operand, operand: &Operand, amount: &Operand) -> Result, Self::Error>; 93 | fn pop_count(&mut self, destination: &Operand, operand: &Operand) -> Result, Self::Error>; 94 | 95 | fn skip(&mut self) -> Result, Self::Error> { 96 | Ok(Outcome::Branch(Branch::Next)) 97 | } 98 | 99 | fn lift(&mut self, address: A) -> Result, Self::Error> 100 | where A: IntoAddress; 101 | 102 | #[allow(unused)] 103 | fn operation(&mut self, location: &Location, step: &PCodeOp) -> Result, Self::Error> { 104 | Ok(().into()) 105 | } 106 | 107 | fn interpreter_space(&self) -> Arc; 108 | } 109 | -------------------------------------------------------------------------------- /fuguex-machine/src/types.rs: -------------------------------------------------------------------------------- 1 | use fugue::ir::{AddressSpace, AddressValue, IntoAddress}; 2 | use fugue::ir::il::Location; 3 | use fugue::ir::il::pcode::{PCode, PCodeOp}; 4 | 5 | use std::sync::Arc; 6 | 7 | #[derive(Debug, Clone)] 8 | #[derive(serde::Deserialize, serde::Serialize)] 9 | pub enum Bound { 10 | Address(A), 11 | Steps(usize), 12 | Unbounded, 13 | } 14 | 15 | impl Bound where A: IntoAddress { 16 | pub fn address(address: A) -> Bound { 17 | Self::Address(address) 18 | } 19 | 20 | pub fn in_space(self, space: &AddressSpace) -> Bound { 21 | match self { 22 | Self::Address(address) => Bound::Address(address.into_address_value(&*space)), 23 | Self::Steps(steps) => Bound::Steps(steps), 24 | Self::Unbounded => Bound::Unbounded, 25 | } 26 | } 27 | } 28 | 29 | impl Bound { 30 | pub fn steps(steps: usize) -> Bound { 31 | Self::Steps(steps) 32 | } 33 | 34 | pub fn unbounded() -> Bound { 35 | Self::Unbounded 36 | } 37 | 38 | // Decrease step count 39 | // Used for counting down from the specified step count 40 | pub fn deplete(self) -> Self { 41 | if let Self::Steps(steps) = self { 42 | Self::Steps(steps.checked_sub(1).unwrap_or(0)) 43 | } else { 44 | self 45 | } 46 | } 47 | 48 | pub fn reached(&self, address: &AddressValue) -> bool { 49 | match self { 50 | Self::Address(ref target) => target == address, 51 | Self::Steps(steps) => *steps == 0, 52 | Self::Unbounded => false, 53 | } 54 | } 55 | } 56 | 57 | #[derive(Debug, Clone)] 58 | #[derive(serde::Deserialize, serde::Serialize)] 59 | pub enum Branch { 60 | Next, 61 | Local(isize), 62 | Global(AddressValue), 63 | } 64 | 65 | #[derive(Debug, Clone)] 66 | #[derive(serde::Deserialize, serde::Serialize)] 67 | pub enum Outcome { 68 | Halt(R), 69 | Branch(Branch), 70 | } 71 | 72 | #[derive(Debug, Clone)] 73 | #[derive(serde::Deserialize, serde::Serialize)] 74 | pub enum OrOutcome { 75 | Halt(R), 76 | Branch(Location), 77 | Continue(S), 78 | } 79 | 80 | impl From for OrOutcome { 81 | fn from(t: T) -> Self { 82 | Self::Continue(t) 83 | } 84 | } 85 | 86 | #[derive(Debug, Clone)] 87 | #[derive(serde::Deserialize, serde::Serialize)] 88 | pub enum StepOutcome { 89 | Halt(R), 90 | Reached, 91 | Branch(AddressValue), 92 | } 93 | 94 | #[derive(Debug, Clone)] 95 | #[derive(serde::Deserialize, serde::Serialize)] 96 | pub enum BranchOutcome { 97 | Local, 98 | Global(AddressValue), 99 | } 100 | 101 | #[derive(Debug, Clone)] 102 | #[derive(serde::Deserialize, serde::Serialize)] 103 | pub struct StepState { 104 | pcode: Arc, 105 | position: usize, 106 | } 107 | 108 | impl From for StepState { 109 | fn from(pcode: PCode) -> Self { 110 | Self { 111 | pcode: Arc::new(pcode), 112 | position: 0, 113 | } 114 | } 115 | } 116 | 117 | impl StepState { 118 | #[inline(always)] 119 | pub fn address(&self) -> AddressValue { 120 | self.pcode.address() 121 | } 122 | 123 | pub fn location(&self) -> Location { 124 | Location::new(self.pcode.address(), self.position) 125 | } 126 | 127 | pub fn operations(&self) -> &PCode { 128 | &*self.pcode 129 | } 130 | 131 | pub fn with_location(self, location: &Location) -> Self { 132 | assert_eq!(self.pcode.address, *location.address()); 133 | Self { position: location.position(), ..self } 134 | } 135 | 136 | #[inline(always)] 137 | pub fn current(&self) -> Option<&PCodeOp> { 138 | self.pcode.operations().get(self.position) 139 | } 140 | 141 | pub fn fallthrough(&self) -> AddressValue { 142 | self.address() + self.pcode.length() 143 | } 144 | 145 | pub fn branch(&mut self, action: &Branch) -> BranchOutcome { 146 | match action { 147 | Branch::Next => { 148 | self.position += 1; 149 | }, 150 | Branch::Local(offset) => { 151 | if offset.is_negative() { 152 | let abs = offset.wrapping_abs() as usize; 153 | if abs <= self.position { 154 | self.position -= abs; 155 | } else { 156 | panic!("negative local branch out of range") 157 | } 158 | } else { 159 | self.position += *offset as usize; 160 | } 161 | }, 162 | Branch::Global(address) => { 163 | return BranchOutcome::Global(address.clone()) 164 | } 165 | } 166 | 167 | if self.position < self.pcode.operations.len() { 168 | BranchOutcome::Local 169 | } else { 170 | BranchOutcome::Global(self.fallthrough()) 171 | } 172 | } 173 | 174 | pub fn branch_location(&self, action: Branch) -> Location { 175 | match action { 176 | Branch::Next => { 177 | if self.position + 1 < self.pcode.operations.len() { 178 | Location::new(self.address(), self.position + 1) 179 | } else { 180 | Location::from(self.fallthrough()) 181 | } 182 | }, 183 | Branch::Local(offset) => { 184 | let npos = if offset.is_negative() { 185 | let abs = offset.wrapping_abs() as usize; 186 | if abs <= self.position { 187 | self.position - abs 188 | } else { 189 | panic!("negative local branch out of range") 190 | } 191 | } else { 192 | self.position + offset as usize 193 | }; 194 | 195 | if npos < self.pcode.operations.len() { 196 | Location::new(self.address(), npos) 197 | } else { 198 | Location::from(self.fallthrough()) 199 | } 200 | }, 201 | Branch::Global(address) => { 202 | Location::from(address) 203 | } 204 | } 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /fuguex-microx/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fuguex-microx" 3 | version = "0.1.3" 4 | edition = "2021" 5 | license = "MIT" 6 | 7 | [dependencies] 8 | fugue = { version = "0.2", registry = "fugue" } 9 | fuguex-hooks = { path = "../fuguex-hooks", version = "0.2", registry = "fugue" } 10 | fuguex-state = { path = "../fuguex-state", version = "0.2", registry = "fugue" } 11 | thiserror = "1" 12 | -------------------------------------------------------------------------------- /fuguex-microx/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod traits; 2 | pub mod types; 3 | 4 | pub use traits::*; 5 | pub use types::ViolationSource; 6 | -------------------------------------------------------------------------------- /fuguex-microx/src/traits.rs: -------------------------------------------------------------------------------- 1 | use fugue::ir::Address; 2 | 3 | use fuguex_hooks::types::{Error, HookOutcome}; 4 | use fuguex_state::StateOps; 5 | 6 | use crate::types::{HookInvalidAccessAction, ViolationSource}; 7 | 8 | pub trait HookInvalidMemoryAccess { 9 | type State: StateOps; 10 | type Outcome; 11 | 12 | type Error: std::error::Error + Send + Sync + 'static; 13 | 14 | fn hook_invalid_memory_access( 15 | &mut self, 16 | state: &mut Self::State, 17 | address: &Address, 18 | size: usize, 19 | source: ViolationSource, 20 | ) -> Result< 21 | HookOutcome::Value>>, 22 | Error, 23 | >; 24 | } 25 | -------------------------------------------------------------------------------- /fuguex-microx/src/types.rs: -------------------------------------------------------------------------------- 1 | #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] 2 | pub enum ViolationSource { 3 | Read, 4 | Write, 5 | ReadVia, 6 | WriteVia, 7 | } 8 | 9 | pub enum HookInvalidAccessAction { 10 | Pass, 11 | Skip, // for writes 12 | Halt(R), 13 | Value(Vec), // for reads 14 | } 15 | 16 | impl HookInvalidAccessAction { 17 | pub fn is_value(&self) -> bool { 18 | matches!(self, Self::Value(_)) 19 | } 20 | 21 | pub fn is_halt(&self) -> bool { 22 | matches!(self, Self::Halt(_)) 23 | } 24 | 25 | pub fn is_pass(&self) -> bool { 26 | matches!(self, Self::Pass) 27 | } 28 | 29 | pub fn is_skip(&self) -> bool { 30 | matches!(self, Self::Skip) 31 | } 32 | 33 | pub fn value(&self) -> Option<&[V]> { 34 | if let Self::Value(ref v) = self { 35 | Some(v) 36 | } else { 37 | None 38 | } 39 | } 40 | 41 | pub fn into_value(self) -> Option> { 42 | if let Self::Value(v) = self { 43 | Some(v) 44 | } else { 45 | None 46 | } 47 | } 48 | 49 | pub fn unwrap_value(self) -> Vec { 50 | self.into_value().unwrap() 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /fuguex-state-derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fuguex-state-derive" 3 | version = "0.2.0" 4 | authors = ["Sam Thomas "] 5 | edition = "2018" 6 | license = "MIT" 7 | 8 | [lib] 9 | proc_macro = true 10 | 11 | [dependencies] 12 | itertools = "0.10" 13 | proc-macro2 = "1" 14 | syn = { version = "1", features = ["full"] } 15 | quote = "1" 16 | -------------------------------------------------------------------------------- /fuguex-state-derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; 2 | 3 | use proc_macro::TokenStream; 4 | use proc_macro2::TokenStream as TokenStream2; 5 | use quote::quote; 6 | use syn::{parse_macro_input, DeriveInput, Ident, Token}; 7 | use syn::parse::{Parse, ParseStream}; 8 | use syn::spanned::Spanned; 9 | 10 | use itertools::Itertools; 11 | 12 | #[derive(Clone)] 13 | enum Transform { 14 | Nothing, 15 | Single(syn::ExprClosure), 16 | Pair(syn::ExprClosure, syn::ExprClosure), 17 | } 18 | 19 | fn parse_transform(t: ParseStream) -> syn::Result { 20 | let first = syn::ExprClosure::parse(t)?; 21 | let peek = t.lookahead1(); 22 | if peek.peek(Token![,]) { 23 | let _ = t.parse::()?; 24 | Ok(Transform::Pair(first, syn::ExprClosure::parse(t)?)) 25 | } else { 26 | Ok(Transform::Single(first)) 27 | } 28 | } 29 | 30 | #[proc_macro_derive(AsState, attributes(fugue))] 31 | pub fn derive_as_state(input: TokenStream) -> TokenStream { 32 | let input = parse_macro_input!(input as DeriveInput); 33 | let span = input.span(); 34 | let ident = &input.ident; 35 | let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); 36 | let marked_fields = if let syn::Data::Struct(struc) = input.data { 37 | let fields = struc.fields; 38 | let marked = fields.into_iter() 39 | .enumerate() 40 | .filter_map(|(i, field)| { 41 | field.attrs.iter() 42 | .find_map(|attr| if attr.path.is_ident("fugue") { 43 | if attr.tokens.is_empty() { 44 | Some(Ok((i, Transform::Nothing, field.clone()))) 45 | } else { 46 | let attr_map = attr.parse_args_with(parse_transform); 47 | Some(attr_map.map(|map| (i, map, field.clone()))) 48 | } 49 | } else { 50 | None 51 | }) 52 | }) 53 | .collect::>>(); 54 | 55 | match marked { 56 | Ok(marked) => marked, 57 | Err(e) => return e.into_compile_error().into(), 58 | } 59 | } else { 60 | return syn::Error::new(span, "only structs are supported") 61 | .into_compile_error() 62 | .into() 63 | }; 64 | 65 | let marked_powerset = marked_fields.into_iter() 66 | .powerset() 67 | .filter(|v| !v.is_empty()); 68 | 69 | marked_powerset.into_iter().flat_map(|v| { let n = v.len(); v.into_iter().permutations(n).collect::>() }).map(|fields| { 70 | let types = fields.iter().map(|(_, _, f)| f.ty.clone()).collect::>(); 71 | let (type_tokens, refs, muts) = if types.len() == 1 { 72 | let ty = &types[0]; 73 | ( 74 | quote! { #ty }, 75 | quote! { &#ty }, 76 | quote! { &mut #ty }, 77 | ) 78 | } else { 79 | ( 80 | quote! { #(#types),* }, 81 | quote! { (#(&#types),*) }, 82 | quote! { (#(&mut #types),*) }, 83 | ) 84 | }; 85 | 86 | let (accessors, accessors_mut) = fields.iter() 87 | .map(|(i, ff, f)| if let Some(ref ident) = f.ident { 88 | match ff { 89 | Transform::Nothing => (quote! { &self.#ident }, quote! { &mut self.#ident }), 90 | Transform::Single(ff) => (quote! { (#ff)(&self.#ident) }, quote! { (#ff)(&mut self.#ident) }), 91 | Transform::Pair(ffr, ffm) => (quote! { (#ffr)(&self.#ident) }, quote! { (#ffm)(&mut self.#ident) }), 92 | } 93 | } else { 94 | match ff { 95 | Transform::Nothing => (quote! { &self.#i }, quote! { &mut self.#i }), 96 | Transform::Single(ff) => (quote! { (#ff)(&self.#i) }, quote! { (#ff)(&mut self.#i) }), 97 | Transform::Pair(ffr, ffm) => (quote! { (#ffr)(&self.#i) }, quote! { (#ffm)(&mut self.#i) }), 98 | } 99 | }) 100 | .unzip::<_, _, Vec<_>, Vec<_>>(); 101 | 102 | let arity = types.len(); 103 | let impl_fn = if arity > 1 { Ident::new(&format!("state{}_ref", arity), span) } else { Ident::new("state_ref", span) }; 104 | let impl_fn_mut = if arity > 1 { Ident::new(&format!("state{}_mut", arity), span) } else { Ident::new("state_mut", span) }; 105 | let impl_trait = if arity > 1 { Ident::new(&format!("AsState{}", types.len()), span) } else { Ident::new("AsState", span) }; 106 | 107 | quote! { 108 | impl #impl_generics ::fugue_state::#impl_trait<#type_tokens> for #ident #ty_generics #where_clause { 109 | fn #impl_fn(&self) -> #refs { 110 | (#(#accessors),*) 111 | } 112 | 113 | fn #impl_fn_mut(&mut self) -> #muts { 114 | (#(#accessors_mut),*) 115 | } 116 | } 117 | } 118 | }) 119 | .collect::() 120 | .into() 121 | } 122 | -------------------------------------------------------------------------------- /fuguex-state/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fuguex-state" 3 | version = "0.2.19" 4 | authors = ["Sam Thomas "] 5 | edition = "2021" 6 | license = "MIT" 7 | 8 | [dependencies] 9 | fugue = { version = "0.2", registry = "fugue", default-features = false } 10 | fuguex-state-derive = { path = "../fuguex-state-derive", version = "0.2", registry = "fugue" } 11 | iset = "0.2" 12 | log = "0.4" 13 | paste = "1" 14 | thiserror = "1" 15 | ustr = "0.9" 16 | -------------------------------------------------------------------------------- /fuguex-state/src/chunked.rs: -------------------------------------------------------------------------------- 1 | use fugue::ir::{Address, AddressSpace, AddressValue}; 2 | 3 | use iset::IntervalSet; 4 | use std::sync::Arc; 5 | use thiserror::Error; 6 | 7 | use crate::flat::{self, Access, FlatState}; 8 | use crate::traits::{State, StateOps, StateValue}; 9 | 10 | #[derive(Debug, Error)] 11 | pub enum Error { 12 | #[error(transparent)] 13 | Backing(flat::Error), 14 | #[error("not enough free space to allocate {0} bytes")] 15 | NotEnoughFreeSpace(usize), 16 | #[error("attempt to access unmanaged region of `{size}` bytes at {address}")] 17 | AccessUnmanaged { address: Address, size: usize }, 18 | #[error("attempt to free unmanaged region at {0}")] 19 | FreeUnmanaged(Address), 20 | #[error("attempt to reallocate unmanaged region at {0}")] 21 | ReallocateUnmanaged(Address), 22 | #[error("access at {address} of `{size}` bytes spans multiple allocations")] 23 | HeapOverflow { address: Address, size: usize }, 24 | } 25 | 26 | impl Error { 27 | fn backing(base: Address, e: flat::Error) -> Self { 28 | Self::Backing(match e { 29 | flat::Error::OOBRead { address, size } => flat::Error::OOBRead { 30 | address: address + base, 31 | size, 32 | }, 33 | flat::Error::OOBWrite { address, size } => flat::Error::OOBWrite { 34 | address: address + base, 35 | size, 36 | }, 37 | flat::Error::AccessViolation { 38 | address, 39 | access, 40 | size, 41 | } => flat::Error::AccessViolation { 42 | address: address + base, 43 | access, 44 | size, 45 | }, 46 | }) 47 | } 48 | } 49 | 50 | #[derive(Debug, Clone)] 51 | enum ChunkStatus { 52 | Taken { offset: usize, size: usize }, 53 | Free { offset: usize, size: usize }, 54 | } 55 | 56 | impl ChunkStatus { 57 | fn free(offset: usize, size: usize) -> Self { 58 | Self::Free { offset, size } 59 | } 60 | 61 | fn taken(offset: usize, size: usize) -> Self { 62 | Self::Taken { offset, size } 63 | } 64 | 65 | fn is_free(&self) -> bool { 66 | matches!(self, Self::Free { .. }) 67 | } 68 | 69 | fn is_taken(&self) -> bool { 70 | matches!(self, Self::Taken { .. }) 71 | } 72 | 73 | fn offset(&self) -> usize { 74 | match self { 75 | Self::Free { offset, .. } | Self::Taken { offset, .. } => *offset, 76 | } 77 | } 78 | 79 | fn size(&self) -> usize { 80 | match self { 81 | Self::Free { size, .. } | Self::Taken { size, .. } => *size, 82 | } 83 | } 84 | } 85 | 86 | #[derive(Debug, Clone)] 87 | #[repr(transparent)] 88 | struct ChunkList(Vec); 89 | 90 | impl ChunkList { 91 | fn new(size: usize) -> Self { 92 | Self(vec![ChunkStatus::free(0, size)]) 93 | } 94 | 95 | fn allocate(&mut self, size: usize) -> Option { 96 | for i in 0..self.0.len() { 97 | if self.0[i].is_free() { 98 | let free_size = self.0[i].size(); 99 | if free_size == size { 100 | let offset = self.0[i].offset(); 101 | // mut to taken 102 | self.0[i] = ChunkStatus::taken(offset, size); 103 | return Some(offset); 104 | } else if free_size > size { 105 | // split to taken/free 106 | let offset = self.0[i].offset(); 107 | self.0[i] = ChunkStatus::taken(offset, size); 108 | self.0 109 | .insert(i + 1, ChunkStatus::free(offset + size, free_size - size)); 110 | return Some(offset); 111 | } 112 | } 113 | } 114 | None 115 | } 116 | 117 | fn reallocate(&mut self, offset: usize, new_size: usize) -> Option<(usize, usize)> { 118 | for i in 0..self.0.len() { 119 | if self.0[i].is_taken() && self.0[i].offset() == offset { 120 | let mut size = self.0[i].size(); 121 | let old_size = size; 122 | 123 | if new_size == old_size { 124 | // do nothing 125 | return Some((offset, old_size)); 126 | } else if new_size < old_size { 127 | self.0[i] = ChunkStatus::taken(offset, new_size); 128 | let diff = old_size - new_size; 129 | 130 | // maybe merge up 131 | if i < self.0.len() - 1 && self.0[i + 1].is_free() { 132 | let upd_offset = self.0[i + 1].offset() - diff; 133 | let upd_size = self.0[i + 1].size() + diff; 134 | 135 | self.0[i + 1] = ChunkStatus::free(upd_offset, upd_size); 136 | } else { 137 | self.0 138 | .insert(i + 1, ChunkStatus::free(offset + new_size, diff)); 139 | } 140 | 141 | return Some((offset, old_size)); 142 | } 143 | 144 | // test if we can merge frees 145 | let mut spos = i; 146 | let mut epos = i; 147 | 148 | let mut offset = offset; 149 | 150 | if i < self.0.len() - 1 && self.0[i + 1].is_free() { 151 | size += self.0[i + 1].size(); 152 | epos = i + 1; 153 | } 154 | 155 | if size > new_size { 156 | self.0[spos] = ChunkStatus::taken(offset, new_size); 157 | self.0[epos] = ChunkStatus::free(offset + new_size, size - new_size); 158 | return Some((offset, old_size)); 159 | } else if size == new_size { 160 | self.0[spos] = ChunkStatus::taken(offset, new_size); 161 | self.0.remove(epos); 162 | return Some((offset, old_size)); 163 | } 164 | 165 | if i > 0 && self.0[i - 1].is_free() { 166 | offset = self.0[i - 1].offset(); 167 | size += self.0[i - 1].size(); 168 | spos = i - 1; 169 | } 170 | 171 | if size > new_size { 172 | self.0[spos] = ChunkStatus::taken(offset, new_size); 173 | self.0[spos + 1] = ChunkStatus::free(offset + new_size, size - new_size); 174 | self.0.remove(i + 1); 175 | return Some((offset, old_size)); 176 | } else if size == new_size { 177 | self.0[spos] = ChunkStatus::taken(offset, new_size); 178 | self.0.remove(i); 179 | self.0.remove(i + 1); 180 | return Some((offset, old_size)); 181 | } 182 | 183 | // all else fails. 184 | if let Some(new_offset) = self.allocate(new_size) { 185 | self.deallocate(offset); 186 | return Some((new_offset, old_size)); 187 | } else { 188 | return None; 189 | } 190 | } 191 | } 192 | None 193 | } 194 | 195 | fn deallocate(&mut self, offset: usize) -> Option { 196 | for i in 0..self.0.len() { 197 | if self.0[i].is_taken() && self.0[i].offset() == offset { 198 | // see if we should merge frees 199 | let mut spos = i; 200 | let mut offset = offset; 201 | let mut size = self.0[i].size(); 202 | let old_size = size; 203 | 204 | if i < self.0.len() - 1 && self.0[i + 1].is_free() { 205 | size += self.0[i + 1].size(); 206 | self.0.remove(i + 1); 207 | } 208 | 209 | if i > 0 && self.0[i - 1].is_free() { 210 | offset = self.0[i - 1].offset(); 211 | size += self.0[i - 1].size(); 212 | self.0.remove(i); 213 | spos = i - 1; 214 | } 215 | 216 | self.0[spos] = ChunkStatus::free(offset, size); 217 | return Some(old_size); 218 | } 219 | } 220 | None 221 | } 222 | } 223 | 224 | #[derive(Debug, Clone)] 225 | pub struct ChunkState { 226 | base_address: Address, 227 | chunks: ChunkList, 228 | regions: IntervalSet

, 229 | backing: FlatState, 230 | space: Arc, 231 | } 232 | 233 | impl AsRef for ChunkState { 234 | #[inline(always)] 235 | fn as_ref(&self) -> &Self { 236 | self 237 | } 238 | } 239 | 240 | impl AsMut for ChunkState { 241 | #[inline(always)] 242 | fn as_mut(&mut self) -> &mut Self { 243 | self 244 | } 245 | } 246 | 247 | impl AsRef> for ChunkState { 248 | #[inline(always)] 249 | fn as_ref(&self) -> &FlatState { 250 | &self.backing 251 | } 252 | } 253 | 254 | impl AsMut> for ChunkState { 255 | #[inline(always)] 256 | fn as_mut(&mut self) -> &mut FlatState { 257 | &mut self.backing 258 | } 259 | } 260 | 261 | impl ChunkState { 262 | pub fn new(space: Arc, base_address: A, size: usize) -> Self 263 | where 264 | A: Into
, 265 | { 266 | Self { 267 | base_address: base_address.into(), 268 | chunks: ChunkList::new(size), 269 | regions: IntervalSet::new(), 270 | backing: FlatState::read_only(space.clone(), size), 271 | space, 272 | } 273 | } 274 | 275 | pub fn base_address(&self) -> Address { 276 | self.base_address 277 | } 278 | 279 | pub fn inner(&self) -> &FlatState { 280 | &self.backing 281 | } 282 | 283 | pub fn inner_mut(&mut self) -> &mut FlatState { 284 | &mut self.backing 285 | } 286 | 287 | pub fn allocate(&mut self, size: usize) -> Result { 288 | self.allocate_with(size, |_, _| ()) 289 | } 290 | 291 | pub fn allocate_all(&mut self) -> Result<(), Error> { 292 | self.allocate_with(self.len() - 1, |_, _| ()).map(|_| ()) 293 | } 294 | 295 | #[inline] 296 | pub fn allocate_with(&mut self, size: usize, f: F) -> Result 297 | where 298 | F: FnOnce(Address, &mut [T]), 299 | { 300 | // we allocate +1 on size to mark the last part as a red-zone 301 | let offset = self 302 | .chunks 303 | .allocate(size + 1) 304 | .ok_or_else(|| Error::NotEnoughFreeSpace(size))?; 305 | let address = self.base_address + offset; 306 | 307 | // set R/W permissions 308 | self.backing.permissions_mut().set_region( 309 | &Address::from(offset as u64), 310 | size, 311 | Access::ReadWrite, 312 | ); 313 | self.backing 314 | .permissions_mut() 315 | .clear_byte(&(Address::from(offset as u64) + size), Access::Write); 316 | 317 | // update region mappings 318 | self.regions.insert(address..address + size); 319 | 320 | // get a mutable view over the backing 321 | let view = self 322 | .backing 323 | .view_values_mut(Address::from(offset as u64), size) 324 | .map_err(Error::Backing)?; 325 | 326 | f(address, view); 327 | 328 | Ok(address) 329 | } 330 | 331 | pub fn reallocate(&mut self, address: A, size: usize) -> Result 332 | where 333 | A: Into
, 334 | { 335 | let address = address.into(); 336 | let interval = self 337 | .regions 338 | .overlap(address) 339 | .next() 340 | .ok_or_else(|| Error::ReallocateUnmanaged(address))?; 341 | 342 | if interval.start != address { 343 | return Err(Error::ReallocateUnmanaged(address)); 344 | } 345 | 346 | // check permissions first 347 | let old_offset = address - self.base_address; 348 | let old_size = interval.end - interval.start; 349 | 350 | if !self 351 | .backing 352 | .permissions() 353 | .all_readable(&old_offset, old_size.into()) 354 | { 355 | return Err(Error::Backing(flat::Error::AccessViolation { 356 | address: AddressValue::new(self.space.clone(), address.into()), 357 | access: Access::Read, 358 | size, 359 | })); 360 | } 361 | 362 | // size + 1 to use the last byte as a red-zone 363 | let (offset, _old_size) = self 364 | .chunks 365 | .reallocate(old_offset.into(), size + 1) 366 | .ok_or_else(|| Error::NotEnoughFreeSpace(size))?; 367 | 368 | let new_address = self.base_address + offset; 369 | 370 | // set R/W permissions 371 | self.backing.permissions_mut().set_region( 372 | &Address::from(offset as u64), 373 | size, 374 | Access::ReadWrite, 375 | ); 376 | self.backing 377 | .permissions_mut() 378 | .clear_byte(&(Address::from(offset as u64) + size), Access::Write); 379 | 380 | // copy if moved 381 | let offset = Address::from(offset as u64); 382 | if old_offset != offset { 383 | self.backing 384 | .copy_values(old_offset, offset, old_size.into()) 385 | .map_err(Error::Backing)?; 386 | 387 | self.backing.permissions_mut().clear_region( 388 | &old_offset, 389 | old_size.into(), 390 | Access::Write, 391 | ); 392 | } 393 | 394 | // update region mappings 395 | self.regions.remove(interval); 396 | self.regions 397 | .insert(new_address..new_address + size); 398 | 399 | Ok(new_address) 400 | } 401 | 402 | pub fn deallocate(&mut self, address: A) -> Result<(), Error> 403 | where 404 | A: Into
, 405 | { 406 | let address = address.into(); 407 | let interval = self 408 | .regions 409 | .overlap(address) 410 | .next() 411 | .ok_or_else(|| Error::FreeUnmanaged(address))?; 412 | 413 | if interval.start != address { 414 | return Err(Error::FreeUnmanaged(address)); 415 | } 416 | 417 | let offset = address - self.base_address; 418 | self.chunks 419 | .deallocate(offset.into()) 420 | .ok_or_else(|| Error::FreeUnmanaged(address))?; 421 | 422 | let size = usize::from(interval.end - interval.start); 423 | 424 | self.backing 425 | .permissions_mut() 426 | .clear_region(&offset, size, flat::Access::Write); 427 | 428 | self.regions.remove(interval); 429 | 430 | Ok(()) 431 | } 432 | 433 | pub(crate) fn translate_checked(&self, address: A, size: usize) -> Result 434 | where 435 | A: Into
, 436 | { 437 | let address = address.into(); 438 | let mut regions = self.regions.iter(address..address + size); 439 | 440 | // we just need to know that it exists 441 | let _region = regions.next().ok_or_else(|| { 442 | Error::AccessUnmanaged { address, size } 443 | })?; 444 | 445 | // ..and that another does not exist 446 | if regions.next().is_some() { 447 | // violation 448 | return Err(Error::HeapOverflow { address, size }); 449 | } 450 | 451 | Ok(usize::from(address - self.base_address)) 452 | } 453 | } 454 | 455 | impl State for ChunkState { 456 | type Error = Error; 457 | 458 | fn fork(&self) -> Self { 459 | Self { 460 | base_address: self.base_address.clone(), 461 | chunks: self.chunks.clone(), 462 | regions: self.regions.clone(), 463 | backing: self.backing.fork(), 464 | space: self.space.clone(), 465 | } 466 | } 467 | 468 | fn restore(&mut self, other: &Self) { 469 | self.base_address = other.base_address; 470 | self.chunks = other.chunks.clone(); 471 | self.regions = other.regions.clone(); 472 | self.backing.restore(&other.backing); 473 | } 474 | } 475 | 476 | impl StateOps for ChunkState { 477 | type Value = V; 478 | 479 | fn len(&self) -> usize { 480 | self.backing.len() 481 | } 482 | 483 | fn copy_values(&mut self, from: F, to: T, size: usize) -> Result<(), Error> 484 | where 485 | F: Into
, 486 | T: Into
, 487 | { 488 | let from = self.translate_checked(from, size)?; 489 | let to = self.translate_checked(to, size)?; 490 | 491 | self.backing 492 | .copy_values(from as u64, to as u64, size) 493 | .map_err(|e| Error::backing(self.base_address, e)) 494 | } 495 | 496 | fn get_values(&self, address: A, values: &mut [Self::Value]) -> Result<(), Error> 497 | where 498 | A: Into
, 499 | { 500 | let size = values.len(); 501 | let address = self.translate_checked(address, size)?; 502 | 503 | self.backing 504 | .get_values(address as u64, values) 505 | .map_err(|e| Error::backing(self.base_address, e)) 506 | } 507 | 508 | fn view_values(&self, address: A, n: usize) -> Result<&[Self::Value], Error> 509 | where 510 | A: Into
, 511 | { 512 | let address = self.translate_checked(address, n)?; 513 | 514 | self.backing 515 | .view_values(address as u64, n) 516 | .map_err(|e| Error::backing(self.base_address, e)) 517 | } 518 | 519 | fn view_values_mut(&mut self, address: A, n: usize) -> Result<&mut [Self::Value], Error> 520 | where 521 | A: Into
, 522 | { 523 | let address = self.translate_checked(address, n)?; 524 | let base_address = self.base_address; 525 | 526 | self.backing 527 | .view_values_mut(address as u64, n) 528 | .map_err(|e| Error::backing(base_address, e)) 529 | } 530 | 531 | fn set_values(&mut self, address: A, values: &[Self::Value]) -> Result<(), Error> 532 | where 533 | A: Into
, 534 | { 535 | let size = values.len(); 536 | let address = self.translate_checked(address, size)?; 537 | 538 | self.backing 539 | .set_values(address as u64, values) 540 | .map_err(|e| Error::backing(self.base_address, e)) 541 | } 542 | } 543 | -------------------------------------------------------------------------------- /fuguex-state/src/flat.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | use std::mem::size_of; 3 | use std::sync::Arc; 4 | 5 | use fugue::ir::{Address, AddressValue, AddressSpace}; 6 | 7 | use crate::traits::{State, StateOps, StateValue}; 8 | 9 | use thiserror::Error; 10 | 11 | #[derive(Debug, Error)] 12 | pub enum Error { 13 | #[error("{access} access violation at {address} of {size} bytes in space `{}`", address.space().index())] 14 | AccessViolation { address: AddressValue, size: usize, access: Access }, 15 | #[error("out-of-bounds read of `{size}` bytes at {address}")] 16 | OOBRead { address: Address, size: usize }, 17 | #[error("out-of-bounds write of `{size}` bytes at {address}")] 18 | OOBWrite { address: Address, size: usize }, 19 | } 20 | 21 | #[derive(Debug, Clone, Eq, PartialEq, Hash)] 22 | pub struct FlatState { 23 | backing: Vec, 24 | dirty: DirtyBacking, 25 | permissions: Permissions, 26 | space: Arc, 27 | } 28 | 29 | impl AsRef for FlatState { 30 | #[inline(always)] 31 | fn as_ref(&self) -> &Self { 32 | self 33 | } 34 | } 35 | 36 | impl AsMut for FlatState { 37 | #[inline(always)] 38 | fn as_mut(&mut self) -> &mut Self { 39 | self 40 | } 41 | } 42 | 43 | impl FlatState { 44 | pub fn new(space: Arc, size: usize) -> Self { 45 | Self { 46 | backing: vec![T::default(); size], 47 | dirty: DirtyBacking::new(size), 48 | permissions: Permissions::new(space.clone(), size), 49 | space, 50 | } 51 | } 52 | 53 | pub fn read_only(space: Arc, size: usize) -> Self { 54 | Self { 55 | backing: vec![T::default(); size], 56 | dirty: DirtyBacking::new(size), 57 | permissions: Permissions::new_with(space.clone(), size, PERM_READ_MASK), 58 | space, 59 | } 60 | } 61 | 62 | pub fn from_vec(space: Arc, values: Vec) -> Self { 63 | let size = values.len(); 64 | Self { 65 | backing: values, 66 | dirty: DirtyBacking::new(size), 67 | permissions: Permissions::new(space.clone(), size), 68 | space, 69 | } 70 | } 71 | 72 | pub fn permissions(&self) -> &Permissions { 73 | &self.permissions 74 | } 75 | 76 | pub fn permissions_mut(&mut self) -> &mut Permissions { 77 | &mut self.permissions 78 | } 79 | 80 | pub fn address_space(&self) -> Arc { 81 | self.space.clone() 82 | } 83 | 84 | pub fn address_space_ref(&self) -> &AddressSpace { 85 | self.space.as_ref() 86 | } 87 | } 88 | 89 | impl State for FlatState { 90 | type Error = Error; 91 | 92 | fn fork(&self) -> Self { 93 | Self { 94 | backing: self.backing.clone(), 95 | dirty: self.dirty.fork(), 96 | permissions: self.permissions.clone(), 97 | space: self.space.clone(), 98 | } 99 | } 100 | 101 | fn restore(&mut self, other: &Self) { 102 | for block in self.dirty.indices.drain(..) { 103 | let start = usize::from(block.start_address()); 104 | let end = usize::from(block.end_address()); 105 | 106 | let real_end = self.backing.len().min(end); 107 | 108 | self.dirty.bitsmap[block.index()] = 0; 109 | self.backing[start..real_end].clone_from_slice(&other.backing[start..real_end]); 110 | } 111 | self.permissions.restore(&other.permissions); 112 | self.dirty.clone_from(&other.dirty); 113 | } 114 | } 115 | 116 | impl StateOps for FlatState { 117 | type Value = V; 118 | 119 | fn len(&self) -> usize { 120 | self.backing.len() 121 | } 122 | 123 | fn copy_values(&mut self, from: F, to: T, size: usize) -> Result<(), Error> 124 | where F: Into
, 125 | T: Into
{ 126 | let from = from.into(); 127 | let to = to.into(); 128 | 129 | let soff = usize::from(from); 130 | let doff = usize::from(to); 131 | 132 | if soff > self.len() || soff.checked_add(size).is_none() || soff + size > self.len() { 133 | return Err(Error::OOBRead { 134 | address: from.clone(), 135 | size, //(soff + size) - self.len(), 136 | }); 137 | } 138 | 139 | if !self.permissions.all_readable(&from, size) { 140 | return Err(Error::AccessViolation { 141 | address: AddressValue::new(self.space.clone(), from.into()), 142 | size, 143 | access: Access::Read, 144 | }) 145 | } 146 | 147 | if doff > self.len() || doff.checked_add(size).is_none() || doff + size > self.len() { 148 | return Err(Error::OOBWrite { 149 | address: to.clone(), 150 | size, // (doff + size) - self.len(), 151 | }); 152 | } 153 | 154 | if !self.permissions.all_writable(&to, size) { 155 | return Err(Error::AccessViolation { 156 | address: AddressValue::new(self.space.clone(), to.into()), 157 | size, 158 | access: Access::Write, 159 | }) 160 | } 161 | 162 | if doff == soff { 163 | return Ok(()) 164 | } 165 | 166 | if doff >= soff + size { 167 | let (shalf, dhalf) = self.backing.split_at_mut(doff); 168 | dhalf.clone_from_slice(&shalf[soff..(soff + size)]); 169 | } else if doff + size <= soff { 170 | let (dhalf, shalf) = self.backing.split_at_mut(soff); 171 | dhalf[doff..(doff+size)].clone_from_slice(&shalf); 172 | } else { // overlap; TODO: see if we can avoid superfluous clones 173 | if doff < soff { 174 | for i in 0..size { 175 | unsafe { 176 | let dptr = self.backing.as_mut_ptr().add(doff + i); 177 | let sptr = self.backing.as_ptr().add(soff + i); 178 | (&mut *dptr).clone_from(&*sptr); 179 | } 180 | } 181 | } else { 182 | for i in (0..size).rev() { 183 | unsafe { 184 | let dptr = self.backing.as_mut_ptr().add(doff + i); 185 | let sptr = self.backing.as_ptr().add(soff + i); 186 | (&mut *dptr).clone_from(&*sptr); 187 | } 188 | } 189 | } 190 | } 191 | 192 | self.dirty.dirty_region(&to, size); 193 | 194 | Ok(()) 195 | } 196 | 197 | fn get_values(&self, address: A, values: &mut [Self::Value]) -> Result<(), Error> 198 | where A: Into
{ 199 | let address = address.into(); 200 | let size = values.len(); 201 | let start = usize::from(address); 202 | let end = start.checked_add(size); 203 | 204 | if start > self.len() || end.is_none() || end.unwrap() > self.len() { 205 | return Err(Error::OOBRead { 206 | address: address.clone(), 207 | size: values.len(), 208 | }); 209 | } 210 | 211 | if !self.permissions.all_readable(&address, size) { 212 | return Err(Error::AccessViolation { 213 | address: AddressValue::new(self.space.clone(), address.into()), 214 | size, 215 | access: Access::Read, 216 | }) 217 | } 218 | 219 | let end = end.unwrap(); 220 | 221 | values[..].clone_from_slice(&self.backing[start..end]); 222 | 223 | Ok(()) 224 | } 225 | 226 | fn view_values(&self, address: A, size: usize) -> Result<&[Self::Value], Error> 227 | where A: Into
{ 228 | let address = address.into(); 229 | let start = usize::from(address); 230 | let end = start.checked_add(size); 231 | 232 | if start > self.len() || end.is_none() || end.unwrap() > self.len() { 233 | return Err(Error::OOBRead { 234 | address: address.clone(), 235 | size, 236 | }); 237 | } 238 | 239 | if !self.permissions.all_readable(&address, size) { 240 | return Err(Error::AccessViolation { 241 | address: AddressValue::new(self.space.clone(), address.into()), 242 | size, 243 | access: Access::Read, 244 | }) 245 | } 246 | 247 | let end = end.unwrap(); 248 | 249 | Ok(&self.backing[start..end]) 250 | } 251 | 252 | fn view_values_mut(&mut self, address: A, size: usize) -> Result<&mut [Self::Value], Error> 253 | where A: Into
{ 254 | let address = address.into(); 255 | let start = usize::from(address); 256 | let end = start.checked_add(size); 257 | 258 | if start > self.len() || end.is_none() || end.unwrap() > self.len() { 259 | return Err(Error::OOBRead { 260 | address: address.clone(), 261 | size, 262 | }); 263 | } 264 | 265 | if !self.permissions.all_readable_and_writable(&address, size) { 266 | return Err(Error::AccessViolation { 267 | address: AddressValue::new(self.space.clone(), address.into()), 268 | size, 269 | access: Access::ReadWrite, 270 | }) 271 | } 272 | 273 | let end = end.unwrap(); 274 | 275 | self.dirty.dirty_region(&address, size); 276 | 277 | Ok(&mut self.backing[start..end]) 278 | } 279 | 280 | fn set_values(&mut self, address: A, values: &[Self::Value]) -> Result<(), Error> 281 | where A: Into
{ 282 | let address = address.into(); 283 | let size = values.len(); 284 | let start = usize::from(address); 285 | let end = start.checked_add(size); 286 | 287 | if start > self.len() || end.is_none() || end.unwrap() > self.len() { 288 | return Err(Error::OOBWrite { 289 | address: address.clone(), 290 | size, 291 | }); 292 | } 293 | 294 | if !self.permissions.all_writable(&address, size) { 295 | return Err(Error::AccessViolation { 296 | address: AddressValue::new(self.space.clone(), address.into()), 297 | size, 298 | access: Access::Write, 299 | }) 300 | } 301 | 302 | let end = end.unwrap(); 303 | 304 | self.backing[start..end].clone_from_slice(values); 305 | self.dirty.dirty_region(&address, size); 306 | 307 | Ok(()) 308 | } 309 | } 310 | 311 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] 312 | #[repr(transparent)] 313 | pub struct Block(u64); 314 | 315 | pub const BLOCK_SIZE: u64 = 64; 316 | 317 | impl From<&'_ Address> for Block { 318 | fn from(t: &Address) -> Block { 319 | Self(u64::from(*t) / BLOCK_SIZE) 320 | } 321 | } 322 | 323 | impl From
for Block { 324 | fn from(t: Address) -> Block { 325 | Self(u64::from(t) / BLOCK_SIZE) 326 | } 327 | } 328 | 329 | impl From for Block { 330 | fn from(t: u64) -> Block { 331 | Self(t) 332 | } 333 | } 334 | 335 | impl Block { 336 | #[inline] 337 | fn bit(&self) -> usize { 338 | self.0 as usize % size_of::() 339 | } 340 | 341 | #[inline] 342 | fn index(&self) -> usize { 343 | self.0 as usize / size_of::() 344 | } 345 | 346 | #[inline] 347 | fn start_address(&self) -> Address { 348 | (self.0 * BLOCK_SIZE).into() 349 | } 350 | 351 | #[inline] 352 | fn end_address(&self) -> Address { 353 | ((self.0 + 1) * BLOCK_SIZE).into() 354 | } 355 | } 356 | 357 | #[derive(Debug, Clone, Eq, PartialEq, Hash)] 358 | pub struct DirtyBacking { 359 | indices: Vec, 360 | bitsmap: Vec, 361 | } 362 | 363 | impl DirtyBacking { 364 | pub fn new(size: usize) -> Self { 365 | let backing_size = 1 + (size as u64 / BLOCK_SIZE) as usize; 366 | Self { 367 | indices: Vec::with_capacity(backing_size), 368 | bitsmap: vec![0 as u64; 1 + backing_size / size_of::()], 369 | } 370 | } 371 | 372 | #[inline] 373 | pub fn fork(&self) -> Self { 374 | /* 375 | Self { 376 | indices: Vec::with_capacity(self.indices.capacity()), 377 | bitsmap: vec![0 as u64; self.bitsmap.len()], 378 | } 379 | */ 380 | self.clone() 381 | } 382 | 383 | #[inline] 384 | pub fn dirty>(&mut self, block: B) { 385 | let block = block.into(); 386 | let index = block.index(); 387 | let check = 1 << block.bit(); 388 | 389 | if self.bitsmap[index] & check == 0 { 390 | self.bitsmap[index] |= check; 391 | self.indices.push(block); 392 | } 393 | } 394 | 395 | #[inline] 396 | pub fn dirty_region(&mut self, start: &Address, size: usize) { 397 | let sblock = Block::from(start).0; 398 | let eblock = Block::from(*start + size as u64).0; 399 | 400 | for block in sblock..=eblock { 401 | self.dirty(block); 402 | } 403 | } 404 | } 405 | 406 | #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] 407 | pub enum Access { 408 | Read, 409 | Write, 410 | ReadWrite, 411 | } 412 | 413 | impl fmt::Display for Access { 414 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 415 | match self { 416 | Access::Read => write!(f, "read"), 417 | Access::Write => write!(f, "write"), 418 | Access::ReadWrite => write!(f, "read/write") 419 | } 420 | } 421 | } 422 | 423 | impl Access { 424 | #[inline] 425 | pub fn is_read(&self) -> bool { 426 | matches!(self, Access::Read) 427 | } 428 | 429 | #[inline] 430 | pub fn is_write(&self) -> bool { 431 | matches!(self, Access::Write) 432 | } 433 | 434 | #[inline] 435 | pub fn is_read_write(&self) -> bool { 436 | matches!(self, Access::ReadWrite) 437 | } 438 | } 439 | 440 | #[derive(Debug, Clone, Eq, PartialEq, Hash)] 441 | pub struct Permissions { 442 | bitsmap: Vec, 443 | space: Arc, 444 | } 445 | 446 | const PERM_READ_OFF: usize = 1; 447 | const PERM_WRITE_OFF: usize = 0; 448 | const PERM_READ_WRITE_OFF: usize = 0; 449 | 450 | const PERM_READ_MASK: u64 = 0xAAAAAAAAAAAAAAAA; 451 | const PERM_WRITE_MASK: u64 = 0x5555555555555555; 452 | 453 | const PERM_SCALE: usize = (size_of::() << 3) >> 1; 454 | const PERM_SELECT: usize = 1; 455 | 456 | impl Permissions { 457 | pub fn new(space: Arc, size: usize) -> Self { 458 | Self::new_with(space, size, PERM_READ_MASK | PERM_WRITE_MASK) 459 | } 460 | 461 | #[inline] 462 | pub fn new_with(space: Arc, size: usize, mask: u64) -> Self { 463 | Self { 464 | // NOTE: we represent each permission by two bits and set 465 | // each byte to readable by default 466 | bitsmap: vec![mask; 1 + size / PERM_SCALE], 467 | space, 468 | } 469 | } 470 | 471 | pub fn restore(&mut self, other: &Permissions) { 472 | for (t, s) in self.bitsmap.iter_mut().zip(other.bitsmap.iter()) { 473 | *t = *s; 474 | } 475 | } 476 | 477 | #[inline] 478 | pub fn is_marked(&self, address: &Address, access: Access) -> bool { 479 | let address = u64::from(address); 480 | let index = (address / PERM_SCALE as u64) as usize; 481 | let bit = ((address % PERM_SCALE as u64) as usize) << PERM_SELECT; 482 | let check = if access.is_read_write() { 483 | 0b11 << (bit + PERM_READ_WRITE_OFF) 484 | } else { 485 | 1 << if access.is_read() { 486 | bit + PERM_READ_OFF 487 | } else { 488 | bit + PERM_WRITE_OFF 489 | } 490 | }; 491 | 492 | self.bitsmap[index] & check == check 493 | } 494 | 495 | #[inline] 496 | pub fn is_readable(&self, address: &Address) -> bool { 497 | self.is_marked(address, Access::Read) 498 | } 499 | 500 | #[inline] 501 | pub fn is_writable(&self, address: &Address) -> bool { 502 | self.is_marked(address, Access::Write) 503 | } 504 | 505 | #[inline] 506 | pub fn is_readable_and_writable(&self, address: &Address) -> bool { 507 | self.is_marked(address, Access::ReadWrite) 508 | } 509 | 510 | #[inline] 511 | pub fn all_marked(&self, address: &Address, size: usize, access: Access) -> bool { 512 | let start = u64::from(address); 513 | for addr in start..(start + size as u64) { 514 | if !self.is_marked(&Address::new(self.space.as_ref(), addr), access) { 515 | return false 516 | } 517 | } 518 | true 519 | } 520 | 521 | #[inline] 522 | pub fn all_readable(&self, address: &Address, size: usize) -> bool { 523 | self.all_marked(address, size, Access::Read) 524 | } 525 | 526 | #[inline] 527 | pub fn all_writable(&self, address: &Address, size: usize) -> bool { 528 | self.all_marked(address, size, Access::Write) 529 | } 530 | 531 | #[inline] 532 | pub fn all_readable_and_writable(&self, address: &Address, size: usize) -> bool { 533 | self.all_marked(address, size, Access::ReadWrite) 534 | } 535 | 536 | #[inline] 537 | pub fn clear_byte(&mut self, address: &Address, access: Access) { 538 | let address = u64::from(address); 539 | let index = (address / PERM_SCALE as u64) as usize; 540 | let bit = ((address % PERM_SCALE as u64) as usize) << PERM_SELECT; 541 | let check = if access.is_read_write() { 542 | 0b11 << (bit + PERM_READ_WRITE_OFF) 543 | } else { 544 | 1 << if access.is_read() { 545 | bit + PERM_READ_OFF 546 | } else { 547 | bit + PERM_WRITE_OFF 548 | } 549 | }; 550 | self.bitsmap[index] &= !check; 551 | } 552 | 553 | #[inline] 554 | pub fn set_byte(&mut self, address: &Address, access: Access) { 555 | let address = u64::from(address); 556 | let index = (address / PERM_SCALE as u64) as usize; 557 | let bit = ((address % PERM_SCALE as u64) as usize) << PERM_SELECT; 558 | let check = if access.is_read_write() { 559 | 0b11 << (bit + PERM_READ_WRITE_OFF) 560 | } else { 561 | 1 << if access.is_read() { 562 | bit + PERM_READ_OFF 563 | } else { 564 | bit + PERM_WRITE_OFF 565 | } 566 | }; 567 | 568 | self.bitsmap[index] |= check; 569 | } 570 | 571 | #[inline] 572 | pub fn clear_region(&mut self, address: &Address, size: usize, access: Access) { 573 | let start = u64::from(address); 574 | for addr in start..(start + size as u64) { 575 | self.clear_byte(&Address::new(self.space.as_ref(), addr), access); 576 | } 577 | } 578 | 579 | #[inline] 580 | pub fn set_region(&mut self, address: &Address, size: usize, access: Access) { 581 | let start = u64::from(address); 582 | for addr in start..(start + size as u64) { 583 | self.set_byte(&Address::new(self.space.as_ref(), addr), access); 584 | } 585 | } 586 | } 587 | -------------------------------------------------------------------------------- /fuguex-state/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod chunked; 2 | pub mod flat; 3 | pub mod paged; 4 | pub mod pcode; 5 | pub mod register; 6 | pub mod unique; 7 | 8 | pub mod traits; 9 | pub use self::traits::*; 10 | -------------------------------------------------------------------------------- /fuguex-state/src/paged.rs: -------------------------------------------------------------------------------- 1 | use fugue::ir::{Address, AddressSpace}; 2 | 3 | use std::mem::take; 4 | use std::ops::Range; 5 | use std::sync::Arc; 6 | 7 | use iset::IntervalMap; 8 | use thiserror::Error; 9 | use ustr::Ustr; 10 | 11 | use crate::chunked::{self, ChunkState}; 12 | use crate::flat::{self, FlatState}; 13 | use crate::traits::{State, StateOps, StateValue}; 14 | 15 | #[derive(Debug, Error)] 16 | pub enum Error { 17 | #[error("unmapped virtual address at {address}")] 18 | UnmappedAddress { address: Address, size: usize }, 19 | #[error("overlapped access from {address} byte access at {size}")] 20 | OverlappedAccess { address: Address, size: usize }, 21 | #[error("overlapped mapping of {size} bytes from {address}")] 22 | OverlappedMapping { address: Address, size: usize }, 23 | #[error(transparent)] 24 | Backing(flat::Error), 25 | #[error(transparent)] 26 | Chunked(chunked::Error), 27 | } 28 | 29 | impl Error { 30 | pub fn access(&self) -> (Address, usize) { 31 | match self { 32 | Self::UnmappedAddress { address, size } 33 | | Self::OverlappedAccess { address, size } 34 | | Self::OverlappedMapping { address, size } => (*address, *size), 35 | Self::Backing( 36 | flat::Error::OOBRead { address, size } | flat::Error::OOBWrite { address, size }, 37 | ) => (*address, *size), 38 | Self::Backing(flat::Error::AccessViolation { address, size, .. }) => { 39 | (address.into(), *size) 40 | } 41 | Self::Chunked( 42 | chunked::Error::Backing( 43 | flat::Error::OOBRead { address, size } 44 | | flat::Error::OOBWrite { address, size }, 45 | ) 46 | | chunked::Error::AccessUnmanaged { address, size } 47 | | chunked::Error::HeapOverflow { address, size }, 48 | ) => (*address, *size), 49 | Self::Chunked(chunked::Error::Backing(flat::Error::AccessViolation { 50 | address, 51 | size, 52 | .. 53 | })) => (address.into(), *size), 54 | _ => panic!("error is not an access violation"), 55 | } 56 | } 57 | 58 | fn backing(base: Address, e: flat::Error) -> Self { 59 | Self::Backing(match e { 60 | flat::Error::OOBRead { address, size } => flat::Error::OOBRead { 61 | address: address + base, 62 | size, 63 | }, 64 | flat::Error::OOBWrite { address, size } => flat::Error::OOBWrite { 65 | address: address + base, 66 | size, 67 | }, 68 | flat::Error::AccessViolation { 69 | address, 70 | access, 71 | size, 72 | } => flat::Error::AccessViolation { 73 | address: address + base, 74 | access, 75 | size, 76 | }, 77 | }) 78 | } 79 | } 80 | 81 | #[derive(Debug, Clone)] 82 | pub enum Segment { 83 | Static { name: Ustr, offset: usize }, 84 | Mapping { name: Ustr, backing: ChunkState }, 85 | StaticMapping { name: Ustr, backing: FlatState }, 86 | } 87 | 88 | #[derive(Debug, Clone)] 89 | pub enum MappingRef<'a, T: StateValue> { 90 | Dynamic(&'a ChunkState), 91 | Static(&'a FlatState), 92 | } 93 | 94 | #[derive(Debug, Clone)] 95 | pub enum MappingMut<'a, T: StateValue> { 96 | Dynamic(&'a ChunkState), 97 | Static(&'a FlatState), 98 | } 99 | 100 | impl Segment { 101 | pub fn new>(name: S, offset: usize) -> Self { 102 | Self::Static { 103 | name: Ustr::from(name.as_ref()), 104 | offset, 105 | } 106 | } 107 | 108 | pub fn mapping>(name: S, mapping: ChunkState) -> Self { 109 | Self::Mapping { 110 | name: Ustr::from(name.as_ref()), 111 | backing: mapping, 112 | } 113 | } 114 | 115 | pub fn static_mapping>(name: S, mapping: FlatState) -> Self { 116 | Self::StaticMapping { 117 | name: Ustr::from(name.as_ref()), 118 | backing: mapping, 119 | } 120 | } 121 | 122 | pub fn is_static(&self) -> bool { 123 | matches!(self, Self::Static { .. }) 124 | } 125 | 126 | pub fn is_mapping(&self) -> bool { 127 | matches!(self, Self::Mapping { .. }) 128 | } 129 | 130 | pub fn as_mapping(&self) -> Option> { 131 | match self { 132 | Self::Mapping { ref backing, .. } => Some(MappingRef::Dynamic(backing)), 133 | Self::StaticMapping { ref backing, .. } => Some(MappingRef::Static(backing)), 134 | _ => None, 135 | } 136 | } 137 | 138 | pub fn as_mapping_mut(&mut self) -> Option> { 139 | match self { 140 | Self::Mapping { 141 | ref mut backing, .. 142 | } => Some(MappingMut::Dynamic(backing)), 143 | Self::StaticMapping { 144 | ref mut backing, .. 145 | } => Some(MappingMut::Static(backing)), 146 | _ => None, 147 | } 148 | } 149 | 150 | pub fn name(&self) -> &str { 151 | match self { 152 | Self::Static { name, .. } 153 | | Self::Mapping { name, .. } 154 | | Self::StaticMapping { name, .. } => name, 155 | } 156 | } 157 | 158 | pub fn fork(&self) -> Self { 159 | match self { 160 | Self::Static { .. } => self.clone(), 161 | Self::Mapping { name, backing } => Self::Mapping { 162 | name: name.clone(), 163 | backing: backing.fork(), 164 | }, 165 | Self::StaticMapping { name, backing } => Self::StaticMapping { 166 | name: name.clone(), 167 | backing: backing.fork(), 168 | }, 169 | } 170 | } 171 | 172 | pub fn restore(&mut self, other: &Self) { 173 | match (self, other) { 174 | ( 175 | Self::Static { name, offset }, 176 | Self::Static { 177 | name: rname, 178 | offset: roffset, 179 | }, 180 | ) => { 181 | if name != rname || offset != roffset { 182 | panic!("attempting to restore segment `{}` at {} from incompatible segment `{}` at {}", 183 | name, 184 | offset, 185 | rname, 186 | roffset 187 | ); 188 | } 189 | } 190 | ( 191 | Self::Mapping { name, backing }, 192 | Self::Mapping { 193 | name: rname, 194 | backing: rbacking, 195 | }, 196 | ) => { 197 | if name != rname 198 | || backing.base_address() != rbacking.base_address() 199 | || backing.len() != rbacking.len() 200 | { 201 | panic!("attempting to restore segment `{}` at {} of size {} from incompatible segment `{}` at {} of size {}", 202 | name, 203 | backing.base_address(), 204 | backing.len(), 205 | rname, 206 | rbacking.base_address(), 207 | rbacking.len(), 208 | ); 209 | } 210 | 211 | backing.restore(rbacking); 212 | } 213 | ( 214 | Self::StaticMapping { name, backing }, 215 | Self::StaticMapping { 216 | name: rname, 217 | backing: rbacking, 218 | }, 219 | ) => { 220 | if name != rname || backing.len() != rbacking.len() { 221 | panic!("attempting to restore segment `{}` of size {} from incompatible segment `{}` of size {}", 222 | name, 223 | backing.len(), 224 | rname, 225 | rbacking.len(), 226 | ); 227 | } 228 | 229 | backing.restore(rbacking); 230 | } 231 | (slf, oth) => panic!( 232 | "attempting to restore segment `{}` from segment `{}` which have different kinds", 233 | slf.name(), 234 | oth.name() 235 | ), 236 | } 237 | } 238 | } 239 | 240 | #[derive(Debug, Clone)] 241 | pub struct PagedState { 242 | segments: IntervalMap>, 243 | inner: FlatState, 244 | } 245 | 246 | impl AsRef for PagedState { 247 | #[inline(always)] 248 | fn as_ref(&self) -> &Self { 249 | self 250 | } 251 | } 252 | 253 | impl AsMut for PagedState { 254 | #[inline(always)] 255 | fn as_mut(&mut self) -> &mut Self { 256 | self 257 | } 258 | } 259 | 260 | impl PagedState { 261 | pub fn new( 262 | mapping: impl IntoIterator, Segment)>, 263 | space: Arc, 264 | size: usize, 265 | ) -> Self { 266 | Self::from_parts(mapping, FlatState::new(space, size)) 267 | } 268 | 269 | pub fn from_parts( 270 | mapping: impl IntoIterator, Segment)>, 271 | backing: FlatState, 272 | ) -> Self { 273 | Self { 274 | segments: IntervalMap::from_iter(mapping.into_iter().map(|(r, s)| (r.start..r.end, s))), 275 | inner: backing, 276 | } 277 | } 278 | 279 | pub fn static_mapping( 280 | &mut self, 281 | name: S, 282 | base_address: A, 283 | size: usize, 284 | ) -> Result<(), Error> 285 | where 286 | S: AsRef, 287 | A: Into
, 288 | { 289 | let base_address = base_address.into(); 290 | let range = base_address..base_address + size; // TODO: error for zero-size 291 | 292 | if self.segments.has_overlap(range.clone()) { 293 | return Err(Error::OverlappedMapping { 294 | address: base_address, 295 | size, 296 | }); 297 | } 298 | 299 | self.segments.insert( 300 | range, 301 | Segment::static_mapping(name, FlatState::new(self.inner.address_space(), size)), 302 | ); 303 | Ok(()) 304 | } 305 | 306 | pub fn mapping(&mut self, name: S, base_address: A, size: usize) -> Result<(), Error> 307 | where 308 | S: AsRef, 309 | A: Into
, 310 | { 311 | let base_address = base_address.into(); 312 | let range = base_address..base_address + size; // TODO: error for zero-size 313 | 314 | if self.segments.has_overlap(range.clone()) { 315 | return Err(Error::OverlappedMapping { 316 | address: base_address, 317 | size, 318 | }); 319 | } 320 | 321 | self.segments.insert( 322 | range, 323 | Segment::mapping( 324 | name, 325 | ChunkState::new(self.inner.address_space(), base_address, size), 326 | ), 327 | ); 328 | Ok(()) 329 | } 330 | 331 | pub fn segments(&self) -> &IntervalMap> { 332 | &self.segments 333 | } 334 | 335 | pub fn mappings(&self) -> impl Iterator> { 336 | self.segments.values(..).filter_map(|v| { 337 | if let Segment::Mapping { backing, .. } = v { 338 | Some(backing) 339 | } else { 340 | None 341 | } 342 | }) 343 | } 344 | 345 | pub fn mapping_for(&self, address: A) -> Option> 346 | where 347 | A: Into
, 348 | { 349 | let address = address.into(); 350 | if address + 1usize < address { 351 | return None; 352 | } 353 | self.segments 354 | .values_overlap(address) 355 | .next() 356 | .and_then(|e| e.as_mapping()) 357 | } 358 | 359 | pub fn mapping_for_mut(&mut self, address: A) -> Option> 360 | where 361 | A: Into
, 362 | { 363 | let address = address.into(); 364 | if address + 1usize < address { 365 | return None; 366 | } 367 | self.segments 368 | .values_overlap_mut(address) 369 | .next() 370 | .and_then(|e| e.as_mapping_mut()) 371 | } 372 | 373 | pub fn inner(&self) -> &FlatState { 374 | &self.inner 375 | } 376 | 377 | pub fn inner_mut(&mut self) -> &mut FlatState { 378 | &mut self.inner 379 | } 380 | 381 | pub fn address_space(&self) -> Arc { 382 | self.inner.address_space() 383 | } 384 | 385 | pub fn address_space_ref(&self) -> &AddressSpace { 386 | self.inner.address_space_ref() 387 | } 388 | } 389 | 390 | impl PagedState { 391 | #[inline(always)] 392 | pub fn with_flat<'a, A, F, O: 'a>( 393 | &'a self, 394 | address: A, 395 | access_size: usize, 396 | f: F, 397 | ) -> Result 398 | where 399 | A: Into
, 400 | F: FnOnce(&'a FlatState, Address, usize) -> Result, 401 | { 402 | let address = address.into(); 403 | if address + 1usize < address { 404 | return Err(Error::UnmappedAddress { 405 | address, 406 | size: access_size, 407 | }); 408 | } 409 | if let Some((interval, value)) = self.segments.overlap(address).next() { 410 | if address + access_size > interval.end { 411 | // FIXME: checked arith. 412 | return Err(Error::OverlappedAccess { 413 | address, 414 | size: access_size, 415 | }); 416 | } 417 | 418 | match value { 419 | Segment::Mapping { ref backing, .. } => { 420 | let translated = backing 421 | .translate_checked(address, access_size) 422 | .map_err(Error::Chunked)?; 423 | f( 424 | backing.inner(), 425 | Address::from(translated as u64), 426 | access_size, 427 | ) 428 | } 429 | Segment::StaticMapping { ref backing, .. } => { 430 | let translated = address - interval.start; 431 | f(backing, translated, access_size) 432 | } 433 | Segment::Static { offset, .. } => { 434 | let address = (address - interval.start) + *offset; 435 | f(&self.inner, address, access_size) 436 | } 437 | } 438 | } else { 439 | Err(Error::UnmappedAddress { 440 | address, 441 | size: access_size, 442 | }) 443 | } 444 | } 445 | 446 | #[inline(always)] 447 | pub fn with_flat_mut<'a, A, F, O: 'a>( 448 | &'a mut self, 449 | address: A, 450 | access_size: usize, 451 | f: F, 452 | ) -> Result 453 | where 454 | A: Into
, 455 | F: FnOnce(&'a mut FlatState, Address, usize) -> Result, 456 | { 457 | let address = address.into(); 458 | if address + 1usize < address { 459 | return Err(Error::UnmappedAddress { 460 | address, 461 | size: access_size, 462 | }); 463 | } 464 | if let Some((interval, value)) = self.segments.overlap_mut(address).next() { 465 | if address + access_size > interval.end { 466 | return Err(Error::OverlappedAccess { 467 | address, 468 | size: access_size, 469 | }); 470 | } 471 | match value { 472 | Segment::Mapping { 473 | ref mut backing, .. 474 | } => { 475 | let translated = backing 476 | .translate_checked(address, access_size) 477 | .map_err(Error::Chunked)?; 478 | f( 479 | backing.inner_mut(), 480 | Address::from(translated as u64), 481 | access_size, 482 | ) 483 | } 484 | Segment::StaticMapping { 485 | ref mut backing, .. 486 | } => { 487 | let translated = address - interval.start; 488 | f(backing, translated, access_size) 489 | } 490 | Segment::Static { offset, .. } => { 491 | let address = (address - interval.start) + *offset; 492 | f(&mut self.inner, address, access_size) 493 | } 494 | } 495 | } else { 496 | Err(Error::UnmappedAddress { 497 | address, 498 | size: access_size, 499 | }) 500 | } 501 | } 502 | 503 | #[inline(always)] 504 | pub fn with_flat_from<'a, A, F, O: 'a>(&'a self, address: A, f: F) -> Result 505 | where 506 | A: Into
, 507 | F: FnOnce(&'a FlatState, Address, usize) -> Result, 508 | { 509 | let address = address.into(); 510 | if address + 1usize < address { 511 | return Err(Error::UnmappedAddress { 512 | address, 513 | size: 1, 514 | }); 515 | } 516 | if let Some((interval, value)) = self.segments.overlap(address).next() { 517 | match value { 518 | Segment::Mapping { ref backing, .. } => { 519 | // TODO: Chunked::available_len (view whole allocation) 520 | let access_size = backing.len(); // FIXME: should this be -1 due to the red-zone? 521 | let translated = backing 522 | .translate_checked(address, access_size) 523 | .map_err(Error::Chunked)?; 524 | f( 525 | backing.inner(), 526 | Address::from(translated as u64), 527 | access_size, 528 | ) 529 | } 530 | Segment::StaticMapping { ref backing, .. } => { 531 | let max_access_size = 532 | usize::from(interval.end - interval.start); 533 | let address = address - interval.start; 534 | 535 | let access_size = max_access_size - usize::from(address); 536 | 537 | f(backing, address, access_size) 538 | } 539 | Segment::Static { offset, .. } => { 540 | let max_access_size = 541 | usize::from(interval.end - interval.start); 542 | let offset_in = address - interval.start; 543 | 544 | let address = offset_in + *offset; 545 | let access_size = max_access_size - usize::from(offset_in); 546 | 547 | f(&self.inner, address, access_size) 548 | } 549 | } 550 | } else { 551 | Err(Error::UnmappedAddress { address, size: 1 }) 552 | } 553 | } 554 | 555 | pub fn view_values_from(&self, address: A) -> Result<&[T], Error> 556 | where 557 | A: Into
, 558 | { 559 | self.with_flat_from(address, |inner, address, n| { 560 | inner 561 | .view_values(address, n) 562 | .map_err(|e| Error::backing(address, e)) 563 | }) 564 | } 565 | 566 | pub fn segment_bounds(&self, address: A) -> Result<(Range
, &Segment), Error> 567 | where 568 | A: Into
, 569 | { 570 | let address = address.into(); 571 | if address + 1usize < address { 572 | return Err(Error::UnmappedAddress { 573 | address, 574 | size: 1usize, 575 | }); 576 | } 577 | self.segments 578 | .overlap(address) 579 | .next() 580 | .ok_or_else(|| Error::UnmappedAddress { 581 | address, 582 | size: 1usize, 583 | }) 584 | } 585 | } 586 | 587 | impl State for PagedState { 588 | type Error = Error; 589 | 590 | fn fork(&self) -> Self { 591 | Self { 592 | segments: self.segments.iter(..).map(|(i, v)| (i, v.fork())).collect(), 593 | inner: self.inner.fork(), 594 | } 595 | } 596 | 597 | fn restore(&mut self, other: &Self) { 598 | self.inner.restore(&other.inner); 599 | 600 | let segments = take(&mut self.segments); 601 | self.segments = segments 602 | .into_iter(..) 603 | .filter_map(|(i, mut v)| { 604 | if let Some(vo) = other.segments.get(i.clone()) { 605 | v.restore(vo); 606 | Some((i, v)) 607 | } else { 608 | None 609 | } 610 | }) 611 | .collect(); 612 | } 613 | } 614 | 615 | impl StateOps for PagedState { 616 | type Value = V; 617 | 618 | fn copy_values(&mut self, from: F, to: T, size: usize) -> Result<(), Error> 619 | where 620 | F: Into
, 621 | T: Into
, 622 | { 623 | let from = from.into(); 624 | let to = to.into(); 625 | 626 | // TODO: can we avoid the intermediate allocation? 627 | 628 | let vals = self.view_values(from, size)?.to_vec(); 629 | let view = self.view_values_mut(to, size)?; 630 | 631 | for (d, s) in view.iter_mut().zip(vals.into_iter()) { 632 | *d = s; 633 | } 634 | 635 | Ok(()) 636 | } 637 | 638 | fn get_values(&self, address: A, values: &mut [Self::Value]) -> Result<(), Error> 639 | where 640 | A: Into
, 641 | { 642 | let address = address.into(); 643 | let n = values.len(); 644 | 645 | self.with_flat(address, n, |inner, address, _size| { 646 | inner 647 | .get_values(address, values) 648 | .map_err(|e| Error::backing(address, e)) 649 | }) 650 | } 651 | 652 | fn view_values(&self, address: A, n: usize) -> Result<&[Self::Value], Error> 653 | where 654 | A: Into
, 655 | { 656 | let address = address.into(); 657 | self.with_flat(address, n, |inner, address, n| { 658 | inner 659 | .view_values(address, n) 660 | .map_err(|e| Error::backing(address, e)) 661 | }) 662 | } 663 | 664 | fn view_values_mut(&mut self, address: A, n: usize) -> Result<&mut [Self::Value], Error> 665 | where 666 | A: Into
, 667 | { 668 | let address = address.into(); 669 | self.with_flat_mut(address, n, |inner, address, n| { 670 | inner 671 | .view_values_mut(address, n) 672 | .map_err(|e| Error::backing(address, e)) 673 | }) 674 | } 675 | 676 | fn set_values(&mut self, address: A, values: &[Self::Value]) -> Result<(), Error> 677 | where 678 | A: Into
, 679 | { 680 | let address = address.into(); 681 | let n = values.len(); 682 | self.with_flat_mut(address, n, |inner, address, _size| { 683 | inner 684 | .set_values(address, values) 685 | .map_err(|e| Error::backing(address, e)) 686 | }) 687 | } 688 | 689 | fn len(&self) -> usize { 690 | // what to do here? sum of all sizes? 691 | self.inner.len() + self.mappings().map(|m| m.len()).sum::() 692 | } 693 | } 694 | -------------------------------------------------------------------------------- /fuguex-state/src/pcode.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::sync::Arc; 3 | 4 | use fugue::bytes::{ByteCast, Order}; 5 | 6 | use fugue::ir::convention::Convention; 7 | use fugue::ir::il::pcode::Operand; 8 | use fugue::ir::{Address, AddressSpace, Translator}; 9 | 10 | use thiserror::Error; 11 | 12 | use crate::paged::{self, PagedState}; 13 | use crate::register::{self, RegisterState}; 14 | use crate::unique::{self, UniqueState}; 15 | 16 | use crate::traits::{State, StateOps, StateValue}; 17 | use crate::traits::{FromStateValues, IntoStateValues}; 18 | 19 | pub const POINTER_8_SIZE: usize = 1; 20 | pub const POINTER_16_SIZE: usize = 2; 21 | pub const POINTER_32_SIZE: usize = 4; 22 | pub const POINTER_64_SIZE: usize = 8; 23 | pub const MAX_POINTER_SIZE: usize = POINTER_64_SIZE; 24 | 25 | #[derive(Debug, Error)] 26 | pub enum Error { 27 | #[error(transparent)] 28 | Memory(paged::Error), 29 | #[error(transparent)] 30 | Register(register::Error), 31 | #[error(transparent)] 32 | Temporary(unique::Error), 33 | #[error("unsupported addess size of `{0}` bytes")] 34 | UnsupportedAddressSize(usize), 35 | } 36 | 37 | #[derive(Debug, Clone)] 38 | pub struct PCodeState { 39 | memory: PagedState, 40 | registers: RegisterState, 41 | temporaries: UniqueState, 42 | convention: Convention, 43 | marker: PhantomData, 44 | } 45 | 46 | impl AsRef for PCodeState { 47 | fn as_ref(&self) -> &Self { 48 | self 49 | } 50 | } 51 | 52 | impl AsMut for PCodeState { 53 | fn as_mut(&mut self) -> &mut Self { 54 | self 55 | } 56 | } 57 | 58 | impl PCodeState { 59 | pub fn new(memory: PagedState, translator: &Translator, convention: &Convention) -> Self { 60 | Self { 61 | memory, 62 | registers: RegisterState::new(translator, convention), 63 | temporaries: UniqueState::new(translator), 64 | convention: convention.clone(), 65 | marker: PhantomData, 66 | } 67 | } 68 | 69 | pub fn convention(&self) -> &Convention { 70 | &self.convention 71 | } 72 | 73 | pub fn memory(&self) -> &PagedState { 74 | &self.memory 75 | } 76 | 77 | pub fn memory_mut(&mut self) -> &mut PagedState { 78 | &mut self.memory 79 | } 80 | 81 | pub fn memory_space(&self) -> Arc { 82 | self.memory().address_space() 83 | } 84 | 85 | pub fn memory_space_ref(&self) -> &AddressSpace { 86 | self.memory().address_space_ref() 87 | } 88 | 89 | pub fn registers(&self) -> &RegisterState { 90 | &self.registers 91 | } 92 | 93 | pub fn registers_mut(&mut self) -> &mut RegisterState { 94 | &mut self.registers 95 | } 96 | 97 | pub fn temporaries(&self) -> &UniqueState { 98 | &self.temporaries 99 | } 100 | 101 | pub fn temporaries_mut(&mut self) -> &mut UniqueState { 102 | &mut self.temporaries 103 | } 104 | 105 | pub fn with_operand_values(&self, operand: &Operand, f: F) -> Result 106 | where F: FnOnce(&[T]) -> U { 107 | match operand { 108 | Operand::Address { value, size } => { 109 | self.memory() 110 | .view_values(value.offset(), *size) 111 | .map_err(Error::Memory) 112 | .map(f) 113 | }, 114 | Operand::Constant { value, size, .. } => { 115 | // max size of value 116 | let mut values: [T; 8] = Default::default(); 117 | 118 | if O::ENDIAN.is_big() { 119 | for (d, s) in values[..*size].iter_mut().zip(&value.to_be_bytes()[8-*size..]) { 120 | *d = T::from_byte(*s); 121 | } 122 | } else { 123 | for (d, s) in values[..*size].iter_mut().zip(&value.to_le_bytes()[..*size]) { 124 | *d = T::from_byte(*s); 125 | } 126 | } 127 | 128 | Ok(f(&values[..*size])) 129 | }, 130 | Operand::Register { offset, size, .. } => { 131 | self.registers() 132 | .view_values(*offset, *size) 133 | .map_err(Error::Register) 134 | .map(f) 135 | }, 136 | Operand::Variable { offset, size, .. } => { 137 | self.temporaries() 138 | .view_values(*offset, *size) 139 | .map_err(Error::Temporary) 140 | .map(f) 141 | }, 142 | } 143 | } 144 | 145 | pub fn with_operand_values_mut(&mut self, operand: &Operand, f: F) -> Result 146 | where F: FnOnce(&mut [T]) -> U { 147 | match operand { 148 | Operand::Address { value, size } => { 149 | self.memory_mut() 150 | .view_values_mut(value.offset(), *size) 151 | .map_err(Error::Memory) 152 | .map(f) 153 | }, 154 | Operand::Register { offset, size, .. } => { 155 | self.registers_mut() 156 | .view_values_mut(*offset, *size) 157 | .map_err(Error::Register) 158 | .map(f) 159 | }, 160 | Operand::Variable { offset, size, .. } => { 161 | self.temporaries_mut() 162 | .view_values_mut(*offset, *size) 163 | .map_err(Error::Temporary) 164 | .map(f) 165 | }, 166 | Operand::Constant { .. } => { 167 | panic!("cannot mutate Operand::Constant"); 168 | }, 169 | } 170 | } 171 | 172 | pub fn get_operand>(&self, operand: &Operand) -> Result { 173 | let res = self.with_operand_values(operand, |values| V::from_values::(values)); 174 | res 175 | } 176 | 177 | pub fn set_operand>(&mut self, operand: &Operand, value: V) -> Result<(), Error> { 178 | self.with_operand_values_mut(operand, |values| value.into_values::(values)) 179 | } 180 | 181 | #[inline(always)] 182 | pub fn view_values_from(&self, address: A) -> Result<&[T], Error> 183 | where A: Into
{ 184 | self.memory.view_values_from(address) 185 | .map_err(Error::Memory) 186 | } 187 | } 188 | 189 | impl PCodeState { 190 | pub fn program_counter_value(&self) -> Result { 191 | self.get_address(&self.registers.program_counter()) 192 | } 193 | 194 | pub fn stack_pointer_value(&self) -> Result { 195 | self.get_address(&self.registers.stack_pointer()) 196 | } 197 | 198 | pub fn get_pointer(&self, address: Address) -> Result { 199 | let opnd = Operand::Address { 200 | value: address, 201 | size: self.memory_space_ref().address_size(), 202 | }; 203 | self.get_address(&opnd) 204 | } 205 | 206 | // get value at address 207 | pub fn get_address(&self, operand: &Operand) -> Result { 208 | let mut buf = [0u8; MAX_POINTER_SIZE]; 209 | let size = operand.size(); 210 | 211 | let address = if size == POINTER_64_SIZE { 212 | self.with_operand_values(operand, |values| { 213 | buf[..size].copy_from_slice(values); 214 | u64::from_bytes::(values) 215 | }) 216 | } else if size == POINTER_32_SIZE { 217 | self.with_operand_values(operand, |values| { 218 | buf[..size].copy_from_slice(values); 219 | u32::from_bytes::(values) as u64 220 | }) 221 | } else if size == POINTER_16_SIZE { 222 | self.with_operand_values(operand, |values| { 223 | buf[..size].copy_from_slice(values); 224 | u16::from_bytes::(values) as u64 225 | }) 226 | } else if size == POINTER_8_SIZE { 227 | self.with_operand_values(operand, |values| { 228 | buf[..size].copy_from_slice(values); 229 | u8::from_bytes::(values) as u64 230 | }) 231 | } else { 232 | return Err(Error::UnsupportedAddressSize(size)) 233 | }?; 234 | 235 | Ok(Address::new(self.memory.address_space_ref(), address)) 236 | } 237 | 238 | pub fn set_program_counter_value(&mut self, value: A) -> Result<(), Error> 239 | where A: Into
{ 240 | self.set_address(&self.registers.program_counter(), value) 241 | } 242 | 243 | pub fn set_stack_pointer_value(&mut self, value: A) -> Result<(), Error> 244 | where A: Into
{ 245 | self.set_address(&self.registers.stack_pointer(), value) 246 | } 247 | 248 | pub fn set_address(&mut self, operand: &Operand, value: A) -> Result<(), Error> 249 | where A: Into
{ 250 | 251 | let size = operand.size(); 252 | let address = value.into(); 253 | 254 | if size == POINTER_64_SIZE { 255 | self.with_operand_values_mut(operand, |values| { 256 | u64::from(address).into_bytes::(values) 257 | }) 258 | } else if size == POINTER_32_SIZE { 259 | self.with_operand_values_mut(operand, |values| { 260 | u32::from(address).into_bytes::(values) 261 | }) 262 | } else if size == POINTER_16_SIZE { 263 | self.with_operand_values_mut(operand, |values| { 264 | u16::from(address).into_bytes::(values) 265 | }) 266 | } else if size == POINTER_8_SIZE { 267 | self.with_operand_values_mut(operand, |values| { 268 | u8::from(address).into_bytes::(values) 269 | }) 270 | } else { 271 | return Err(Error::UnsupportedAddressSize(size)) 272 | }?; 273 | 274 | Ok(()) 275 | } 276 | } 277 | 278 | impl State for PCodeState { 279 | type Error = Error; 280 | 281 | fn fork(&self) -> Self { 282 | Self { 283 | convention: self.convention.clone(), 284 | registers: self.registers.fork(), 285 | temporaries: self.temporaries.fork(), 286 | memory: self.memory.fork(), 287 | marker: self.marker, 288 | } 289 | } 290 | 291 | fn restore(&mut self, other: &Self) { 292 | self.registers.restore(&other.registers); 293 | self.temporaries.restore(&other.temporaries); 294 | self.memory.restore(&other.memory); 295 | } 296 | } 297 | 298 | impl StateOps for PCodeState { 299 | type Value = V; 300 | 301 | #[inline(always)] 302 | fn copy_values(&mut self, from: F, to: T, size: usize) -> Result<(), Self::Error> 303 | where F: Into
, 304 | T: Into
{ 305 | self.memory.copy_values(from, to, size) 306 | .map_err(Error::Memory) 307 | } 308 | 309 | #[inline(always)] 310 | fn get_values(&self, address: A, values: &mut [Self::Value]) -> Result<(), Self::Error> 311 | where A: Into
{ 312 | self.memory.get_values(address, values) 313 | .map_err(Error::Memory) 314 | } 315 | 316 | #[inline(always)] 317 | fn view_values(&self, address: A, size: usize) -> Result<&[Self::Value], Self::Error> 318 | where A: Into
{ 319 | self.memory.view_values(address, size) 320 | .map_err(Error::Memory) 321 | } 322 | 323 | #[inline(always)] 324 | fn view_values_mut(&mut self, address: A, size: usize) -> Result<&mut [Self::Value], Self::Error> 325 | where A: Into
{ 326 | self.memory.view_values_mut(address, size) 327 | .map_err(Error::Memory) 328 | } 329 | 330 | #[inline(always)] 331 | fn set_values(&mut self, address: A, values: &[Self::Value]) -> Result<(), Self::Error> 332 | where A: Into
{ 333 | self.memory.set_values(address, values) 334 | .map_err(Error::Memory) 335 | } 336 | 337 | #[inline(always)] 338 | fn len(&self) -> usize { 339 | self.memory.len() 340 | } 341 | } 342 | -------------------------------------------------------------------------------- /fuguex-state/src/register.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::ops::{Deref, DerefMut}; 3 | use std::sync::Arc; 4 | 5 | use fugue::bytes::Order; 6 | use fugue::ir::convention::{Convention, ReturnAddress}; 7 | use fugue::ir::il::pcode::{Operand, Register}; 8 | use fugue::ir::register::RegisterNames; 9 | use fugue::ir::{Address, Translator}; 10 | 11 | use crate::{FromStateValues, IntoStateValues, State, StateOps, StateValue}; 12 | use crate::flat::FlatState; 13 | 14 | pub use crate::flat::Error; 15 | 16 | #[derive(Debug, Clone, Eq, PartialEq, Hash)] 17 | pub enum ReturnLocation { 18 | Register(Operand), 19 | Relative(Operand, u64), 20 | } 21 | 22 | impl ReturnLocation { 23 | pub fn from_convention(translator: &Translator, convention: &Convention) -> Self { 24 | match convention.return_address() { 25 | ReturnAddress::Register { varnode, .. } => { 26 | Self::Register(Operand::from_varnode(translator, *varnode)) 27 | }, 28 | ReturnAddress::StackRelative { offset, .. } => { 29 | Self::Relative( 30 | Operand::from_varnode(translator, *convention.stack_pointer().varnode()), 31 | *offset, 32 | ) 33 | } 34 | } 35 | } 36 | } 37 | 38 | #[derive(Debug, Clone)] 39 | pub struct RegisterState { 40 | program_counter: Arc, 41 | stack_pointer: Arc, 42 | register_names: Arc, 43 | return_location: Arc, 44 | inner: FlatState, 45 | marker: PhantomData, 46 | } 47 | 48 | impl AsRef for RegisterState { 49 | #[inline(always)] 50 | fn as_ref(&self) -> &Self { 51 | self 52 | } 53 | } 54 | 55 | impl AsMut for RegisterState { 56 | #[inline(always)] 57 | fn as_mut(&mut self) -> &mut Self { 58 | self 59 | } 60 | } 61 | 62 | impl AsRef> for RegisterState { 63 | #[inline(always)] 64 | fn as_ref(&self) -> &FlatState { 65 | &self.inner 66 | } 67 | } 68 | 69 | impl AsMut> for RegisterState { 70 | #[inline(always)] 71 | fn as_mut(&mut self) -> &mut FlatState { 72 | &mut self.inner 73 | } 74 | } 75 | 76 | impl Deref for RegisterState { 77 | type Target = FlatState; 78 | 79 | fn deref(&self) -> &Self::Target { 80 | &self.inner 81 | } 82 | } 83 | 84 | impl DerefMut for RegisterState { 85 | fn deref_mut(&mut self) -> &mut Self::Target { 86 | &mut self.inner 87 | } 88 | } 89 | 90 | impl From> for FlatState { 91 | fn from(t: RegisterState) -> Self { 92 | t.inner 93 | } 94 | } 95 | 96 | impl State for RegisterState { 97 | type Error = Error; 98 | 99 | #[inline(always)] 100 | fn fork(&self) -> Self { 101 | Self { 102 | inner: self.inner.fork(), 103 | stack_pointer: self.stack_pointer.clone(), 104 | program_counter: self.program_counter.clone(), 105 | return_location: self.return_location.clone(), 106 | register_names: self.register_names.clone(), 107 | marker: PhantomData, 108 | } 109 | } 110 | 111 | #[inline(always)] 112 | fn restore(&mut self, other: &Self) { 113 | self.inner.restore(&other.inner) 114 | } 115 | } 116 | 117 | impl StateOps for RegisterState { 118 | type Value = V; 119 | 120 | #[inline(always)] 121 | fn len(&self) -> usize { 122 | self.inner.len() 123 | } 124 | 125 | #[inline(always)] 126 | fn copy_values(&mut self, from: F, to: T, size: usize) -> Result<(), Self::Error> 127 | where F: Into
, 128 | T: Into
{ 129 | self.inner.copy_values(from, to, size) 130 | } 131 | 132 | #[inline(always)] 133 | fn get_values(&self, address: A, bytes: &mut [Self::Value]) -> Result<(), Self::Error> 134 | where A: Into
{ 135 | self.inner.get_values(address, bytes) 136 | } 137 | 138 | #[inline(always)] 139 | fn view_values(&self, address: A, size: usize) -> Result<&[Self::Value], Self::Error> 140 | where A: Into
{ 141 | self.inner.view_values(address, size) 142 | } 143 | 144 | #[inline(always)] 145 | fn view_values_mut(&mut self, address: A, size: usize) -> Result<&mut [Self::Value], Self::Error> 146 | where A: Into
{ 147 | self.inner.view_values_mut(address, size) 148 | } 149 | 150 | #[inline(always)] 151 | fn set_values(&mut self, address: A, bytes: &[Self::Value]) -> Result<(), Self::Error> 152 | where A: Into
{ 153 | self.inner.set_values(address, bytes) 154 | } 155 | } 156 | 157 | impl RegisterState { 158 | pub fn new(translator: &Translator, convention: &Convention) -> Self { 159 | let program_counter = Arc::new(Operand::from_varnode(translator, *translator.program_counter())); 160 | let stack_pointer = Arc::new(Operand::from_varnode(translator, *convention.stack_pointer().varnode())); 161 | let return_location = Arc::new(ReturnLocation::from_convention(translator, convention)); 162 | 163 | let space = translator.manager().register_space(); 164 | let register_names = translator.registers().clone(); 165 | let size = translator.register_space_size(); 166 | 167 | log::debug!("register space size: {} bytes", size); 168 | 169 | Self { 170 | inner: FlatState::new(space, size), 171 | program_counter, 172 | stack_pointer, 173 | return_location, 174 | register_names, 175 | marker: PhantomData, 176 | } 177 | } 178 | 179 | pub fn program_counter(&self) -> Arc { 180 | self.program_counter.clone() 181 | } 182 | 183 | pub fn stack_pointer(&self) -> Arc { 184 | self.stack_pointer.clone() 185 | } 186 | 187 | pub fn return_location(&self) -> Arc { 188 | self.return_location.clone() 189 | } 190 | 191 | pub fn get_register_values(&self, register: &Register, values: &mut [T]) -> Result<(), Error> { 192 | let view = self.view_values(register.offset(), register.size())?; 193 | values.clone_from_slice(view); 194 | Ok(()) 195 | } 196 | 197 | pub fn register_names(&self) -> &Arc { 198 | &self.register_names 199 | } 200 | 201 | pub fn register_by_name(&self, name: N) -> Option 202 | where N: AsRef { 203 | self.register_names 204 | .get_by_name(name) 205 | .map(|(name, offset, size)| Register::new(name.clone(), offset, size)) 206 | } 207 | 208 | pub fn get_register>(&self, register: &Register) -> Result { 209 | Ok(V::from_values::(self.view_values(register.offset(), register.size())?)) 210 | } 211 | 212 | pub fn set_register_values(&mut self, register: &Register, values: &[T]) -> Result<(), Error> { 213 | let view = self.view_values_mut(register.offset(), register.size())?; 214 | view.clone_from_slice(values); 215 | Ok(()) 216 | } 217 | 218 | pub fn set_register>(&mut self, register: &Register, value: V) -> Result<(), Error> { 219 | value.into_values::(self.view_values_mut(register.offset(), register.size())?); 220 | Ok(()) 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /fuguex-state/src/traits.rs: -------------------------------------------------------------------------------- 1 | use fugue::bv::BitVec; 2 | 3 | use fugue::bytes::Order; 4 | use fugue::bytes::traits::ByteCast; 5 | 6 | use fugue::ir::Address; 7 | 8 | use paste::paste; 9 | 10 | pub use fuguex_state_derive::AsState; 11 | 12 | pub trait StateValue: Clone + Default { 13 | fn from_byte(value: u8) -> Self; 14 | } 15 | 16 | impl StateValue for V where V: Clone + Default + From { 17 | #[inline(always)] 18 | fn from_byte(value: u8) -> Self { 19 | Self::from(value) 20 | } 21 | } 22 | 23 | pub trait FromStateValues: Sized { 24 | fn from_values(values: &[V]) -> Self; 25 | } 26 | 27 | pub trait IntoStateValues: Sized { 28 | fn into_values(self, values: &mut [V]); 29 | } 30 | 31 | macro_rules! impl_for { 32 | ($t:ident) => { 33 | impl FromStateValues for $t { 34 | #[inline(always)] 35 | fn from_values(buf: &[u8]) -> Self { 36 | <$t as ByteCast>::from_bytes::(buf) 37 | } 38 | } 39 | 40 | impl IntoStateValues for $t { 41 | #[inline(always)] 42 | fn into_values(self, buf: &mut [u8]) { 43 | <$t as ByteCast>::into_bytes::(&self, buf) 44 | } 45 | } 46 | }; 47 | } 48 | 49 | macro_rules! impls_for { 50 | [$($tname:ident),*] => { 51 | $( 52 | paste! { 53 | impl_for!($tname); 54 | } 55 | )* 56 | }; 57 | } 58 | 59 | impls_for![bool, i8, i16, i32, i64, i128, isize, u8, u16, u32, u64, u128, usize]; 60 | 61 | impl FromStateValues for BitVec { 62 | #[inline(always)] 63 | fn from_values(values: &[u8]) -> Self { 64 | BitVec::from_bytes::(values, false) 65 | } 66 | } 67 | 68 | impl IntoStateValues for BitVec { 69 | #[inline(always)] 70 | fn into_values(self, values: &mut [u8]) { 71 | self.into_bytes::(values) 72 | } 73 | } 74 | 75 | impl IntoStateValues for &'_ BitVec { 76 | #[inline(always)] 77 | fn into_values(self, values: &mut [u8]) { 78 | if O::ENDIAN.is_big() { 79 | self.to_be_bytes(values) 80 | } else { 81 | self.to_le_bytes(values) 82 | } 83 | } 84 | } 85 | 86 | pub trait State: Clone { 87 | type Error: std::error::Error; 88 | 89 | fn fork(&self) -> Self; 90 | fn restore(&mut self, other: &Self); 91 | } 92 | 93 | pub trait StateOps: State { 94 | type Value: StateValue; 95 | 96 | fn len(&self) -> usize; 97 | 98 | fn copy_values(&mut self, from: F, to: T, size: usize) -> Result<(), Self::Error> 99 | where F: Into
, 100 | T: Into
; 101 | 102 | fn get_values(&self, address: A, bytes: &mut [Self::Value]) -> Result<(), Self::Error> 103 | where A: Into
; 104 | 105 | fn view_values(&self, address: A, size: usize) -> Result<&[Self::Value], Self::Error> 106 | where A: Into
; 107 | 108 | fn view_values_mut(&mut self, address: A, size: usize) -> Result<&mut [Self::Value], Self::Error> 109 | where A: Into
; 110 | 111 | fn set_values(&mut self, address: A, bytes: &[Self::Value]) -> Result<(), Self::Error> 112 | where A: Into
; 113 | } 114 | 115 | pub trait AsState: State { 116 | fn state_ref(&self) -> &S; 117 | fn state_mut(&mut self) -> &mut S; 118 | } 119 | 120 | impl AsState for T where T: State + AsRef + AsMut { 121 | fn state_ref(&self) -> &S { 122 | self.as_ref() 123 | } 124 | 125 | fn state_mut(&mut self) -> &mut S { 126 | self.as_mut() 127 | } 128 | } 129 | 130 | pub trait AsState2: State + AsState + AsState { 131 | fn state2_ref(&self) -> (&S, &T) { 132 | (self.state_ref(), self.state_ref()) 133 | } 134 | 135 | fn state2_mut(&mut self) -> (&mut S, &mut T); 136 | } 137 | 138 | pub trait AsState3: State + AsState + AsState + AsState { 139 | fn state3_ref(&self) -> (&S, &T, &U) { 140 | (self.state_ref(), self.state_ref(), self.state_ref()) 141 | } 142 | 143 | fn state3_mut(&mut self) -> (&mut S, &mut T, &mut U); 144 | } 145 | 146 | pub trait AsState4: State + AsState + AsState + AsState + AsState { 147 | fn state4_ref(&self) -> (&S, &T, &U, &V) { 148 | (self.state_ref(), self.state_ref(), self.state_ref(), self.state_ref()) 149 | } 150 | 151 | fn state4_mut(&mut self) -> (&mut S, &mut T, &mut U, &mut V); 152 | } 153 | -------------------------------------------------------------------------------- /fuguex-state/src/unique.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Deref, DerefMut}; 2 | 3 | use fugue::ir::{Address, Translator}; 4 | 5 | use crate::flat::FlatState; 6 | use crate::traits::{State, StateOps, StateValue}; 7 | 8 | pub use crate::flat::Error; 9 | 10 | #[derive(Debug, Clone, Eq, PartialEq, Hash)] 11 | #[repr(transparent)] 12 | pub struct UniqueState(FlatState); 13 | 14 | impl AsRef for UniqueState { 15 | #[inline(always)] 16 | fn as_ref(&self) -> &Self { 17 | self 18 | } 19 | } 20 | 21 | impl AsMut for UniqueState { 22 | #[inline(always)] 23 | fn as_mut(&mut self) -> &mut Self { 24 | self 25 | } 26 | } 27 | 28 | impl AsRef> for UniqueState { 29 | #[inline(always)] 30 | fn as_ref(&self) -> &FlatState { 31 | &self.0 32 | } 33 | } 34 | 35 | impl AsMut> for UniqueState { 36 | #[inline(always)] 37 | fn as_mut(&mut self) -> &mut FlatState { 38 | &mut self.0 39 | } 40 | } 41 | 42 | impl Deref for UniqueState { 43 | type Target = FlatState; 44 | 45 | fn deref(&self) -> &Self::Target { 46 | &self.0 47 | } 48 | } 49 | 50 | impl DerefMut for UniqueState { 51 | fn deref_mut(&mut self) -> &mut Self::Target { 52 | &mut self.0 53 | } 54 | } 55 | 56 | impl From> for FlatState { 57 | fn from(t: UniqueState) -> Self { 58 | t.0 59 | } 60 | } 61 | 62 | impl State for UniqueState { 63 | type Error = Error; 64 | 65 | #[inline(always)] 66 | fn fork(&self) -> Self { 67 | Self(self.0.fork()) 68 | } 69 | 70 | #[inline(always)] 71 | fn restore(&mut self, other: &Self) { 72 | self.0.restore(&other.0) 73 | } 74 | } 75 | 76 | impl StateOps for UniqueState { 77 | type Value = V; 78 | 79 | #[inline(always)] 80 | fn len(&self) -> usize { 81 | self.0.len() 82 | } 83 | 84 | #[inline(always)] 85 | fn copy_values(&mut self, from: F, to: T, size: usize) -> Result<(), Self::Error> 86 | where F: Into
, 87 | T: Into
{ 88 | self.0.copy_values(from, to, size) 89 | } 90 | 91 | #[inline(always)] 92 | fn get_values(&self, address: A, bytes: &mut [Self::Value]) -> Result<(), Self::Error> 93 | where A: Into
{ 94 | self.0.get_values(address, bytes) 95 | } 96 | 97 | #[inline(always)] 98 | fn view_values(&self, address: A, size: usize) -> Result<&[Self::Value], Self::Error> 99 | where A: Into
{ 100 | self.0.view_values(address, size) 101 | } 102 | 103 | #[inline(always)] 104 | fn view_values_mut(&mut self, address: A, size: usize) -> Result<&mut [Self::Value], Self::Error> 105 | where A: Into
{ 106 | self.0.view_values_mut(address, size) 107 | } 108 | 109 | #[inline(always)] 110 | fn set_values(&mut self, address: A, bytes: &[Self::Value]) -> Result<(), Self::Error> 111 | where A: Into
{ 112 | self.0.set_values(address, bytes) 113 | } 114 | } 115 | 116 | impl UniqueState { 117 | pub fn new(translator: &Translator) -> Self { 118 | let space = translator.manager().unique_space(); 119 | let size = translator.unique_space_size(); 120 | 121 | log::debug!("unique space size: {} bytes", size); 122 | 123 | Self(FlatState::new(space, size)) 124 | } 125 | } 126 | --------------------------------------------------------------------------------