binaryninja/
basic_block.rs

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