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
34/// Per-function store of basic block instruction bytes, populated during basic block analysis and
35/// read during lifting. Obtained from [`BasicBlockAnalysisContext::lifter_instruction_data`] or
36/// [`crate::architecture::FunctionLifterContext::lifter_instruction_data`].
37pub struct LifterInstructionData {
38    handle: *mut BNLifterInstructionData,
39}
40
41impl LifterInstructionData {
42    pub(crate) unsafe fn from_raw(handle: *mut BNLifterInstructionData) -> Self {
43        Self {
44            handle: BNNewLifterInstructionDataReference(handle),
45        }
46    }
47
48    /// Append decoded bytes for a block. Call during basic block analysis only.
49    pub fn append(&self, block: &BasicBlock<NativeBlock>, data: &[u8]) {
50        unsafe {
51            BNLifterInstructionDataAppend(
52                self.handle,
53                block.handle,
54                data.as_ptr() as *const _,
55                data.len(),
56            );
57        }
58    }
59
60    /// The bytes from `addr` to the end of its block, or an empty slice when the block has no stored
61    /// data. Read-only, call during lifting.
62    pub fn get(&self, block: &BasicBlock<NativeBlock>, addr: u64) -> &[u8] {
63        unsafe {
64            let mut len: usize = 0;
65            let data = BNLifterInstructionDataGet(self.handle, block.handle, addr, &mut len);
66            if data.is_null() {
67                return &[];
68            }
69            std::slice::from_raw_parts(data, len)
70        }
71    }
72}
73
74impl Drop for LifterInstructionData {
75    fn drop(&mut self) {
76        unsafe {
77            BNFreeLifterInstructionData(self.handle);
78        }
79    }
80}
81
82impl BasicBlockAnalysisContext {
83    pub unsafe fn from_raw(handle: *mut BNBasicBlockAnalysisContext) -> Self {
84        debug_assert!(!handle.is_null());
85
86        let ctx_ref = &*handle;
87
88        let raw_indirect_branches: &[BNIndirectBranchInfo] =
89            std::slice::from_raw_parts(ctx_ref.indirectBranches, ctx_ref.indirectBranchesCount);
90        let indirect_branches: Vec<IndirectBranchInfo> = raw_indirect_branches
91            .iter()
92            .map(IndirectBranchInfo::from)
93            .collect();
94
95        let raw_indirect_no_return_calls: &[BNArchitectureAndAddress] = std::slice::from_raw_parts(
96            ctx_ref.indirectNoReturnCalls,
97            ctx_ref.indirectNoReturnCallsCount,
98        );
99        let indirect_no_return_calls: HashSet<Location> = raw_indirect_no_return_calls
100            .iter()
101            .map(Location::from)
102            .collect();
103
104        let raw_contextual_return_locs: &[BNArchitectureAndAddress] = unsafe {
105            std::slice::from_raw_parts(
106                ctx_ref.contextualFunctionReturnLocations,
107                ctx_ref.contextualFunctionReturnCount,
108            )
109        };
110        let raw_contextual_return_vals: &[bool] = unsafe {
111            std::slice::from_raw_parts(
112                ctx_ref.contextualFunctionReturnValues,
113                ctx_ref.contextualFunctionReturnCount,
114            )
115        };
116        let contextual_returns: HashMap<Location, bool> = raw_contextual_return_locs
117            .iter()
118            .map(Location::from)
119            .zip(raw_contextual_return_vals.iter().copied())
120            .collect();
121
122        // The lists below this are out params and are possibly not initialized.
123        let raw_direct_ref_sources: &[BNArchitectureAndAddress] = match ctx_ref
124            .directRefSources
125            .is_null()
126        {
127            true => &[],
128            false => std::slice::from_raw_parts(ctx_ref.directRefSources, ctx_ref.directRefCount),
129        };
130        let raw_direct_ref_targets: &[u64] = match ctx_ref.directRefTargets.is_null() {
131            true => &[],
132            false => std::slice::from_raw_parts(ctx_ref.directRefTargets, ctx_ref.directRefCount),
133        };
134        let direct_code_references: HashMap<u64, Location> = raw_direct_ref_targets
135            .iter()
136            .copied()
137            .zip(raw_direct_ref_sources.iter().map(Location::from))
138            .collect();
139
140        let raw_direct_no_return_calls: &[BNArchitectureAndAddress] =
141            match ctx_ref.directNoReturnCalls.is_null() {
142                true => &[],
143                false => std::slice::from_raw_parts(
144                    ctx_ref.directNoReturnCalls,
145                    ctx_ref.directNoReturnCallsCount,
146                ),
147            };
148        let direct_no_return_calls: HashSet<Location> = raw_direct_no_return_calls
149            .iter()
150            .map(Location::from)
151            .collect();
152
153        let raw_halted_disassembly_address: &[BNArchitectureAndAddress] =
154            match ctx_ref.haltedDisassemblyAddresses.is_null() {
155                true => &[],
156                false => std::slice::from_raw_parts(
157                    ctx_ref.haltedDisassemblyAddresses,
158                    ctx_ref.haltedDisassemblyAddressesCount,
159                ),
160            };
161        let halted_disassembly_addresses: HashSet<Location> = raw_halted_disassembly_address
162            .iter()
163            .map(Location::from)
164            .collect();
165
166        let raw_inlined_unresolved_indirect_branches: &[BNArchitectureAndAddress] =
167            match ctx_ref.inlinedUnresolvedIndirectBranches.is_null() {
168                true => &[],
169                false => std::slice::from_raw_parts(
170                    ctx_ref.inlinedUnresolvedIndirectBranches,
171                    ctx_ref.inlinedUnresolvedIndirectBranchCount,
172                ),
173            };
174        let inlined_unresolved_indirect_branches: HashSet<Location> =
175            raw_inlined_unresolved_indirect_branches
176                .iter()
177                .map(Location::from)
178                .collect();
179
180        BasicBlockAnalysisContext {
181            handle,
182            contextual_returns_dirty: false,
183            indirect_branches,
184            indirect_no_return_calls,
185            analysis_skip_override: ctx_ref.analysisSkipOverride,
186            guided_analysis_mode: ctx_ref.guidedAnalysisMode,
187            trigger_guided_on_invalid_instruction: ctx_ref.triggerGuidedOnInvalidInstruction,
188            translate_tail_calls: ctx_ref.translateTailCalls,
189            disallow_branch_to_string: ctx_ref.disallowBranchToString,
190            max_function_size: ctx_ref.maxFunctionSize,
191            max_size_reached: ctx_ref.maxSizeReached,
192            contextual_returns,
193            direct_code_references,
194            direct_no_return_calls,
195            halted_disassembly_addresses,
196            inlined_unresolved_indirect_branches,
197        }
198    }
199
200    /// Adds a contextual function return location and its value to the current function.
201    pub fn add_contextual_return(&mut self, loc: impl Into<Location>, value: bool) {
202        let loc = loc.into();
203        if !self.contextual_returns.contains_key(&loc) {
204            self.contextual_returns_dirty = true;
205        }
206
207        self.contextual_returns.insert(loc, value);
208    }
209
210    /// Adds a direct code reference to the current function.
211    pub fn add_direct_code_reference(&mut self, target: u64, src: impl Into<Location>) {
212        self.direct_code_references
213            .entry(target)
214            .or_insert(src.into());
215    }
216
217    /// Adds a direct no-return call location to the current function.
218    pub fn add_direct_no_return_call(&mut self, loc: impl Into<Location>) {
219        self.direct_no_return_calls.insert(loc.into());
220    }
221
222    /// Adds an address to the set of halted disassembly addresses.
223    pub fn add_halted_disassembly_address(&mut self, loc: impl Into<Location>) {
224        self.halted_disassembly_addresses.insert(loc.into());
225    }
226
227    pub fn add_inlined_unresolved_indirect_branch(&mut self, loc: impl Into<Location>) {
228        self.inlined_unresolved_indirect_branches.insert(loc.into());
229    }
230
231    pub fn set_function_arch_context<A: ArchitectureWithFunctionContext>(
232        &mut self,
233        _arch: &A,
234        context: Box<A::FunctionArchContext>,
235    ) -> bool {
236        unsafe {
237            if !(*self.handle).functionArchContext.is_null() {
238                return false;
239            }
240            (*self.handle).functionArchContext = Box::into_raw(context) as *mut std::ffi::c_void;
241        }
242        true
243    }
244
245    pub fn get_function_arch_context<A: ArchitectureWithFunctionContext>(
246        &self,
247        _arch: &A,
248    ) -> Option<&A::FunctionArchContext> {
249        unsafe {
250            let ptr = (*self.handle).functionArchContext;
251            if ptr.is_null() {
252                None
253            } else {
254                Some(&*(ptr as *const A::FunctionArchContext))
255            }
256        }
257    }
258
259    /// Creates a new [`BasicBlock`] at the specified address for the given [`CoreArchitecture`].
260    ///
261    /// After creating, you can add using [`BasicBlockAnalysisContext::add_basic_block`].
262    pub fn create_basic_block(
263        &self,
264        arch: CoreArchitecture,
265        start: u64,
266    ) -> Option<Ref<BasicBlock<NativeBlock>>> {
267        let raw_block =
268            unsafe { BNAnalyzeBasicBlocksContextCreateBasicBlock(self.handle, arch.handle, start) };
269
270        if raw_block.is_null() {
271            return None;
272        }
273
274        unsafe { Some(BasicBlock::ref_from_raw(raw_block, NativeBlock::new())) }
275    }
276
277    /// Adds a [`BasicBlock`] to the current function.
278    ///
279    /// You can create a [`BasicBlock`] via [`BasicBlockAnalysisContext::create_basic_block`].
280    pub fn add_basic_block(&self, block: Ref<BasicBlock<NativeBlock>>) {
281        unsafe {
282            BNAnalyzeBasicBlocksContextAddBasicBlockToFunction(self.handle, block.handle);
283        }
284    }
285
286    /// The per-function instruction byte store. Populate it here during basic block analysis so that
287    /// lifting can read instruction bytes without touching the view from the multi-threaded stage.
288    pub fn lifter_instruction_data(&self) -> Option<LifterInstructionData> {
289        let handle = unsafe { (*self.handle).lifterInstructionData };
290        if handle.is_null() {
291            None
292        } else {
293            Some(unsafe { LifterInstructionData::from_raw(handle) })
294        }
295    }
296
297    /// Adds a temporary outgoing reference to the specified function.
298    pub fn add_temp_outgoing_reference(&self, target: &Function) {
299        unsafe {
300            BNAnalyzeBasicBlocksContextAddTempReference(self.handle, target.handle);
301        }
302    }
303
304    /// To be called before finalizing the basic block analysis.
305    fn update_direct_code_references(&mut self) {
306        let total = self.direct_code_references.len();
307        let mut sources: Vec<BNArchitectureAndAddress> = Vec::with_capacity(total);
308        let mut targets: Vec<u64> = Vec::with_capacity(total);
309        for (target, src) in &self.direct_code_references {
310            sources.push(src.into());
311            targets.push(*target);
312        }
313        unsafe {
314            BNAnalyzeBasicBlocksContextSetDirectCodeReferences(
315                self.handle,
316                sources.as_mut_ptr(),
317                targets.as_mut_ptr(),
318                total,
319            );
320        }
321    }
322
323    /// To be called before finalizing the basic block analysis.
324    fn update_direct_no_return_calls(&mut self) {
325        let total = self.direct_no_return_calls.len();
326        let mut raw_locations: Vec<_> = self
327            .direct_no_return_calls
328            .iter()
329            .map(BNArchitectureAndAddress::from)
330            .collect();
331        unsafe {
332            BNAnalyzeBasicBlocksContextSetDirectNoReturnCalls(
333                self.handle,
334                raw_locations.as_mut_ptr(),
335                total,
336            );
337        }
338    }
339
340    /// To be called before finalizing the basic block analysis.
341    fn update_inlined_unresolved_indirect_branches(&mut self) {
342        let total = self.inlined_unresolved_indirect_branches.len();
343        let mut raw_locations: Vec<_> = self
344            .inlined_unresolved_indirect_branches
345            .iter()
346            .map(BNArchitectureAndAddress::from)
347            .collect();
348        unsafe {
349            BNAnalyzeBasicBlocksContextSetInlinedUnresolvedIndirectBranches(
350                self.handle,
351                raw_locations.as_mut_ptr(),
352                total,
353            );
354        }
355    }
356
357    /// To be called before finalizing the basic block analysis.
358    fn update_halted_disassembly_addresses(&mut self) {
359        let total = self.halted_disassembly_addresses.len();
360        let mut raw_locations: Vec<_> = self
361            .halted_disassembly_addresses
362            .iter()
363            .map(BNArchitectureAndAddress::from)
364            .collect();
365        unsafe {
366            BNAnalyzeBasicBlocksContextSetHaltedDisassemblyAddresses(
367                self.handle,
368                raw_locations.as_mut_ptr(),
369                total,
370            );
371        }
372    }
373
374    /// To be called before finalizing the basic block analysis.
375    fn update_contextual_returns(&mut self) {
376        let total = self.contextual_returns.len();
377        let mut locations: Vec<BNArchitectureAndAddress> = Vec::with_capacity(total);
378        let mut values: Vec<bool> = Vec::with_capacity(total);
379        for (loc, value) in &self.contextual_returns {
380            locations.push(loc.into());
381            values.push(*value);
382        }
383        unsafe {
384            BNAnalyzeBasicBlocksContextSetContextualFunctionReturns(
385                self.handle,
386                locations.as_mut_ptr(),
387                values.as_mut_ptr(),
388                total,
389            );
390        }
391    }
392
393    /// Finalizes the function's basic block analysis.
394    pub fn finalize(&mut self) {
395        if !self.direct_code_references.is_empty() {
396            self.update_direct_code_references();
397        }
398
399        if !self.direct_no_return_calls.is_empty() {
400            self.update_direct_no_return_calls();
401        }
402
403        if !self.halted_disassembly_addresses.is_empty() {
404            self.update_halted_disassembly_addresses();
405        }
406
407        if !self.inlined_unresolved_indirect_branches.is_empty() {
408            self.update_inlined_unresolved_indirect_branches();
409        }
410
411        unsafe {
412            (*self.handle).maxSizeReached = self.max_size_reached;
413        }
414
415        if self.contextual_returns_dirty {
416            self.update_contextual_returns();
417        }
418    }
419}
420
421impl Debug for BasicBlockAnalysisContext {
422    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
423        f.debug_struct("BasicBlockAnalysisContext")
424            .field("indirect_branches", &self.indirect_branches)
425            .field("indirect_no_return_calls", &self.indirect_no_return_calls)
426            .field("analysis_skip_override", &self.analysis_skip_override)
427            .field("translate_tail_calls", &self.translate_tail_calls)
428            .field("disallow_branch_to_string", &self.disallow_branch_to_string)
429            .field("max_function_size", &self.max_function_size)
430            .field("guided_analysis_mode", &self.guided_analysis_mode)
431            .field(
432                "trigger_guided_on_invalid_instruction",
433                &self.trigger_guided_on_invalid_instruction,
434            )
435            .field("max_size_reached", &self.max_size_reached)
436            .field("contextual_returns", &self.contextual_returns)
437            .field("direct_code_references", &self.direct_code_references)
438            .field("direct_no_return_calls", &self.direct_no_return_calls)
439            .field(
440                "halted_disassembly_addresses",
441                &self.halted_disassembly_addresses,
442            )
443            .finish()
444    }
445}