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::disassembly::DisassemblySettings;
12use crate::flowgraph::FlowGraph;
13use crate::function::{Function, Location};
14use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref, RefCountable};
15use crate::variable::{PossibleValueSet, RegisterValue, SSAVariable, UserVariableValue, Variable};
16
17pub use binaryninjacore_sys::BNFunctionGraphType as FunctionGraphType;
19
20pub struct MediumLevelILFunction {
21 pub(crate) handle: *mut BNMediumLevelILFunction,
22}
23
24impl MediumLevelILFunction {
25 pub(crate) unsafe fn from_raw(handle: *mut BNMediumLevelILFunction) -> Self {
26 debug_assert!(!handle.is_null());
27 Self { handle }
28 }
29
30 pub(crate) unsafe fn ref_from_raw(handle: *mut BNMediumLevelILFunction) -> Ref<Self> {
31 debug_assert!(!handle.is_null());
32 Ref::new(Self::from_raw(handle))
33 }
34
35 pub fn instruction_at<L: Into<Location>>(&self, loc: L) -> Option<MediumLevelILInstruction> {
36 Some(MediumLevelILInstruction::from_instr_index(
37 self.to_owned(),
38 self.instruction_index_at(loc)?,
39 ))
40 }
41
42 pub fn instruction_index_at<L: Into<Location>>(
43 &self,
44 loc: L,
45 ) -> Option<MediumLevelInstructionIndex> {
46 let loc: Location = loc.into();
47 let arch = loc.arch.unwrap_or_else(|| self.function().arch());
49 let instr_idx =
50 unsafe { BNMediumLevelILGetInstructionStart(self.handle, arch.handle, loc.addr) };
51 if instr_idx >= self.instruction_count() {
53 None
54 } else {
55 Some(MediumLevelInstructionIndex(instr_idx))
56 }
57 }
58
59 pub fn instruction_from_index(
60 &self,
61 index: MediumLevelInstructionIndex,
62 ) -> Option<MediumLevelILInstruction> {
63 if index.0 >= self.instruction_count() {
64 None
65 } else {
66 Some(MediumLevelILInstruction::from_instr_index(
67 self.to_owned(),
68 index,
69 ))
70 }
71 }
72
73 pub fn instruction_from_expr_index(
74 &self,
75 expr_index: MediumLevelExpressionIndex,
76 ) -> Option<MediumLevelILInstruction> {
77 if expr_index.0 >= self.expression_count() {
78 None
79 } else {
80 Some(MediumLevelILInstruction::from_expr_index(
81 self.to_owned(),
82 expr_index,
83 ))
84 }
85 }
86
87 pub fn instruction_count(&self) -> usize {
88 unsafe { BNGetMediumLevelILInstructionCount(self.handle) }
89 }
90
91 pub fn expression_count(&self) -> usize {
92 unsafe { BNGetMediumLevelILExprCount(self.handle) }
93 }
94
95 pub fn ssa_form(&self) -> Ref<MediumLevelILFunction> {
96 let ssa = unsafe { BNGetMediumLevelILSSAForm(self.handle) };
97 assert!(!ssa.is_null());
98 unsafe { MediumLevelILFunction::ref_from_raw(ssa) }
99 }
100
101 pub fn function(&self) -> Ref<Function> {
102 unsafe {
103 let func = BNGetMediumLevelILOwnerFunction(self.handle);
104 Function::ref_from_raw(func)
105 }
106 }
107
108 pub fn basic_blocks(&self) -> Array<BasicBlock<MediumLevelILBlock>> {
109 let mut count = 0;
110 let blocks = unsafe { BNGetMediumLevelILBasicBlockList(self.handle, &mut count) };
111 let context = MediumLevelILBlock {
112 function: self.to_owned(),
113 };
114 unsafe { Array::new(blocks, count, context) }
115 }
116
117 pub fn set_user_var_value(
143 &self,
144 var: &Variable,
145 addr: u64,
146 value: PossibleValueSet,
147 after: bool,
148 ) -> Result<(), ()> {
149 let function = self.function();
150 let def_site = BNArchitectureAndAddress {
151 arch: function.arch().handle,
152 address: addr,
153 };
154 let raw_var = BNVariable::from(var);
155 let raw_value = PossibleValueSet::into_rust_raw(value);
156 unsafe { BNSetUserVariableValue(function.handle, &raw_var, &def_site, after, &raw_value) }
157 PossibleValueSet::free_rust_raw(raw_value);
158 Ok(())
159 }
160
161 pub fn clear_user_var_value(&self, var: &Variable, addr: u64, after: bool) -> Result<(), ()> {
166 let Some(_var_def) = self
167 .variable_definitions(var)
168 .iter()
169 .find(|site| site.address == addr)
170 else {
171 return Err(());
173 };
174
175 let function = self.function();
176 let raw_var = BNVariable::from(var);
177 let def_site = BNArchitectureAndAddress {
178 arch: function.arch().handle,
179 address: addr,
180 };
181
182 unsafe { BNClearUserVariableValue(function.handle, &raw_var, &def_site, after) };
183 Ok(())
184 }
185
186 pub fn user_var_values(&self) -> Array<UserVariableValue> {
189 let mut count = 0;
190 let function = self.function();
191 let var_values = unsafe { BNGetAllUserVariableValues(function.handle, &mut count) };
192 assert!(!var_values.is_null());
193 unsafe { Array::new(var_values, count, ()) }
194 }
195
196 pub fn clear_user_var_values(&self) -> Result<(), ()> {
198 for user_var_val in &self.user_var_values() {
199 self.clear_user_var_value(
200 &user_var_val.variable,
201 user_var_val.def_site.addr,
202 user_var_val.after,
203 )?;
204 }
205 Ok(())
206 }
207
208 pub fn var_refs(&self, var: &Variable) -> Array<ILReferenceSource> {
226 let mut count = 0;
227 let mut raw_var = BNVariable::from(var);
228 let refs = unsafe {
229 BNGetMediumLevelILVariableReferences(self.function().handle, &mut raw_var, &mut count)
230 };
231 assert!(!refs.is_null());
232 unsafe { Array::new(refs, count, ()) }
233 }
234
235 pub fn var_refs_from(
240 &self,
241 location: impl Into<Location>,
242 length: Option<u64>,
243 ) -> Array<VariableReferenceSource> {
244 let location = location.into();
245 let raw_arch = location
246 .arch
247 .map(|a| a.handle)
248 .unwrap_or(std::ptr::null_mut());
249 let function = self.function();
250 let mut count = 0;
251
252 let refs = if let Some(length) = length {
253 unsafe {
254 BNGetMediumLevelILVariableReferencesInRange(
255 function.handle,
256 raw_arch,
257 location.addr,
258 length,
259 &mut count,
260 )
261 }
262 } else {
263 unsafe {
264 BNGetMediumLevelILVariableReferencesFrom(
265 function.handle,
266 raw_arch,
267 location.addr,
268 &mut count,
269 )
270 }
271 };
272 assert!(!refs.is_null());
273 unsafe { Array::new(refs, count, ()) }
274 }
275
276 pub fn current_address(&self) -> Location {
279 let addr = unsafe { BNMediumLevelILGetCurrentAddress(self.handle) };
280 Location::from(addr)
281 }
282
283 pub fn set_current_address(&self, location: impl Into<Location>) {
286 let location = location.into();
287 let arch = location
288 .arch
289 .map(|a| a.handle)
290 .unwrap_or(std::ptr::null_mut());
291 unsafe { BNMediumLevelILSetCurrentAddress(self.handle, arch, location.addr) }
292 }
293
294 pub fn basic_block_containing_index(
298 &self,
299 index: MediumLevelInstructionIndex,
300 ) -> Option<Ref<BasicBlock<MediumLevelILBlock>>> {
301 let context = MediumLevelILBlock {
302 function: self.to_owned(),
303 };
304 let basic_block_ptr =
306 unsafe { BNGetMediumLevelILBasicBlockForInstruction(self.handle, index.0) };
307 match basic_block_ptr.is_null() {
308 false => Some(unsafe { BasicBlock::ref_from_raw(basic_block_ptr, context) }),
309 true => None,
310 }
311 }
312
313 pub fn finalize(&self) {
317 unsafe { BNFinalizeMediumLevelILFunction(self.handle) }
318 }
319
320 pub fn generate_ssa_form(
329 &self,
330 analyze_conditionals: bool,
331 handle_aliases: bool,
332 non_aliased_vars: impl IntoIterator<Item = Variable>,
333 aliased_vars: impl IntoIterator<Item = Variable>,
334 ) {
335 let raw_non_aliased_vars: Vec<BNVariable> =
336 non_aliased_vars.into_iter().map(Into::into).collect();
337 let raw_aliased_vars: Vec<BNVariable> = aliased_vars.into_iter().map(Into::into).collect();
338 unsafe {
339 BNGenerateMediumLevelILSSAForm(
340 self.handle,
341 analyze_conditionals,
342 handle_aliases,
343 raw_non_aliased_vars.as_ptr() as *mut _,
344 raw_non_aliased_vars.len(),
345 raw_aliased_vars.as_ptr() as *mut _,
346 raw_aliased_vars.len(),
347 )
348 }
349 }
350
351 pub fn ssa_variable_definition(
356 &self,
357 ssa_variable: &SSAVariable,
358 ) -> Option<MediumLevelILInstruction> {
359 let raw_var = BNVariable::from(ssa_variable.variable);
360 let result = unsafe {
361 BNGetMediumLevelILSSAVarDefinition(self.handle, &raw_var, ssa_variable.version)
362 };
363 self.instruction_from_index(MediumLevelInstructionIndex(result))
365 }
366
367 pub fn ssa_memory_definition(&self, version: usize) -> Option<MediumLevelILInstruction> {
368 let result = unsafe { BNGetMediumLevelILSSAMemoryDefinition(self.handle, version) };
369 self.instruction_from_index(MediumLevelInstructionIndex(result))
371 }
372
373 pub fn ssa_variable_uses(&self, ssa_variable: &SSAVariable) -> Array<MediumLevelILInstruction> {
375 let mut count = 0;
376 let raw_var = BNVariable::from(ssa_variable.variable);
377 let uses = unsafe {
378 BNGetMediumLevelILSSAVarUses(self.handle, &raw_var, ssa_variable.version, &mut count)
379 };
380 assert!(!uses.is_null());
381 unsafe { Array::new(uses, count, self.to_owned()) }
382 }
383
384 pub fn ssa_memory_uses(&self, version: usize) -> Array<MediumLevelILInstruction> {
385 let mut count = 0;
386 let uses = unsafe { BNGetMediumLevelILSSAMemoryUses(self.handle, version, &mut count) };
387 assert!(!uses.is_null());
388 unsafe { Array::new(uses, count, self.to_owned()) }
389 }
390
391 pub fn is_ssa_variable_live(&self, ssa_variable: &SSAVariable) -> bool {
393 let raw_var = BNVariable::from(ssa_variable.variable);
394 unsafe { BNIsMediumLevelILSSAVarLive(self.handle, &raw_var, ssa_variable.version) }
395 }
396
397 pub fn variable_definitions(&self, variable: &Variable) -> Array<MediumLevelILInstruction> {
398 let mut count = 0;
399 let raw_var = BNVariable::from(variable);
400 let defs =
401 unsafe { BNGetMediumLevelILVariableDefinitions(self.handle, &raw_var, &mut count) };
402 assert!(!defs.is_null());
403 unsafe { Array::new(defs, count, self.to_owned()) }
404 }
405
406 pub fn variable_uses(&self, variable: &Variable) -> Array<MediumLevelILInstruction> {
407 let mut count = 0;
408 let raw_var = BNVariable::from(variable);
409 let uses = unsafe { BNGetMediumLevelILVariableUses(self.handle, &raw_var, &mut count) };
410 unsafe { Array::new(uses, count, self.to_owned()) }
411 }
412
413 pub fn live_instruction_for_variable(
421 &self,
422 variable: &Variable,
423 include_last_use: bool,
424 ) -> Array<MediumLevelILInstruction> {
425 let mut count = 0;
426 let raw_var = BNVariable::from(variable);
427 let uses = unsafe {
428 BNGetMediumLevelILLiveInstructionsForVariable(
429 self.handle,
430 &raw_var,
431 include_last_use,
432 &mut count,
433 )
434 };
435 unsafe { Array::new(uses, count, self.to_owned()) }
436 }
437
438 pub fn ssa_variable_value(&self, ssa_variable: &SSAVariable) -> RegisterValue {
439 let raw_var = BNVariable::from(ssa_variable.variable);
440 unsafe { BNGetMediumLevelILSSAVarValue(self.handle, &raw_var, ssa_variable.version) }.into()
441 }
442
443 pub fn create_graph(&self, settings: Option<DisassemblySettings>) -> Ref<FlowGraph> {
444 let settings = settings.map(|x| x.handle).unwrap_or(std::ptr::null_mut());
445 let graph = unsafe { BNCreateMediumLevelILFunctionGraph(self.handle, settings) };
446 unsafe { FlowGraph::ref_from_raw(graph) }
447 }
448
449 pub fn variables(&self) -> Array<Variable> {
453 let mut count = 0;
454 let uses = unsafe { BNGetMediumLevelILVariables(self.handle, &mut count) };
455 unsafe { Array::new(uses, count, ()) }
456 }
457
458 pub fn aliased_variables(&self) -> Array<Variable> {
462 let mut count = 0;
463 let uses = unsafe { BNGetMediumLevelILAliasedVariables(self.handle, &mut count) };
464 unsafe { Array::new(uses, count, ()) }
465 }
466
467 pub fn ssa_variables(&self, variable: &Variable) -> Array<SSAVariable> {
469 let mut count = 0;
470 let raw_variable = BNVariable::from(variable);
471 let versions = unsafe {
472 BNGetMediumLevelILVariableSSAVersions(self.handle, &raw_variable, &mut count)
473 };
474 unsafe { Array::new(versions, count, *variable) }
475 }
476}
477
478impl ToOwned for MediumLevelILFunction {
479 type Owned = Ref<Self>;
480
481 fn to_owned(&self) -> Self::Owned {
482 unsafe { RefCountable::inc_ref(self) }
483 }
484}
485
486unsafe impl RefCountable for MediumLevelILFunction {
487 unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
488 Ref::new(Self {
489 handle: BNNewMediumLevelILFunctionReference(handle.handle),
490 })
491 }
492
493 unsafe fn dec_ref(handle: &Self) {
494 BNFreeMediumLevelILFunction(handle.handle);
495 }
496}
497
498impl Debug for MediumLevelILFunction {
499 fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
500 f.debug_struct("MediumLevelILFunction")
501 .field("arch", &self.function().arch())
502 .field("instruction_count", &self.instruction_count())
503 .finish()
504 }
505}
506
507unsafe impl Send for MediumLevelILFunction {}
508unsafe impl Sync for MediumLevelILFunction {}
509
510impl Eq for MediumLevelILFunction {}
511impl PartialEq for MediumLevelILFunction {
512 fn eq(&self, rhs: &Self) -> bool {
513 self.function().eq(&rhs.function())
514 }
515}
516
517impl Hash for MediumLevelILFunction {
518 fn hash<H: Hasher>(&self, state: &mut H) {
519 self.function().hash(state)
520 }
521}
522
523pub struct ILReferenceSource {
524 pub function: Ref<Function>,
525 pub arch: CoreArchitecture,
526 pub addr: u64,
527 pub graph_type: FunctionGraphType,
528 pub expr_idx: MediumLevelExpressionIndex,
529}
530
531impl From<BNILReferenceSource> for ILReferenceSource {
532 fn from(value: BNILReferenceSource) -> Self {
533 Self {
534 function: unsafe { Function::ref_from_raw(value.func) },
535 arch: unsafe { CoreArchitecture::from_raw(value.arch) },
536 addr: value.addr,
537 graph_type: value.type_,
538 expr_idx: MediumLevelExpressionIndex(value.exprId),
539 }
540 }
541}
542
543impl From<&BNILReferenceSource> for ILReferenceSource {
544 fn from(value: &BNILReferenceSource) -> Self {
545 Self {
546 function: unsafe { Function::from_raw(value.func).to_owned() },
547 arch: unsafe { CoreArchitecture::from_raw(value.arch) },
548 addr: value.addr,
549 graph_type: value.type_,
550 expr_idx: MediumLevelExpressionIndex(value.exprId),
551 }
552 }
553}
554
555impl CoreArrayProvider for ILReferenceSource {
556 type Raw = BNILReferenceSource;
557 type Context = ();
558 type Wrapped<'a> = Self;
559}
560
561unsafe impl CoreArrayProviderInner for ILReferenceSource {
562 unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
563 BNFreeILReferences(raw, count)
564 }
565
566 unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
567 raw.into()
568 }
569}
570
571pub struct VariableReferenceSource {
572 pub variable: Variable,
573 pub source: ILReferenceSource,
574}
575
576impl From<BNVariableReferenceSource> for VariableReferenceSource {
577 fn from(value: BNVariableReferenceSource) -> Self {
578 Self {
579 variable: Variable::from(value.var),
580 source: value.source.into(),
581 }
582 }
583}
584
585impl From<&BNVariableReferenceSource> for VariableReferenceSource {
586 fn from(value: &BNVariableReferenceSource) -> Self {
587 Self {
588 variable: Variable::from(value.var),
589 source: ILReferenceSource::from(&value.source),
592 }
593 }
594}
595
596impl CoreArrayProvider for VariableReferenceSource {
597 type Raw = BNVariableReferenceSource;
598 type Context = ();
599 type Wrapped<'a> = Self;
600}
601
602unsafe impl CoreArrayProviderInner for VariableReferenceSource {
603 unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
604 BNFreeVariableReferenceSourceList(raw, count)
605 }
606
607 unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
608 raw.into()
609 }
610}