binaryninja/architecture/
basic_block.rs1use 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 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 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 pub fn add_basic_block(&self, block: Ref<BasicBlock<NativeBlock>>) {
205 unsafe {
206 BNAnalyzeBasicBlocksContextAddBasicBlockToFunction(self.handle, block.handle);
207 }
208 }
209
210 pub fn add_temp_outgoing_reference(&self, target: &Function) {
212 unsafe {
213 BNAnalyzeBasicBlocksContextAddTempReference(self.handle, target.handle);
214 }
215 }
216
217 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 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 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 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 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 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}