binaryninja/architecture/
basic_block.rs

1use crate::architecture::{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    /// Creates a new [`BasicBlock`] at the specified address for the given [`CoreArchitecture`].
184    ///
185    /// After creating, you can add using [`BasicBlockAnalysisContext::add_basic_block`].
186    pub fn create_basic_block(
187        &self,
188        arch: CoreArchitecture,
189        start: u64,
190    ) -> Option<Ref<BasicBlock<NativeBlock>>> {
191        let raw_block =
192            unsafe { BNAnalyzeBasicBlocksContextCreateBasicBlock(self.handle, arch.handle, start) };
193
194        if raw_block.is_null() {
195            return None;
196        }
197
198        unsafe { Some(BasicBlock::ref_from_raw(raw_block, NativeBlock::new())) }
199    }
200
201    /// Adds a [`BasicBlock`] to the current function.
202    ///
203    /// You can create a [`BasicBlock`] via [`BasicBlockAnalysisContext::create_basic_block`].
204    pub fn add_basic_block(&self, block: Ref<BasicBlock<NativeBlock>>) {
205        unsafe {
206            BNAnalyzeBasicBlocksContextAddBasicBlockToFunction(self.handle, block.handle);
207        }
208    }
209
210    /// Adds a temporary outgoing reference to the specified function.
211    pub fn add_temp_outgoing_reference(&self, target: &Function) {
212        unsafe {
213            BNAnalyzeBasicBlocksContextAddTempReference(self.handle, target.handle);
214        }
215    }
216
217    /// To be called before finalizing the basic block analysis.
218    fn update_direct_code_references(&mut self) {
219        let total = self.direct_code_references.len();
220        let mut sources: Vec<BNArchitectureAndAddress> = Vec::with_capacity(total);
221        let mut targets: Vec<u64> = Vec::with_capacity(total);
222        for (target, src) in &self.direct_code_references {
223            sources.push(src.into());
224            targets.push(*target);
225        }
226        unsafe {
227            BNAnalyzeBasicBlocksContextSetDirectCodeReferences(
228                self.handle,
229                sources.as_mut_ptr(),
230                targets.as_mut_ptr(),
231                total,
232            );
233        }
234    }
235
236    /// To be called before finalizing the basic block analysis.
237    fn update_direct_no_return_calls(&mut self) {
238        let total = self.direct_no_return_calls.len();
239        let mut raw_locations: Vec<_> = self
240            .direct_no_return_calls
241            .iter()
242            .map(BNArchitectureAndAddress::from)
243            .collect();
244        unsafe {
245            BNAnalyzeBasicBlocksContextSetDirectNoReturnCalls(
246                self.handle,
247                raw_locations.as_mut_ptr(),
248                total,
249            );
250        }
251    }
252
253    /// To be called before finalizing the basic block analysis.
254    fn update_inlined_unresolved_indirect_branches(&mut self) {
255        let total = self.inlined_unresolved_indirect_branches.len();
256        let mut raw_locations: Vec<_> = self
257            .inlined_unresolved_indirect_branches
258            .iter()
259            .map(BNArchitectureAndAddress::from)
260            .collect();
261        unsafe {
262            BNAnalyzeBasicBlocksContextSetInlinedUnresolvedIndirectBranches(
263                self.handle,
264                raw_locations.as_mut_ptr(),
265                total,
266            );
267        }
268    }
269
270    /// To be called before finalizing the basic block analysis.
271    fn update_halted_disassembly_addresses(&mut self) {
272        let total = self.halted_disassembly_addresses.len();
273        let mut raw_locations: Vec<_> = self
274            .halted_disassembly_addresses
275            .iter()
276            .map(BNArchitectureAndAddress::from)
277            .collect();
278        unsafe {
279            BNAnalyzeBasicBlocksContextSetHaltedDisassemblyAddresses(
280                self.handle,
281                raw_locations.as_mut_ptr(),
282                total,
283            );
284        }
285    }
286
287    /// To be called before finalizing the basic block analysis.
288    fn update_contextual_returns(&mut self) {
289        let total = self.contextual_returns.len();
290        let mut locations: Vec<BNArchitectureAndAddress> = Vec::with_capacity(total);
291        let mut values: Vec<bool> = Vec::with_capacity(total);
292        for (loc, value) in &self.contextual_returns {
293            locations.push(loc.into());
294            values.push(*value);
295        }
296        unsafe {
297            BNAnalyzeBasicBlocksContextSetContextualFunctionReturns(
298                self.handle,
299                locations.as_mut_ptr(),
300                values.as_mut_ptr(),
301                total,
302            );
303        }
304    }
305
306    /// Finalizes the function's basic block analysis.
307    pub fn finalize(&mut self) {
308        if !self.direct_code_references.is_empty() {
309            self.update_direct_code_references();
310        }
311
312        if !self.direct_no_return_calls.is_empty() {
313            self.update_direct_no_return_calls();
314        }
315
316        if !self.halted_disassembly_addresses.is_empty() {
317            self.update_halted_disassembly_addresses();
318        }
319
320        if !self.inlined_unresolved_indirect_branches.is_empty() {
321            self.update_inlined_unresolved_indirect_branches();
322        }
323
324        unsafe {
325            (*self.handle).maxSizeReached = self.max_size_reached;
326        }
327
328        if self.contextual_returns_dirty {
329            self.update_contextual_returns();
330        }
331
332        unsafe { BNAnalyzeBasicBlocksContextFinalize(self.handle) };
333    }
334}
335
336impl Debug for BasicBlockAnalysisContext {
337    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
338        f.debug_struct("BasicBlockAnalysisContext")
339            .field("indirect_branches", &self.indirect_branches)
340            .field("indirect_no_return_calls", &self.indirect_no_return_calls)
341            .field("analysis_skip_override", &self.analysis_skip_override)
342            .field("translate_tail_calls", &self.translate_tail_calls)
343            .field("disallow_branch_to_string", &self.disallow_branch_to_string)
344            .field("max_function_size", &self.max_function_size)
345            .field("guided_analysis_mode", &self.guided_analysis_mode)
346            .field(
347                "trigger_guided_on_invalid_instruction",
348                &self.trigger_guided_on_invalid_instruction,
349            )
350            .field("max_size_reached", &self.max_size_reached)
351            .field("contextual_returns", &self.contextual_returns)
352            .field("direct_code_references", &self.direct_code_references)
353            .field("direct_no_return_calls", &self.direct_no_return_calls)
354            .field(
355                "halted_disassembly_addresses",
356                &self.halted_disassembly_addresses,
357            )
358            .finish()
359    }
360}