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 add_instruction_data(&self, data: &[u8]) {
231        unsafe {
232            BNBasicBlockAddInstructionData(self.handle, data.as_ptr() as *const _, data.len());
233        }
234    }
235
236    pub fn instruction_data(&self, addr: u64) -> &[u8] {
237        unsafe {
238            let mut size: usize = 0;
239            let data = BNBasicBlockGetInstructionData(self.handle, addr, &mut size);
240            if data.is_null() {
241                return &[];
242            }
243
244            std::slice::from_raw_parts(data, size)
245        }
246    }
247
248    pub fn set_has_invalid_instructions(&self, value: bool) {
249        unsafe {
250            BNBasicBlockSetHasInvalidInstructions(self.handle, value);
251        }
252    }
253
254    pub fn has_invalid_instructions(&self) -> bool {
255        unsafe { BNBasicBlockHasInvalidInstructions(self.handle) }
256    }
257
258    pub fn raw_length(&self) -> u64 {
259        unsafe { BNGetBasicBlockLength(self.handle) }
260    }
261
262    pub fn incoming_edges(&self) -> Array<Edge<'_, C>> {
263        unsafe {
264            let mut count = 0;
265            let edges = BNGetBasicBlockIncomingEdges(self.handle, &mut count);
266            Array::new(
267                edges,
268                count,
269                EdgeContext {
270                    dir: EdgeDirection::Incoming,
271                    orig_block: self,
272                },
273            )
274        }
275    }
276
277    pub fn outgoing_edges(&self) -> Array<Edge<'_, C>> {
278        unsafe {
279            let mut count = 0;
280            let edges = BNGetBasicBlockOutgoingEdges(self.handle, &mut count);
281            Array::new(
282                edges,
283                count,
284                EdgeContext {
285                    dir: EdgeDirection::Outgoing,
286                    orig_block: self,
287                },
288            )
289        }
290    }
291
292    /// Pending outgoing edges for the basic block. These are edges that have not yet been resolved.
293    pub fn pending_outgoing_edges(&self) -> Array<PendingBasicBlockEdge> {
294        unsafe {
295            let mut count = 0;
296            let edges_ptr = BNGetBasicBlockPendingOutgoingEdges(self.handle, &mut count);
297            Array::new(edges_ptr, count, ())
298        }
299    }
300
301    pub fn add_pending_outgoing_edge(&self, edge: &PendingBasicBlockEdge) {
302        unsafe {
303            BNBasicBlockAddPendingOutgoingEdge(
304                self.handle,
305                edge.branch_type,
306                edge.target,
307                edge.arch.handle,
308                edge.fallthrough,
309            );
310        }
311    }
312
313    pub fn clear_pending_outgoing_edges(&self) {
314        unsafe {
315            BNClearBasicBlockPendingOutgoingEdges(self.handle);
316        }
317    }
318
319    pub fn set_fallthrough_to_function(&self, value: bool) {
320        unsafe {
321            BNBasicBlockSetFallThroughToFunction(self.handle, value);
322        }
323    }
324
325    pub fn is_fallthrough_to_function(&self) -> bool {
326        unsafe { BNBasicBlockIsFallThroughToFunction(self.handle) }
327    }
328
329    // is this valid for il blocks? (it looks like up to MLIL it is)
330    pub fn has_undetermined_outgoing_edges(&self) -> bool {
331        unsafe { BNBasicBlockHasUndeterminedOutgoingEdges(self.handle) }
332    }
333
334    pub fn set_undetermined_outgoing_edges(&self, value: bool) {
335        unsafe {
336            BNBasicBlockSetUndeterminedOutgoingEdges(self.handle, value);
337        }
338    }
339
340    pub fn can_exit(&self) -> bool {
341        unsafe { BNBasicBlockCanExit(self.handle) }
342    }
343
344    pub fn set_can_exit(&self, value: bool) {
345        unsafe {
346            BNBasicBlockSetCanExit(self.handle, value);
347        }
348    }
349
350    // TODO: Should we new type this? I just cant tell where the consumers of this are.
351    pub fn index(&self) -> usize {
352        unsafe { BNGetBasicBlockIndex(self.handle) }
353    }
354
355    pub fn immediate_dominator(&self) -> Option<Ref<Self>> {
356        unsafe {
357            // TODO: We don't allow the user to calculate post dominators
358            let block = BNGetBasicBlockImmediateDominator(self.handle, false);
359            if block.is_null() {
360                return None;
361            }
362            Some(BasicBlock::ref_from_raw(block, self.context.clone()))
363        }
364    }
365
366    pub fn dominators(&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 = BNGetBasicBlockDominators(self.handle, &mut count, false);
371            Array::new(blocks, count, self.context.clone())
372        }
373    }
374
375    pub fn strict_dominators(&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 = BNGetBasicBlockStrictDominators(self.handle, &mut count, false);
380            Array::new(blocks, count, self.context.clone())
381        }
382    }
383
384    pub fn dominator_tree_children(&self) -> Array<BasicBlock<C>> {
385        unsafe {
386            let mut count = 0;
387            // TODO: We don't allow the user to calculate post dominators
388            let blocks = BNGetBasicBlockDominatorTreeChildren(self.handle, &mut count, false);
389            Array::new(blocks, count, self.context.clone())
390        }
391    }
392
393    pub fn dominance_frontier(&self) -> Array<BasicBlock<C>> {
394        unsafe {
395            let mut count = 0;
396            // TODO: We don't allow the user to calculate post dominators
397            let blocks = BNGetBasicBlockDominanceFrontier(self.handle, &mut count, false);
398            Array::new(blocks, count, self.context.clone())
399        }
400    }
401
402    // TODO iterated dominance frontier
403}
404
405impl<C: BlockContext> Hash for BasicBlock<C> {
406    fn hash<H: Hasher>(&self, state: &mut H) {
407        self.function().hash(state);
408        self.block_type().hash(state);
409        state.write_usize(self.index());
410    }
411}
412
413impl<C: BlockContext> PartialEq for BasicBlock<C> {
414    fn eq(&self, other: &Self) -> bool {
415        self.function() == other.function()
416            && self.index() == other.index()
417            && self.block_type() == other.block_type()
418    }
419}
420
421impl<C: BlockContext> Eq for BasicBlock<C> {}
422
423impl<C: BlockContext> IntoIterator for &BasicBlock<C> {
424    type Item = C::Instruction;
425    type IntoIter = C::Iter;
426
427    fn into_iter(self) -> Self::IntoIter {
428        self.iter()
429    }
430}
431
432impl<C: BlockContext> IntoIterator for BasicBlock<C> {
433    type Item = C::Instruction;
434    type IntoIter = C::Iter;
435
436    fn into_iter(self) -> Self::IntoIter {
437        self.iter()
438    }
439}
440
441impl<C: fmt::Debug + BlockContext> fmt::Debug for BasicBlock<C> {
442    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
443        f.debug_struct("BasicBlock")
444            .field("context", &self.context)
445            .field("start_index", &self.start_index())
446            .field("end_index", &self.end_index())
447            .field("raw_length", &self.raw_length())
448            .finish()
449    }
450}
451
452impl<C: BlockContext> ToOwned for BasicBlock<C> {
453    type Owned = Ref<Self>;
454
455    fn to_owned(&self) -> Self::Owned {
456        unsafe { RefCountable::inc_ref(self) }
457    }
458}
459
460unsafe impl<C: BlockContext> RefCountable for BasicBlock<C> {
461    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
462        Ref::new(Self {
463            handle: BNNewBasicBlockReference(handle.handle),
464            context: handle.context.clone(),
465        })
466    }
467
468    unsafe fn dec_ref(handle: &Self) {
469        BNFreeBasicBlock(handle.handle);
470    }
471}
472
473impl<C: BlockContext> CoreArrayProvider for BasicBlock<C> {
474    type Raw = *mut BNBasicBlock;
475    type Context = C;
476    type Wrapped<'a>
477        = Guard<'a, BasicBlock<C>>
478    where
479        C: 'a;
480}
481
482unsafe impl<C: BlockContext> CoreArrayProviderInner for BasicBlock<C> {
483    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
484        BNFreeBasicBlockList(raw, count);
485    }
486
487    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
488        Guard::new(BasicBlock::from_raw(*raw, context.clone()), context)
489    }
490}
491
492unsafe impl<C: BlockContext> Send for BasicBlock<C> {}
493unsafe impl<C: BlockContext> Sync for BasicBlock<C> {}