diff options
Diffstat (limited to 'rust/src/basic_block.rs')
| -rw-r--r-- | rust/src/basic_block.rs | 310 |
1 files changed, 310 insertions, 0 deletions
diff --git a/rust/src/basic_block.rs b/rust/src/basic_block.rs new file mode 100644 index 00000000..b880d28f --- /dev/null +++ b/rust/src/basic_block.rs @@ -0,0 +1,310 @@ +// Copyright 2021-2024 Vector 35 Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +use crate::architecture::CoreArchitecture; +use crate::function::Function; +use crate::rc::*; +use crate::BranchType; +use binaryninjacore_sys::*; +use std::fmt; +use std::fmt::Debug; + +enum EdgeDirection { + Incoming, + Outgoing, +} + +pub struct Edge<'a, C: 'a + BlockContext> { + pub branch: BranchType, + pub back_edge: bool, + pub source: Guard<'a, BasicBlock<C>>, + target: Guard<'a, BasicBlock<C>>, +} + +impl<'a, C: 'a + fmt::Debug + BlockContext> fmt::Debug for Edge<'a, C> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{:?} ({}) {:?} -> {:?}", + self.branch, self.back_edge, &*self.source, &*self.target + ) + } +} + +pub struct EdgeContext<'a, C: 'a + BlockContext> { + dir: EdgeDirection, + orig_block: &'a BasicBlock<C>, +} + +impl<'a, C: 'a + BlockContext> CoreArrayProvider for Edge<'a, C> { + type Raw = BNBasicBlockEdge; + type Context = EdgeContext<'a, C>; + type Wrapped<'b> + = Edge<'b, C> + where + 'a: 'b; +} + +unsafe impl<'a, C: 'a + BlockContext> CoreArrayProviderInner for Edge<'a, C> { + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { + BNFreeBasicBlockEdgeList(raw, count); + } + + unsafe fn wrap_raw<'b>(raw: &'b Self::Raw, context: &'b Self::Context) -> Self::Wrapped<'b> { + let edge_target = Guard::new( + BasicBlock::from_raw(raw.target, context.orig_block.context.clone()), + raw, + ); + let orig_block = Guard::new( + BasicBlock::from_raw( + context.orig_block.handle, + context.orig_block.context.clone(), + ), + raw, + ); + + let (source, target) = match context.dir { + EdgeDirection::Incoming => (edge_target, orig_block), + EdgeDirection::Outgoing => (orig_block, edge_target), + }; + + Edge { + branch: raw.type_, + back_edge: raw.backEdge, + source, + target, + } + } +} + +pub trait BlockContext: Clone + Sync + Send + Sized { + type Instruction; + type InstructionIndex: Debug + From<u64>; + type Iter: Iterator<Item = Self::Instruction>; + + fn start(&self, block: &BasicBlock<Self>) -> Self::Instruction; + fn iter(&self, block: &BasicBlock<Self>) -> Self::Iter; +} + +#[derive(PartialEq, Eq, Hash)] +pub struct BasicBlock<C: BlockContext> { + pub(crate) handle: *mut BNBasicBlock, + context: C, +} + +impl<C: BlockContext> BasicBlock<C> { + pub(crate) unsafe fn from_raw(handle: *mut BNBasicBlock, context: C) -> Self { + Self { handle, context } + } + + pub(crate) unsafe fn ref_from_raw(handle: *mut BNBasicBlock, context: C) -> Ref<Self> { + Ref::new(Self::from_raw(handle, context)) + } + + // TODO native bb vs il bbs + pub fn function(&self) -> Ref<Function> { + unsafe { + let func = BNGetBasicBlockFunction(self.handle); + Function::ref_from_raw(func) + } + } + + pub fn arch(&self) -> CoreArchitecture { + unsafe { + let arch = BNGetBasicBlockArchitecture(self.handle); + CoreArchitecture::from_raw(arch) + } + } + + pub fn iter(&self) -> C::Iter { + self.context.iter(self) + } + + pub fn start_index(&self) -> C::InstructionIndex { + C::InstructionIndex::from(unsafe { BNGetBasicBlockStart(self.handle) }) + } + + pub fn end_index(&self) -> C::InstructionIndex { + C::InstructionIndex::from(unsafe { BNGetBasicBlockEnd(self.handle) }) + } + + pub fn raw_length(&self) -> u64 { + unsafe { BNGetBasicBlockLength(self.handle) } + } + + pub fn incoming_edges(&self) -> Array<Edge<C>> { + unsafe { + let mut count = 0; + let edges = BNGetBasicBlockIncomingEdges(self.handle, &mut count); + Array::new( + edges, + count, + EdgeContext { + dir: EdgeDirection::Incoming, + orig_block: self, + }, + ) + } + } + + pub fn outgoing_edges(&self) -> Array<Edge<C>> { + unsafe { + let mut count = 0; + let edges = BNGetBasicBlockOutgoingEdges(self.handle, &mut count); + Array::new( + edges, + count, + EdgeContext { + dir: EdgeDirection::Outgoing, + orig_block: self, + }, + ) + } + } + + // is this valid for il blocks? (it looks like up to MLIL it is) + pub fn has_undetermined_outgoing_edges(&self) -> bool { + unsafe { BNBasicBlockHasUndeterminedOutgoingEdges(self.handle) } + } + + pub fn can_exit(&self) -> bool { + unsafe { BNBasicBlockCanExit(self.handle) } + } + + // TODO: Should we new type this? I just cant tell where the consumers of this are. + pub fn index(&self) -> usize { + unsafe { BNGetBasicBlockIndex(self.handle) } + } + + pub fn immediate_dominator(&self) -> Option<Ref<Self>> { + unsafe { + // TODO: We don't allow the user to calculate post dominators + let block = BNGetBasicBlockImmediateDominator(self.handle, false); + if block.is_null() { + return None; + } + Some(Ref::new(BasicBlock::from_raw(block, self.context.clone()))) + } + } + + pub fn dominators(&self) -> Array<BasicBlock<C>> { + unsafe { + let mut count = 0; + // TODO: We don't allow the user to calculate post dominators + let blocks = BNGetBasicBlockDominators(self.handle, &mut count, false); + Array::new(blocks, count, self.context.clone()) + } + } + + pub fn strict_dominators(&self) -> Array<BasicBlock<C>> { + unsafe { + let mut count = 0; + // TODO: We don't allow the user to calculate post dominators + let blocks = BNGetBasicBlockStrictDominators(self.handle, &mut count, false); + Array::new(blocks, count, self.context.clone()) + } + } + + pub fn dominator_tree_children(&self) -> Array<BasicBlock<C>> { + unsafe { + let mut count = 0; + // TODO: We don't allow the user to calculate post dominators + let blocks = BNGetBasicBlockDominatorTreeChildren(self.handle, &mut count, false); + Array::new(blocks, count, self.context.clone()) + } + } + + pub fn dominance_frontier(&self) -> Array<BasicBlock<C>> { + unsafe { + let mut count = 0; + // TODO: We don't allow the user to calculate post dominators + let blocks = BNGetBasicBlockDominanceFrontier(self.handle, &mut count, false); + Array::new(blocks, count, self.context.clone()) + } + } + + // TODO iterated dominance frontier +} + +impl<C: BlockContext> IntoIterator for &BasicBlock<C> { + type Item = C::Instruction; + type IntoIter = C::Iter; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl<C: BlockContext> IntoIterator for BasicBlock<C> { + type Item = C::Instruction; + type IntoIter = C::Iter; + + fn into_iter(self) -> Self::IntoIter { + self.iter() + } +} + +impl<C: fmt::Debug + BlockContext> fmt::Debug for BasicBlock<C> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("BasicBlock") + .field("context", &self.context) + .field("start_index", &self.start_index()) + .field("end_index", &self.end_index()) + .field("raw_length", &self.raw_length()) + .finish() + } +} + +impl<C: BlockContext> ToOwned for BasicBlock<C> { + type Owned = Ref<Self>; + + fn to_owned(&self) -> Self::Owned { + unsafe { RefCountable::inc_ref(self) } + } +} + +unsafe impl<C: BlockContext> RefCountable for BasicBlock<C> { + unsafe fn inc_ref(handle: &Self) -> Ref<Self> { + Ref::new(Self { + handle: BNNewBasicBlockReference(handle.handle), + context: handle.context.clone(), + }) + } + + unsafe fn dec_ref(handle: &Self) { + BNFreeBasicBlock(handle.handle); + } +} + +impl<C: BlockContext> CoreArrayProvider for BasicBlock<C> { + type Raw = *mut BNBasicBlock; + type Context = C; + type Wrapped<'a> + = Guard<'a, BasicBlock<C>> + where + C: 'a; +} + +unsafe impl<C: BlockContext> CoreArrayProviderInner for BasicBlock<C> { + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { + BNFreeBasicBlockList(raw, count); + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { + Guard::new(BasicBlock::from_raw(*raw, context.clone()), context) + } +} + +unsafe impl<C: BlockContext> Send for BasicBlock<C> {} +unsafe impl<C: BlockContext> Sync for BasicBlock<C> {} |
