binaryninja/architecture/
basic_block.rs1use 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 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 pub max_size_reached: bool,
25 contextual_returns: HashMap<Location, bool>,
26
27 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 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 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 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 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 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 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 pub fn add_basic_block(&self, block: Ref<BasicBlock<NativeBlock>>) {
233 unsafe {
234 BNAnalyzeBasicBlocksContextAddBasicBlockToFunction(self.handle, block.handle);
235 }
236 }
237
238 pub fn add_temp_outgoing_reference(&self, target: &Function) {
240 unsafe {
241 BNAnalyzeBasicBlocksContextAddTempReference(self.handle, target.handle);
242 }
243 }
244
245 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 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 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 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 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 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}