binaryninja/architecture/
basic_block.rs

1use crate::architecture::{ArchitectureWithFunctionContext, CoreArchitecture, IndirectBranchInfo};
2use crate::basic_block::BasicBlock;
3use crate::function::{Function, Location, NativeBlock};
4use crate::rc::Ref;
5use binaryninjacore_sys::*;
6use std::collections::{HashMap, HashSet};
7use std::fmt::Debug;
8
9pub struct BasicBlockAnalysisContext {
10    pub(crate) handle: *mut BNBasicBlockAnalysisContext,
11    contextual_returns_dirty: bool,
12
13    // In
14    pub indirect_branches: Vec<IndirectBranchInfo>,
15    pub indirect_no_return_calls: HashSet<Location>,
16    pub analysis_skip_override: BNFunctionAnalysisSkipOverride,
17    pub guided_analysis_mode: bool,
18    pub trigger_guided_on_invalid_instruction: bool,
19    pub translate_tail_calls: bool,
20    pub disallow_branch_to_string: bool,
21    pub max_function_size: u64,
22
23    // In/Out
24    pub max_size_reached: bool,
25    contextual_returns: HashMap<Location, bool>,
26
27    // Out
28    direct_code_references: HashMap<u64, Location>,
29    direct_no_return_calls: HashSet<Location>,
30    halted_disassembly_addresses: HashSet<Location>,
31    inlined_unresolved_indirect_branches: HashSet<Location>,
32}
33
34impl BasicBlockAnalysisContext {
35    pub unsafe fn from_raw(handle: *mut BNBasicBlockAnalysisContext) -> Self {
36        debug_assert!(!handle.is_null());
37
38        let ctx_ref = &*handle;
39
40        let raw_indirect_branches: &[BNIndirectBranchInfo] =
41            std::slice::from_raw_parts(ctx_ref.indirectBranches, ctx_ref.indirectBranchesCount);
42        let indirect_branches: Vec<IndirectBranchInfo> = raw_indirect_branches
43            .iter()
44            .map(IndirectBranchInfo::from)
45            .collect();
46
47        let raw_indirect_no_return_calls: &[BNArchitectureAndAddress] = std::slice::from_raw_parts(
48            ctx_ref.indirectNoReturnCalls,
49            ctx_ref.indirectNoReturnCallsCount,
50        );
51        let indirect_no_return_calls: HashSet<Location> = raw_indirect_no_return_calls
52            .iter()
53            .map(Location::from)
54            .collect();
55
56        let raw_contextual_return_locs: &[BNArchitectureAndAddress] = unsafe {
57            std::slice::from_raw_parts(
58                ctx_ref.contextualFunctionReturnLocations,
59                ctx_ref.contextualFunctionReturnCount,
60            )
61        };
62        let raw_contextual_return_vals: &[bool] = unsafe {
63            std::slice::from_raw_parts(
64                ctx_ref.contextualFunctionReturnValues,
65                ctx_ref.contextualFunctionReturnCount,
66            )
67        };
68        let contextual_returns: HashMap<Location, bool> = raw_contextual_return_locs
69            .iter()
70            .map(Location::from)
71            .zip(raw_contextual_return_vals.iter().copied())
72            .collect();
73
74        // The lists below this are out params and are possibly not initialized.
75        let raw_direct_ref_sources: &[BNArchitectureAndAddress] = match ctx_ref
76            .directRefSources
77            .is_null()
78        {
79            true => &[],
80            false => std::slice::from_raw_parts(ctx_ref.directRefSources, ctx_ref.directRefCount),
81        };
82        let raw_direct_ref_targets: &[u64] = match ctx_ref.directRefTargets.is_null() {
83            true => &[],
84            false => std::slice::from_raw_parts(ctx_ref.directRefTargets, ctx_ref.directRefCount),
85        };
86        let direct_code_references: HashMap<u64, Location> = raw_direct_ref_targets
87            .iter()
88            .copied()
89            .zip(raw_direct_ref_sources.iter().map(Location::from))
90            .collect();
91
92        let raw_direct_no_return_calls: &[BNArchitectureAndAddress] =
93            match ctx_ref.directNoReturnCalls.is_null() {
94                true => &[],
95                false => std::slice::from_raw_parts(
96                    ctx_ref.directNoReturnCalls,
97                    ctx_ref.directNoReturnCallsCount,
98                ),
99            };
100        let direct_no_return_calls: HashSet<Location> = raw_direct_no_return_calls
101            .iter()
102            .map(Location::from)
103            .collect();
104
105        let raw_halted_disassembly_address: &[BNArchitectureAndAddress] =
106            match ctx_ref.haltedDisassemblyAddresses.is_null() {
107                true => &[],
108                false => std::slice::from_raw_parts(
109                    ctx_ref.haltedDisassemblyAddresses,
110                    ctx_ref.haltedDisassemblyAddressesCount,
111                ),
112            };
113        let halted_disassembly_addresses: HashSet<Location> = raw_halted_disassembly_address
114            .iter()
115            .map(Location::from)
116            .collect();
117
118        let raw_inlined_unresolved_indirect_branches: &[BNArchitectureAndAddress] =
119            match ctx_ref.inlinedUnresolvedIndirectBranches.is_null() {
120                true => &[],
121                false => std::slice::from_raw_parts(
122                    ctx_ref.inlinedUnresolvedIndirectBranches,
123                    ctx_ref.inlinedUnresolvedIndirectBranchCount,
124                ),
125            };
126        let inlined_unresolved_indirect_branches: HashSet<Location> =
127            raw_inlined_unresolved_indirect_branches
128                .iter()
129                .map(Location::from)
130                .collect();
131
132        BasicBlockAnalysisContext {
133            handle,
134            contextual_returns_dirty: false,
135            indirect_branches,
136            indirect_no_return_calls,
137            analysis_skip_override: ctx_ref.analysisSkipOverride,
138            guided_analysis_mode: ctx_ref.guidedAnalysisMode,
139            trigger_guided_on_invalid_instruction: ctx_ref.triggerGuidedOnInvalidInstruction,
140            translate_tail_calls: ctx_ref.translateTailCalls,
141            disallow_branch_to_string: ctx_ref.disallowBranchToString,
142            max_function_size: ctx_ref.maxFunctionSize,
143            max_size_reached: ctx_ref.maxSizeReached,
144            contextual_returns,
145            direct_code_references,
146            direct_no_return_calls,
147            halted_disassembly_addresses,
148            inlined_unresolved_indirect_branches,
149        }
150    }
151
152    /// Adds a contextual function return location and its value to the current function.
153    pub fn add_contextual_return(&mut self, loc: impl Into<Location>, value: bool) {
154        let loc = loc.into();
155        if !self.contextual_returns.contains_key(&loc) {
156            self.contextual_returns_dirty = true;
157        }
158
159        self.contextual_returns.insert(loc, value);
160    }
161
162    /// Adds a direct code reference to the current function.
163    pub fn add_direct_code_reference(&mut self, target: u64, src: impl Into<Location>) {
164        self.direct_code_references
165            .entry(target)
166            .or_insert(src.into());
167    }
168
169    /// Adds a direct no-return call location to the current function.
170    pub fn add_direct_no_return_call(&mut self, loc: impl Into<Location>) {
171        self.direct_no_return_calls.insert(loc.into());
172    }
173
174    /// Adds an address to the set of halted disassembly addresses.
175    pub fn add_halted_disassembly_address(&mut self, loc: impl Into<Location>) {
176        self.halted_disassembly_addresses.insert(loc.into());
177    }
178
179    pub fn add_inlined_unresolved_indirect_branch(&mut self, loc: impl Into<Location>) {
180        self.inlined_unresolved_indirect_branches.insert(loc.into());
181    }
182
183    pub fn set_function_arch_context<A: ArchitectureWithFunctionContext>(
184        &mut self,
185        _arch: &A,
186        context: Box<A::FunctionArchContext>,
187    ) -> bool {
188        unsafe {
189            if !(*self.handle).functionArchContext.is_null() {
190                return false;
191            }
192            (*self.handle).functionArchContext = Box::into_raw(context) as *mut std::ffi::c_void;
193        }
194        true
195    }
196
197    pub fn get_function_arch_context<A: ArchitectureWithFunctionContext>(
198        &self,
199        _arch: &A,
200    ) -> Option<&A::FunctionArchContext> {
201        unsafe {
202            let ptr = (*self.handle).functionArchContext;
203            if ptr.is_null() {
204                None
205            } else {
206                Some(&*(ptr as *const A::FunctionArchContext))
207            }
208        }
209    }
210
211    /// Creates a new [`BasicBlock`] at the specified address for the given [`CoreArchitecture`].
212    ///
213    /// After creating, you can add using [`BasicBlockAnalysisContext::add_basic_block`].
214    pub fn create_basic_block(
215        &self,
216        arch: CoreArchitecture,
217        start: u64,
218    ) -> Option<Ref<BasicBlock<NativeBlock>>> {
219        let raw_block =
220            unsafe { BNAnalyzeBasicBlocksContextCreateBasicBlock(self.handle, arch.handle, start) };
221
222        if raw_block.is_null() {
223            return None;
224        }
225
226        unsafe { Some(BasicBlock::ref_from_raw(raw_block, NativeBlock::new())) }
227    }
228
229    /// Adds a [`BasicBlock`] to the current function.
230    ///
231    /// You can create a [`BasicBlock`] via [`BasicBlockAnalysisContext::create_basic_block`].
232    pub fn add_basic_block(&self, block: Ref<BasicBlock<NativeBlock>>) {
233        unsafe {
234            BNAnalyzeBasicBlocksContextAddBasicBlockToFunction(self.handle, block.handle);
235        }
236    }
237
238    /// Adds a temporary outgoing reference to the specified function.
239    pub fn add_temp_outgoing_reference(&self, target: &Function) {
240        unsafe {
241            BNAnalyzeBasicBlocksContextAddTempReference(self.handle, target.handle);
242        }
243    }
244
245    /// To be called before finalizing the basic block analysis.
246    fn update_direct_code_references(&mut self) {
247        let total = self.direct_code_references.len();
248        let mut sources: Vec<BNArchitectureAndAddress> = Vec::with_capacity(total);
249        let mut targets: Vec<u64> = Vec::with_capacity(total);
250        for (target, src) in &self.direct_code_references {
251            sources.push(src.into());
252            targets.push(*target);
253        }
254        unsafe {
255            BNAnalyzeBasicBlocksContextSetDirectCodeReferences(
256                self.handle,
257                sources.as_mut_ptr(),
258                targets.as_mut_ptr(),
259                total,
260            );
261        }
262    }
263
264    /// To be called before finalizing the basic block analysis.
265    fn update_direct_no_return_calls(&mut self) {
266        let total = self.direct_no_return_calls.len();
267        let mut raw_locations: Vec<_> = self
268            .direct_no_return_calls
269            .iter()
270            .map(BNArchitectureAndAddress::from)
271            .collect();
272        unsafe {
273            BNAnalyzeBasicBlocksContextSetDirectNoReturnCalls(
274                self.handle,
275                raw_locations.as_mut_ptr(),
276                total,
277            );
278        }
279    }
280
281    /// To be called before finalizing the basic block analysis.
282    fn update_inlined_unresolved_indirect_branches(&mut self) {
283        let total = self.inlined_unresolved_indirect_branches.len();
284        let mut raw_locations: Vec<_> = self
285            .inlined_unresolved_indirect_branches
286            .iter()
287            .map(BNArchitectureAndAddress::from)
288            .collect();
289        unsafe {
290            BNAnalyzeBasicBlocksContextSetInlinedUnresolvedIndirectBranches(
291                self.handle,
292                raw_locations.as_mut_ptr(),
293                total,
294            );
295        }
296    }
297
298    /// To be called before finalizing the basic block analysis.
299    fn update_halted_disassembly_addresses(&mut self) {
300        let total = self.halted_disassembly_addresses.len();
301        let mut raw_locations: Vec<_> = self
302            .halted_disassembly_addresses
303            .iter()
304            .map(BNArchitectureAndAddress::from)
305            .collect();
306        unsafe {
307            BNAnalyzeBasicBlocksContextSetHaltedDisassemblyAddresses(
308                self.handle,
309                raw_locations.as_mut_ptr(),
310                total,
311            );
312        }
313    }
314
315    /// To be called before finalizing the basic block analysis.
316    fn update_contextual_returns(&mut self) {
317        let total = self.contextual_returns.len();
318        let mut locations: Vec<BNArchitectureAndAddress> = Vec::with_capacity(total);
319        let mut values: Vec<bool> = Vec::with_capacity(total);
320        for (loc, value) in &self.contextual_returns {
321            locations.push(loc.into());
322            values.push(*value);
323        }
324        unsafe {
325            BNAnalyzeBasicBlocksContextSetContextualFunctionReturns(
326                self.handle,
327                locations.as_mut_ptr(),
328                values.as_mut_ptr(),
329                total,
330            );
331        }
332    }
333
334    /// Finalizes the function's basic block analysis.
335    pub fn finalize(&mut self) {
336        if !self.direct_code_references.is_empty() {
337            self.update_direct_code_references();
338        }
339
340        if !self.direct_no_return_calls.is_empty() {
341            self.update_direct_no_return_calls();
342        }
343
344        if !self.halted_disassembly_addresses.is_empty() {
345            self.update_halted_disassembly_addresses();
346        }
347
348        if !self.inlined_unresolved_indirect_branches.is_empty() {
349            self.update_inlined_unresolved_indirect_branches();
350        }
351
352        unsafe {
353            (*self.handle).maxSizeReached = self.max_size_reached;
354        }
355
356        if self.contextual_returns_dirty {
357            self.update_contextual_returns();
358        }
359    }
360}
361
362impl Debug for BasicBlockAnalysisContext {
363    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
364        f.debug_struct("BasicBlockAnalysisContext")
365            .field("indirect_branches", &self.indirect_branches)
366            .field("indirect_no_return_calls", &self.indirect_no_return_calls)
367            .field("analysis_skip_override", &self.analysis_skip_override)
368            .field("translate_tail_calls", &self.translate_tail_calls)
369            .field("disallow_branch_to_string", &self.disallow_branch_to_string)
370            .field("max_function_size", &self.max_function_size)
371            .field("guided_analysis_mode", &self.guided_analysis_mode)
372            .field(
373                "trigger_guided_on_invalid_instruction",
374                &self.trigger_guided_on_invalid_instruction,
375            )
376            .field("max_size_reached", &self.max_size_reached)
377            .field("contextual_returns", &self.contextual_returns)
378            .field("direct_code_references", &self.direct_code_references)
379            .field("direct_no_return_calls", &self.direct_no_return_calls)
380            .field(
381                "halted_disassembly_addresses",
382                &self.halted_disassembly_addresses,
383            )
384            .finish()
385    }
386}