binaryninja/
basic_block.rs

1// Copyright 2021-2025 Vector 35 Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::architecture::CoreArchitecture;
16use crate::function::Function;
17use crate::rc::*;
18use crate::BranchType;
19use binaryninjacore_sys::*;
20use std::fmt;
21use std::fmt::Debug;
22use std::hash::{Hash, Hasher};
23
24enum EdgeDirection {
25    Incoming,
26    Outgoing,
27}
28
29pub struct PendingBasicBlockEdge {
30    pub branch_type: BranchType,
31    pub target: u64,
32    pub arch: CoreArchitecture,
33    pub fallthrough: bool,
34}
35
36pub struct Edge<'a, C: 'a + BlockContext> {
37    pub branch: BranchType,
38    pub back_edge: bool,
39    pub source: Guard<'a, BasicBlock<C>>,
40    pub target: Guard<'a, BasicBlock<C>>,
41}
42
43impl<'a, C: 'a + fmt::Debug + BlockContext> fmt::Debug for Edge<'a, C> {
44    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
45        write!(
46            f,
47            "{:?} ({}) {:?} -> {:?}",
48            self.branch, self.back_edge, &*self.source, &*self.target
49        )
50    }
51}
52
53pub struct EdgeContext<'a, C: 'a + BlockContext> {
54    dir: EdgeDirection,
55    orig_block: &'a BasicBlock<C>,
56}
57
58impl<'a, C: 'a + BlockContext> CoreArrayProvider for Edge<'a, C> {
59    type Raw = BNBasicBlockEdge;
60    type Context = EdgeContext<'a, C>;
61    type Wrapped<'b>
62        = Edge<'b, C>
63    where
64        'a: 'b;
65}
66
67unsafe impl<'a, C: 'a + BlockContext> CoreArrayProviderInner for Edge<'a, C> {
68    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
69        BNFreeBasicBlockEdgeList(raw, count);
70    }
71
72    unsafe fn wrap_raw<'b>(raw: &'b Self::Raw, context: &'b Self::Context) -> Self::Wrapped<'b> {
73        let edge_target = Guard::new(
74            BasicBlock::from_raw(raw.target, context.orig_block.context.clone()),
75            raw,
76        );
77        let orig_block = Guard::new(
78            BasicBlock::from_raw(
79                context.orig_block.handle,
80                context.orig_block.context.clone(),
81            ),
82            raw,
83        );
84
85        let (source, target) = match context.dir {
86            EdgeDirection::Incoming => (edge_target, orig_block),
87            EdgeDirection::Outgoing => (orig_block, edge_target),
88        };
89
90        Edge {
91            branch: raw.type_,
92            back_edge: raw.backEdge,
93            source,
94            target,
95        }
96    }
97}
98
99pub trait BlockContext: Clone + Sync + Send + Sized {
100    type Instruction;
101    type InstructionIndex: Debug + From<u64>;
102    type Iter: Iterator<Item = Self::Instruction>;
103
104    fn start(&self, block: &BasicBlock<Self>) -> Self::Instruction;
105    fn iter(&self, block: &BasicBlock<Self>) -> Self::Iter;
106}
107
108#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, PartialOrd, Ord)]
109pub enum BasicBlockType {
110    Native,
111    LowLevelIL,
112    MediumLevelIL,
113    HighLevelIL,
114}
115
116pub struct BasicBlock<C: BlockContext> {
117    pub(crate) handle: *mut BNBasicBlock,
118    context: C,
119}
120
121impl<C: BlockContext> BasicBlock<C> {
122    pub unsafe fn from_raw(handle: *mut BNBasicBlock, context: C) -> Self {
123        Self { handle, context }
124    }
125
126    pub(crate) unsafe fn ref_from_raw(handle: *mut BNBasicBlock, context: C) -> Ref<Self> {
127        Ref::new(Self::from_raw(handle, context))
128    }
129
130    // TODO native bb vs il bbs
131    pub fn function(&self) -> Ref<Function> {
132        unsafe {
133            let func = BNGetBasicBlockFunction(self.handle);
134            Function::ref_from_raw(func)
135        }
136    }
137
138    pub fn arch(&self) -> CoreArchitecture {
139        unsafe {
140            let arch = BNGetBasicBlockArchitecture(self.handle);
141            CoreArchitecture::from_raw(arch)
142        }
143    }
144
145    pub fn block_type(&self) -> BasicBlockType {
146        if unsafe { !BNIsILBasicBlock(self.handle) } {
147            BasicBlockType::Native
148        } else if unsafe { BNIsLowLevelILBasicBlock(self.handle) } {
149            BasicBlockType::LowLevelIL
150        } else if unsafe { BNIsMediumLevelILBasicBlock(self.handle) } {
151            BasicBlockType::MediumLevelIL
152        } else {
153            // We checked all other IL levels, so this is safe.
154            BasicBlockType::HighLevelIL
155        }
156    }
157
158    pub fn iter(&self) -> C::Iter {
159        self.context.iter(self)
160    }
161
162    pub fn start_index(&self) -> C::InstructionIndex {
163        C::InstructionIndex::from(unsafe { BNGetBasicBlockStart(self.handle) })
164    }
165
166    pub fn end_index(&self) -> C::InstructionIndex {
167        C::InstructionIndex::from(unsafe { BNGetBasicBlockEnd(self.handle) })
168    }
169
170    pub fn start(&self) -> u64 {
171        unsafe { BNGetBasicBlockStart(self.handle) }
172    }
173
174    pub fn end(&self) -> u64 {
175        unsafe { BNGetBasicBlockEnd(self.handle) }
176    }
177
178    pub fn set_end(&self, end: u64) {
179        unsafe {
180            BNSetBasicBlockEnd(self.handle, end);
181        }
182    }
183
184    pub fn add_instruction_data(&self, data: &[u8]) {
185        unsafe {
186            BNBasicBlockAddInstructionData(self.handle, data.as_ptr() as *const _, data.len());
187        }
188    }
189
190    pub fn instruction_data(&self, addr: u64) -> &[u8] {
191        unsafe {
192            let mut size: usize = 0;
193            let data = BNBasicBlockGetInstructionData(self.handle, addr, &mut size);
194            if data.is_null() {
195                return &[];
196            }
197
198            std::slice::from_raw_parts(data, size)
199        }
200    }
201
202    pub fn set_has_invalid_instructions(&self, value: bool) {
203        unsafe {
204            BNBasicBlockSetHasInvalidInstructions(self.handle, value);
205        }
206    }
207
208    pub fn has_invalid_instructions(&self) -> bool {
209        unsafe { BNBasicBlockHasInvalidInstructions(self.handle) }
210    }
211
212    pub fn raw_length(&self) -> u64 {
213        unsafe { BNGetBasicBlockLength(self.handle) }
214    }
215
216    pub fn incoming_edges(&self) -> Array<Edge<'_, C>> {
217        unsafe {
218            let mut count = 0;
219            let edges = BNGetBasicBlockIncomingEdges(self.handle, &mut count);
220            Array::new(
221                edges,
222                count,
223                EdgeContext {
224                    dir: EdgeDirection::Incoming,
225                    orig_block: self,
226                },
227            )
228        }
229    }
230
231    pub fn outgoing_edges(&self) -> Array<Edge<'_, C>> {
232        unsafe {
233            let mut count = 0;
234            let edges = BNGetBasicBlockOutgoingEdges(self.handle, &mut count);
235            Array::new(
236                edges,
237                count,
238                EdgeContext {
239                    dir: EdgeDirection::Outgoing,
240                    orig_block: self,
241                },
242            )
243        }
244    }
245
246    pub fn pending_outgoing_edges(&self) -> Vec<PendingBasicBlockEdge> {
247        unsafe {
248            let mut count = 0;
249            let edges_ptr = BNGetBasicBlockPendingOutgoingEdges(self.handle, &mut count);
250            let edges = std::slice::from_raw_parts(edges_ptr, count);
251
252            let mut result = Vec::with_capacity(count);
253            for edge in edges {
254                result.push(PendingBasicBlockEdge {
255                    branch_type: edge.type_,
256                    target: edge.target,
257                    arch: CoreArchitecture::from_raw(edge.arch),
258                    fallthrough: edge.fallThrough,
259                });
260            }
261
262            BNFreePendingBasicBlockEdgeList(edges_ptr);
263            result
264        }
265    }
266
267    pub fn add_pending_outgoing_edge(
268        &self,
269        typ: BranchType,
270        addr: u64,
271        arch: CoreArchitecture,
272        fallthrough: bool,
273    ) {
274        unsafe {
275            BNBasicBlockAddPendingOutgoingEdge(self.handle, typ, addr, arch.handle, fallthrough);
276        }
277    }
278
279    pub fn clear_pending_outgoing_edges(&self) {
280        unsafe {
281            BNClearBasicBlockPendingOutgoingEdges(self.handle);
282        }
283    }
284
285    pub fn set_fallthrough_to_function(&self, value: bool) {
286        unsafe {
287            BNBasicBlockSetFallThroughToFunction(self.handle, value);
288        }
289    }
290
291    pub fn is_fallthrough_to_function(&self) -> bool {
292        unsafe { BNBasicBlockIsFallThroughToFunction(self.handle) }
293    }
294
295    // is this valid for il blocks? (it looks like up to MLIL it is)
296    pub fn has_undetermined_outgoing_edges(&self) -> bool {
297        unsafe { BNBasicBlockHasUndeterminedOutgoingEdges(self.handle) }
298    }
299
300    pub fn set_undetermined_outgoing_edges(&self, value: bool) {
301        unsafe {
302            BNBasicBlockSetUndeterminedOutgoingEdges(self.handle, value);
303        }
304    }
305
306    pub fn can_exit(&self) -> bool {
307        unsafe { BNBasicBlockCanExit(self.handle) }
308    }
309
310    pub fn set_can_exit(&self, value: bool) {
311        unsafe {
312            BNBasicBlockSetCanExit(self.handle, value);
313        }
314    }
315
316    // TODO: Should we new type this? I just cant tell where the consumers of this are.
317    pub fn index(&self) -> usize {
318        unsafe { BNGetBasicBlockIndex(self.handle) }
319    }
320
321    pub fn immediate_dominator(&self) -> Option<Ref<Self>> {
322        unsafe {
323            // TODO: We don't allow the user to calculate post dominators
324            let block = BNGetBasicBlockImmediateDominator(self.handle, false);
325            if block.is_null() {
326                return None;
327            }
328            Some(BasicBlock::ref_from_raw(block, self.context.clone()))
329        }
330    }
331
332    pub fn dominators(&self) -> Array<BasicBlock<C>> {
333        unsafe {
334            let mut count = 0;
335            // TODO: We don't allow the user to calculate post dominators
336            let blocks = BNGetBasicBlockDominators(self.handle, &mut count, false);
337            Array::new(blocks, count, self.context.clone())
338        }
339    }
340
341    pub fn strict_dominators(&self) -> Array<BasicBlock<C>> {
342        unsafe {
343            let mut count = 0;
344            // TODO: We don't allow the user to calculate post dominators
345            let blocks = BNGetBasicBlockStrictDominators(self.handle, &mut count, false);
346            Array::new(blocks, count, self.context.clone())
347        }
348    }
349
350    pub fn dominator_tree_children(&self) -> Array<BasicBlock<C>> {
351        unsafe {
352            let mut count = 0;
353            // TODO: We don't allow the user to calculate post dominators
354            let blocks = BNGetBasicBlockDominatorTreeChildren(self.handle, &mut count, false);
355            Array::new(blocks, count, self.context.clone())
356        }
357    }
358
359    pub fn dominance_frontier(&self) -> Array<BasicBlock<C>> {
360        unsafe {
361            let mut count = 0;
362            // TODO: We don't allow the user to calculate post dominators
363            let blocks = BNGetBasicBlockDominanceFrontier(self.handle, &mut count, false);
364            Array::new(blocks, count, self.context.clone())
365        }
366    }
367
368    // TODO iterated dominance frontier
369}
370
371impl<C: BlockContext> Hash for BasicBlock<C> {
372    fn hash<H: Hasher>(&self, state: &mut H) {
373        self.function().hash(state);
374        self.block_type().hash(state);
375        state.write_usize(self.index());
376    }
377}
378
379impl<C: BlockContext> PartialEq for BasicBlock<C> {
380    fn eq(&self, other: &Self) -> bool {
381        self.function() == other.function()
382            && self.index() == other.index()
383            && self.block_type() == other.block_type()
384    }
385}
386
387impl<C: BlockContext> Eq for BasicBlock<C> {}
388
389impl<C: BlockContext> IntoIterator for &BasicBlock<C> {
390    type Item = C::Instruction;
391    type IntoIter = C::Iter;
392
393    fn into_iter(self) -> Self::IntoIter {
394        self.iter()
395    }
396}
397
398impl<C: BlockContext> IntoIterator for BasicBlock<C> {
399    type Item = C::Instruction;
400    type IntoIter = C::Iter;
401
402    fn into_iter(self) -> Self::IntoIter {
403        self.iter()
404    }
405}
406
407impl<C: fmt::Debug + BlockContext> fmt::Debug for BasicBlock<C> {
408    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
409        f.debug_struct("BasicBlock")
410            .field("context", &self.context)
411            .field("start_index", &self.start_index())
412            .field("end_index", &self.end_index())
413            .field("raw_length", &self.raw_length())
414            .finish()
415    }
416}
417
418impl<C: BlockContext> ToOwned for BasicBlock<C> {
419    type Owned = Ref<Self>;
420
421    fn to_owned(&self) -> Self::Owned {
422        unsafe { RefCountable::inc_ref(self) }
423    }
424}
425
426unsafe impl<C: BlockContext> RefCountable for BasicBlock<C> {
427    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
428        Ref::new(Self {
429            handle: BNNewBasicBlockReference(handle.handle),
430            context: handle.context.clone(),
431        })
432    }
433
434    unsafe fn dec_ref(handle: &Self) {
435        BNFreeBasicBlock(handle.handle);
436    }
437}
438
439impl<C: BlockContext> CoreArrayProvider for BasicBlock<C> {
440    type Raw = *mut BNBasicBlock;
441    type Context = C;
442    type Wrapped<'a>
443        = Guard<'a, BasicBlock<C>>
444    where
445        C: 'a;
446}
447
448unsafe impl<C: BlockContext> CoreArrayProviderInner for BasicBlock<C> {
449    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
450        BNFreeBasicBlockList(raw, count);
451    }
452
453    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
454        Guard::new(BasicBlock::from_raw(*raw, context.clone()), context)
455    }
456}
457
458unsafe impl<C: BlockContext> Send for BasicBlock<C> {}
459unsafe impl<C: BlockContext> Sync for BasicBlock<C> {}