1use binaryninjacore_sys::*;
2use std::fmt::{Debug, Formatter};
3use std::hash::{Hash, Hasher};
4
5use super::{
6 MediumLevelExpressionIndex, MediumLevelILBlock, MediumLevelILInstruction,
7 MediumLevelInstructionIndex,
8};
9use crate::architecture::CoreArchitecture;
10use crate::basic_block::BasicBlock;
11use crate::confidence::Conf;
12use crate::disassembly::DisassemblySettings;
13use crate::flowgraph::FlowGraph;
14use crate::function::{Function, Location};
15use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref, RefCountable};
16use crate::types::Type;
17use crate::variable::{PossibleValueSet, RegisterValue, SSAVariable, UserVariableValue, Variable};
18
19pub use binaryninjacore_sys::BNFunctionGraphType as FunctionGraphType;
21
22pub struct MediumLevelILFunction {
23 pub(crate) handle: *mut BNMediumLevelILFunction,
24}
25
26impl MediumLevelILFunction {
27 pub(crate) unsafe fn from_raw(handle: *mut BNMediumLevelILFunction) -> Self {
28 debug_assert!(!handle.is_null());
29 Self { handle }
30 }
31
32 pub(crate) unsafe fn ref_from_raw(handle: *mut BNMediumLevelILFunction) -> Ref<Self> {
33 debug_assert!(!handle.is_null());
34 Ref::new(Self::from_raw(handle))
35 }
36
37 pub fn instruction_at<L: Into<Location>>(&self, loc: L) -> Option<MediumLevelILInstruction> {
38 Some(MediumLevelILInstruction::from_instr_index(
39 self.to_owned(),
40 self.instruction_index_at(loc)?,
41 ))
42 }
43
44 pub fn instruction_index_at<L: Into<Location>>(
45 &self,
46 loc: L,
47 ) -> Option<MediumLevelInstructionIndex> {
48 let loc: Location = loc.into();
49 let arch = loc.arch.unwrap_or_else(|| self.function().arch());
51 let instr_idx =
52 unsafe { BNMediumLevelILGetInstructionStart(self.handle, arch.handle, loc.addr) };
53 if instr_idx >= self.instruction_count() {
55 None
56 } else {
57 Some(MediumLevelInstructionIndex(instr_idx))
58 }
59 }
60
61 pub fn instruction_from_index(
62 &self,
63 index: MediumLevelInstructionIndex,
64 ) -> Option<MediumLevelILInstruction> {
65 if index.0 >= self.instruction_count() {
66 None
67 } else {
68 Some(MediumLevelILInstruction::from_instr_index(
69 self.to_owned(),
70 index,
71 ))
72 }
73 }
74
75 pub fn instruction_from_expr_index(
76 &self,
77 expr_index: MediumLevelExpressionIndex,
78 ) -> Option<MediumLevelILInstruction> {
79 if expr_index.0 >= self.expression_count() {
80 None
81 } else {
82 Some(MediumLevelILInstruction::from_expr_index(
83 self.to_owned(),
84 expr_index,
85 ))
86 }
87 }
88
89 pub fn instruction_count(&self) -> usize {
90 unsafe { BNGetMediumLevelILInstructionCount(self.handle) }
91 }
92
93 pub fn expression_count(&self) -> usize {
94 unsafe { BNGetMediumLevelILExprCount(self.handle) }
95 }
96
97 pub fn ssa_form(&self) -> Ref<MediumLevelILFunction> {
98 let ssa = unsafe { BNGetMediumLevelILSSAForm(self.handle) };
99 assert!(!ssa.is_null());
100 unsafe { MediumLevelILFunction::ref_from_raw(ssa) }
101 }
102
103 pub fn function(&self) -> Ref<Function> {
104 unsafe {
105 let func = BNGetMediumLevelILOwnerFunction(self.handle);
106 Function::ref_from_raw(func)
107 }
108 }
109
110 pub fn basic_blocks(&self) -> Array<BasicBlock<MediumLevelILBlock>> {
111 let mut count = 0;
112 let blocks = unsafe { BNGetMediumLevelILBasicBlockList(self.handle, &mut count) };
113 let context = MediumLevelILBlock {
114 function: self.to_owned(),
115 };
116 unsafe { Array::new(blocks, count, context) }
117 }
118
119 #[deprecated = "Use `Function::create_user_stack_var` instead"]
120 pub fn create_user_stack_var<'a, C: Into<Conf<&'a Type>>>(
121 &self,
122 offset: i64,
123 var_type: C,
124 name: &str,
125 ) {
126 self.function()
127 .create_user_stack_var(offset, var_type, name)
128 }
129
130 #[deprecated = "Use `Function::delete_user_stack_var` instead"]
131 pub fn delete_user_stack_var(&self, offset: i64) {
132 self.function().delete_user_stack_var(offset)
133 }
134
135 #[deprecated = "Use `Function::create_user_var` instead"]
136 pub fn create_user_var<'a, C: Into<Conf<&'a Type>>>(
137 &self,
138 var: &Variable,
139 var_type: C,
140 name: &str,
141 ignore_disjoint_uses: bool,
142 ) {
143 self.function()
144 .create_user_var(var, var_type, name, ignore_disjoint_uses)
145 }
146
147 #[deprecated = "Use `Function::delete_user_var` instead"]
148 pub fn delete_user_var(&self, var: &Variable) {
149 self.function().delete_user_var(var)
150 }
151
152 #[deprecated = "Use `Function::is_var_user_defined` instead"]
153 pub fn is_var_user_defined(&self, var: &Variable) -> bool {
154 self.function().is_var_user_defined(var)
155 }
156
157 pub fn set_user_var_value(
183 &self,
184 var: &Variable,
185 addr: u64,
186 value: PossibleValueSet,
187 after: bool,
188 ) -> Result<(), ()> {
189 let function = self.function();
190 let def_site = BNArchitectureAndAddress {
191 arch: function.arch().handle,
192 address: addr,
193 };
194 let raw_var = BNVariable::from(var);
195 let raw_value = PossibleValueSet::into_rust_raw(value);
196 unsafe { BNSetUserVariableValue(function.handle, &raw_var, &def_site, after, &raw_value) }
197 PossibleValueSet::free_rust_raw(raw_value);
198 Ok(())
199 }
200
201 pub fn clear_user_var_value(&self, var: &Variable, addr: u64, after: bool) -> Result<(), ()> {
206 let Some(_var_def) = self
207 .variable_definitions(var)
208 .iter()
209 .find(|site| site.address == addr)
210 else {
211 return Err(());
213 };
214
215 let function = self.function();
216 let raw_var = BNVariable::from(var);
217 let def_site = BNArchitectureAndAddress {
218 arch: function.arch().handle,
219 address: addr,
220 };
221
222 unsafe { BNClearUserVariableValue(function.handle, &raw_var, &def_site, after) };
223 Ok(())
224 }
225
226 pub fn user_var_values(&self) -> Array<UserVariableValue> {
229 let mut count = 0;
230 let function = self.function();
231 let var_values = unsafe { BNGetAllUserVariableValues(function.handle, &mut count) };
232 assert!(!var_values.is_null());
233 unsafe { Array::new(var_values, count, ()) }
234 }
235
236 pub fn clear_user_var_values(&self) -> Result<(), ()> {
238 for user_var_val in &self.user_var_values() {
239 self.clear_user_var_value(
240 &user_var_val.variable,
241 user_var_val.def_site.addr,
242 user_var_val.after,
243 )?;
244 }
245 Ok(())
246 }
247
248 #[deprecated = "Use `Function::create_auto_stack_var` instead"]
249 pub fn create_auto_stack_var<'a, T: Into<Conf<&'a Type>>>(
250 &self,
251 offset: i64,
252 var_type: T,
253 name: &str,
254 ) {
255 self.function()
256 .create_auto_stack_var(offset, var_type, name)
257 }
258
259 #[deprecated = "Use `Function::delete_auto_stack_var` instead"]
260 pub fn delete_auto_stack_var(&self, offset: i64) {
261 self.function().delete_auto_stack_var(offset)
262 }
263
264 #[deprecated = "Use `Function::create_auto_var` instead"]
265 pub fn create_auto_var<'a, C: Into<Conf<&'a Type>>>(
266 &self,
267 var: &Variable,
268 var_type: C,
269 name: &str,
270 ignore_disjoint_uses: bool,
271 ) {
272 self.function()
273 .create_auto_var(var, var_type, name, ignore_disjoint_uses)
274 }
275
276 pub fn var_refs(&self, var: &Variable) -> Array<ILReferenceSource> {
294 let mut count = 0;
295 let mut raw_var = BNVariable::from(var);
296 let refs = unsafe {
297 BNGetMediumLevelILVariableReferences(self.function().handle, &mut raw_var, &mut count)
298 };
299 assert!(!refs.is_null());
300 unsafe { Array::new(refs, count, ()) }
301 }
302
303 pub fn var_refs_from(
308 &self,
309 location: impl Into<Location>,
310 length: Option<u64>,
311 ) -> Array<VariableReferenceSource> {
312 let location = location.into();
313 let raw_arch = location
314 .arch
315 .map(|a| a.handle)
316 .unwrap_or(std::ptr::null_mut());
317 let function = self.function();
318 let mut count = 0;
319
320 let refs = if let Some(length) = length {
321 unsafe {
322 BNGetMediumLevelILVariableReferencesInRange(
323 function.handle,
324 raw_arch,
325 location.addr,
326 length,
327 &mut count,
328 )
329 }
330 } else {
331 unsafe {
332 BNGetMediumLevelILVariableReferencesFrom(
333 function.handle,
334 raw_arch,
335 location.addr,
336 &mut count,
337 )
338 }
339 };
340 assert!(!refs.is_null());
341 unsafe { Array::new(refs, count, ()) }
342 }
343
344 pub fn current_address(&self) -> Location {
347 let addr = unsafe { BNMediumLevelILGetCurrentAddress(self.handle) };
348 Location::from(addr)
349 }
350
351 pub fn set_current_address(&self, location: impl Into<Location>) {
354 let location = location.into();
355 let arch = location
356 .arch
357 .map(|a| a.handle)
358 .unwrap_or(std::ptr::null_mut());
359 unsafe { BNMediumLevelILSetCurrentAddress(self.handle, arch, location.addr) }
360 }
361
362 pub fn basic_block_containing_index(
366 &self,
367 index: MediumLevelInstructionIndex,
368 ) -> Option<Ref<BasicBlock<MediumLevelILBlock>>> {
369 let context = MediumLevelILBlock {
370 function: self.to_owned(),
371 };
372 let basic_block_ptr =
374 unsafe { BNGetMediumLevelILBasicBlockForInstruction(self.handle, index.0) };
375 match basic_block_ptr.is_null() {
376 false => Some(unsafe { BasicBlock::ref_from_raw(basic_block_ptr, context) }),
377 true => None,
378 }
379 }
380
381 pub fn finalize(&self) {
385 unsafe { BNFinalizeMediumLevelILFunction(self.handle) }
386 }
387
388 pub fn generate_ssa_form(
397 &self,
398 analyze_conditionals: bool,
399 handle_aliases: bool,
400 non_aliased_vars: impl IntoIterator<Item = Variable>,
401 aliased_vars: impl IntoIterator<Item = Variable>,
402 ) {
403 let raw_non_aliased_vars: Vec<BNVariable> =
404 non_aliased_vars.into_iter().map(Into::into).collect();
405 let raw_aliased_vars: Vec<BNVariable> = aliased_vars.into_iter().map(Into::into).collect();
406 unsafe {
407 BNGenerateMediumLevelILSSAForm(
408 self.handle,
409 analyze_conditionals,
410 handle_aliases,
411 raw_non_aliased_vars.as_ptr() as *mut _,
412 raw_non_aliased_vars.len(),
413 raw_aliased_vars.as_ptr() as *mut _,
414 raw_aliased_vars.len(),
415 )
416 }
417 }
418
419 pub fn ssa_variable_definition(
424 &self,
425 ssa_variable: &SSAVariable,
426 ) -> Option<MediumLevelILInstruction> {
427 let raw_var = BNVariable::from(ssa_variable.variable);
428 let result = unsafe {
429 BNGetMediumLevelILSSAVarDefinition(self.handle, &raw_var, ssa_variable.version)
430 };
431 self.instruction_from_index(MediumLevelInstructionIndex(result))
433 }
434
435 pub fn ssa_memory_definition(&self, version: usize) -> Option<MediumLevelILInstruction> {
436 let result = unsafe { BNGetMediumLevelILSSAMemoryDefinition(self.handle, version) };
437 self.instruction_from_index(MediumLevelInstructionIndex(result))
439 }
440
441 pub fn ssa_variable_uses(&self, ssa_variable: &SSAVariable) -> Array<MediumLevelILInstruction> {
443 let mut count = 0;
444 let raw_var = BNVariable::from(ssa_variable.variable);
445 let uses = unsafe {
446 BNGetMediumLevelILSSAVarUses(self.handle, &raw_var, ssa_variable.version, &mut count)
447 };
448 assert!(!uses.is_null());
449 unsafe { Array::new(uses, count, self.to_owned()) }
450 }
451
452 pub fn ssa_memory_uses(&self, version: usize) -> Array<MediumLevelILInstruction> {
453 let mut count = 0;
454 let uses = unsafe { BNGetMediumLevelILSSAMemoryUses(self.handle, version, &mut count) };
455 assert!(!uses.is_null());
456 unsafe { Array::new(uses, count, self.to_owned()) }
457 }
458
459 pub fn is_ssa_variable_live(&self, ssa_variable: &SSAVariable) -> bool {
461 let raw_var = BNVariable::from(ssa_variable.variable);
462 unsafe { BNIsMediumLevelILSSAVarLive(self.handle, &raw_var, ssa_variable.version) }
463 }
464
465 pub fn variable_definitions(&self, variable: &Variable) -> Array<MediumLevelILInstruction> {
466 let mut count = 0;
467 let raw_var = BNVariable::from(variable);
468 let defs =
469 unsafe { BNGetMediumLevelILVariableDefinitions(self.handle, &raw_var, &mut count) };
470 assert!(!defs.is_null());
471 unsafe { Array::new(defs, count, self.to_owned()) }
472 }
473
474 pub fn variable_uses(&self, variable: &Variable) -> Array<MediumLevelILInstruction> {
475 let mut count = 0;
476 let raw_var = BNVariable::from(variable);
477 let uses = unsafe { BNGetMediumLevelILVariableUses(self.handle, &raw_var, &mut count) };
478 unsafe { Array::new(uses, count, self.to_owned()) }
479 }
480
481 pub fn live_instruction_for_variable(
489 &self,
490 variable: &Variable,
491 include_last_use: bool,
492 ) -> Array<MediumLevelILInstruction> {
493 let mut count = 0;
494 let raw_var = BNVariable::from(variable);
495 let uses = unsafe {
496 BNGetMediumLevelILLiveInstructionsForVariable(
497 self.handle,
498 &raw_var,
499 include_last_use,
500 &mut count,
501 )
502 };
503 unsafe { Array::new(uses, count, self.to_owned()) }
504 }
505
506 pub fn ssa_variable_value(&self, ssa_variable: &SSAVariable) -> RegisterValue {
507 let raw_var = BNVariable::from(ssa_variable.variable);
508 unsafe { BNGetMediumLevelILSSAVarValue(self.handle, &raw_var, ssa_variable.version) }.into()
509 }
510
511 pub fn create_graph(&self, settings: Option<DisassemblySettings>) -> Ref<FlowGraph> {
512 let settings = settings.map(|x| x.handle).unwrap_or(std::ptr::null_mut());
513 let graph = unsafe { BNCreateMediumLevelILFunctionGraph(self.handle, settings) };
514 unsafe { FlowGraph::ref_from_raw(graph) }
515 }
516
517 pub fn variables(&self) -> Array<Variable> {
521 let mut count = 0;
522 let uses = unsafe { BNGetMediumLevelILVariables(self.handle, &mut count) };
523 unsafe { Array::new(uses, count, ()) }
524 }
525
526 pub fn aliased_variables(&self) -> Array<Variable> {
530 let mut count = 0;
531 let uses = unsafe { BNGetMediumLevelILAliasedVariables(self.handle, &mut count) };
532 unsafe { Array::new(uses, count, ()) }
533 }
534
535 pub fn ssa_variables(&self, variable: &Variable) -> Array<SSAVariable> {
537 let mut count = 0;
538 let raw_variable = BNVariable::from(variable);
539 let versions = unsafe {
540 BNGetMediumLevelILVariableSSAVersions(self.handle, &raw_variable, &mut count)
541 };
542 unsafe { Array::new(versions, count, *variable) }
543 }
544}
545
546impl ToOwned for MediumLevelILFunction {
547 type Owned = Ref<Self>;
548
549 fn to_owned(&self) -> Self::Owned {
550 unsafe { RefCountable::inc_ref(self) }
551 }
552}
553
554unsafe impl RefCountable for MediumLevelILFunction {
555 unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
556 Ref::new(Self {
557 handle: BNNewMediumLevelILFunctionReference(handle.handle),
558 })
559 }
560
561 unsafe fn dec_ref(handle: &Self) {
562 BNFreeMediumLevelILFunction(handle.handle);
563 }
564}
565
566impl Debug for MediumLevelILFunction {
567 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
568 f.debug_struct("MediumLevelILFunction")
569 .field("arch", &self.function().arch())
570 .field("instruction_count", &self.instruction_count())
571 .finish()
572 }
573}
574
575unsafe impl Send for MediumLevelILFunction {}
576unsafe impl Sync for MediumLevelILFunction {}
577
578impl Eq for MediumLevelILFunction {}
579impl PartialEq for MediumLevelILFunction {
580 fn eq(&self, rhs: &Self) -> bool {
581 self.function().eq(&rhs.function())
582 }
583}
584
585impl Hash for MediumLevelILFunction {
586 fn hash<H: Hasher>(&self, state: &mut H) {
587 self.function().hash(state)
588 }
589}
590
591pub struct ILReferenceSource {
592 pub function: Ref<Function>,
593 pub arch: CoreArchitecture,
594 pub addr: u64,
595 pub graph_type: FunctionGraphType,
596 pub expr_idx: usize,
597}
598
599impl From<BNILReferenceSource> for ILReferenceSource {
600 fn from(value: BNILReferenceSource) -> Self {
601 Self {
602 function: unsafe { Function::ref_from_raw(value.func) },
603 arch: unsafe { CoreArchitecture::from_raw(value.arch) },
604 addr: value.addr,
605 graph_type: value.type_,
606 expr_idx: value.exprId,
607 }
608 }
609}
610
611impl From<&BNILReferenceSource> for ILReferenceSource {
612 fn from(value: &BNILReferenceSource) -> Self {
613 Self {
614 function: unsafe { Function::from_raw(value.func).to_owned() },
615 arch: unsafe { CoreArchitecture::from_raw(value.arch) },
616 addr: value.addr,
617 graph_type: value.type_,
618 expr_idx: value.exprId,
619 }
620 }
621}
622
623impl CoreArrayProvider for ILReferenceSource {
624 type Raw = BNILReferenceSource;
625 type Context = ();
626 type Wrapped<'a> = Self;
627}
628
629unsafe impl CoreArrayProviderInner for ILReferenceSource {
630 unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
631 BNFreeILReferences(raw, count)
632 }
633
634 unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
635 raw.into()
636 }
637}
638
639pub struct VariableReferenceSource {
640 pub variable: Variable,
641 pub source: ILReferenceSource,
642}
643
644impl From<BNVariableReferenceSource> for VariableReferenceSource {
645 fn from(value: BNVariableReferenceSource) -> Self {
646 Self {
647 variable: Variable::from(value.var),
648 source: value.source.into(),
649 }
650 }
651}
652
653impl From<&BNVariableReferenceSource> for VariableReferenceSource {
654 fn from(value: &BNVariableReferenceSource) -> Self {
655 Self {
656 variable: Variable::from(value.var),
657 source: ILReferenceSource::from(&value.source),
660 }
661 }
662}
663
664impl CoreArrayProvider for VariableReferenceSource {
665 type Raw = BNVariableReferenceSource;
666 type Context = ();
667 type Wrapped<'a> = Self;
668}
669
670unsafe impl CoreArrayProviderInner for VariableReferenceSource {
671 unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
672 BNFreeVariableReferenceSourceList(raw, count)
673 }
674
675 unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
676 raw.into()
677 }
678}