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
34pub 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 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 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 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 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 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 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 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 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 pub fn add_basic_block(&self, block: Ref<BasicBlock<NativeBlock>>) {
281 unsafe {
282 BNAnalyzeBasicBlocksContextAddBasicBlockToFunction(self.handle, block.handle);
283 }
284 }
285
286 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 pub fn add_temp_outgoing_reference(&self, target: &Function) {
299 unsafe {
300 BNAnalyzeBasicBlocksContextAddTempReference(self.handle, target.handle);
301 }
302 }
303
304 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 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 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 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 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 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}