binaryninja/medium_level_il/
function.rs

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
17// TODO: Does this belong here?
18pub 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        // If the location does not specify an architecture, use the function's architecture.
48        let arch = loc.arch.unwrap_or_else(|| self.function().arch());
49        let instr_idx =
50            unsafe { BNMediumLevelILGetInstructionStart(self.handle, arch.handle, loc.addr) };
51        // `instr_idx` will equal self.instruction_count() if the instruction is not valid.
52        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    /// Allows the user to specify a PossibleValueSet value for an MLIL
118    /// variable at its definition site.
119    ///
120    /// WARNING: Setting the variable value, triggers a reanalysis of the
121    /// function and allows the dataflow to compute and propagate values which
122    /// depend on the current variable. This implies that branch conditions
123    /// whose values can be determined statically will be computed, leading to
124    /// potential branch elimination at the HLIL layer.
125    ///
126    /// * `var` - Variable for which the value is to be set
127    /// * `addr` - Address of the definition site of the variable
128    /// * `value` - Informed value of the variable
129    ///
130    /// # Example
131    /// ```no_run
132    /// # use binaryninja::medium_level_il::MediumLevelILFunction;
133    /// # use binaryninja::variable::PossibleValueSet;
134    /// # let mlil_fun: MediumLevelILFunction = todo!();
135    /// let user_var_val = mlil_fun.user_var_values().iter().next().unwrap();
136    /// let def_address = user_var_val.def_site.addr;
137    /// let var_value = PossibleValueSet::ConstantValue { value: 5 };
138    /// mlil_fun
139    ///     .set_user_var_value(&user_var_val.variable, def_address, var_value, false)
140    ///     .unwrap();
141    /// ```
142    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    /// Clears a previously defined user variable value.
162    ///
163    /// * `var` - Variable for which the value was informed
164    /// * `def_addr` - Address of the definition site of the variable
165    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            //error "Could not get definition for Variable"
172            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    /// Returns a map of current defined user variable values.
187    /// Returns a Map of user current defined user variable values and their definition sites.
188    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    /// Clear all user defined variable values.
197    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    /// Returns a list of ILReferenceSource objects (IL xrefs or cross-references)
209    /// that reference the given variable. The variable is a local variable that can be either on the stack,
210    /// in a register, or in a flag.
211    /// This function is related to get_hlil_var_refs(), which returns variable references collected
212    /// from HLIL. The two can be different in several cases, e.g., multiple variables in MLIL can be merged
213    /// into a single variable in HLIL.
214    ///
215    /// * `var` - Variable for which to query the xref
216    ///
217    /// # Example
218    /// ```no_run
219    /// # use binaryninja::medium_level_il::MediumLevelILFunction;
220    /// # use binaryninja::variable::Variable;
221    /// # let mlil_fun: MediumLevelILFunction = todo!();
222    /// # let mlil_var: Variable = todo!();
223    /// let instr_addr = mlil_fun.var_refs(&mlil_var).get(0).addr;
224    /// ```
225    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    /// Retrieves variable references from a specified location or range within a medium-level IL function.
236    ///
237    /// Passing in a `length` will query a range for variable references, instead of just the address
238    /// specified in `location`.
239    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    // TODO: Rename to `current_location`?
277    /// Current IL Address
278    pub fn current_address(&self) -> Location {
279        let addr = unsafe { BNMediumLevelILGetCurrentAddress(self.handle) };
280        Location::from(addr)
281    }
282
283    // TODO: Rename to `set_current_location`?
284    /// Set the current IL Address
285    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    /// Returns the [`BasicBlock`] at the given instruction `index`. Function must be finalized.
295    ///
296    /// You can also retrieve this using [`MediumLevelILInstruction::basic_block`].
297    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        // TODO: If we can guarantee self.index is valid we can omit the wrapped Option.
305        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    /// Ends the function and computes the list of basic blocks.
314    ///
315    /// NOTE: This should be called after updating MLIL.
316    pub fn finalize(&self) {
317        unsafe { BNFinalizeMediumLevelILFunction(self.handle) }
318    }
319
320    /// Generate SSA form given the current MLIL.
321    ///
322    /// NOTE: This should be called after updating MLIL.
323    ///
324    /// * `analyze_conditionals` - whether to analyze conditionals
325    /// * `handle_aliases` - whether to handle aliases
326    /// * `non_aliased_vars` - optional list of variables known to be not aliased
327    /// * `aliased_vars` - optional list of variables known to be aliased
328    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    /// Gets the instruction that contains the given SSA variable's definition.
352    ///
353    /// Since SSA variables can only be defined once, this will return the single instruction where that occurs.
354    /// For SSA variable version 0s, which don't have definitions, this will return `None` instead.
355    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        // TODO: Does this return the expression or instruction index? Also we dont diff and this prob doesnt work.
364        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        // TODO: Does this return the expression or instruction index? Also we dont diff and this prob doesnt work.
370        self.instruction_from_index(MediumLevelInstructionIndex(result))
371    }
372
373    /// Gets all the instructions that use the given SSA variable.
374    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    /// Determines if `variable` is live at any point in the function
392    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    /// Computes the list of instructions for which `var` is live.
414    /// If `include_last_use` is false, the last use of the variable will not be included in the
415    /// list (this allows for easier computation of overlaps in liveness between two variables).
416    /// If the variable is never used, this function will return an empty list.
417    ///
418    /// `var` - the variable to query
419    /// `include_last_use` - whether to include the last use of the variable in the list of instructions
420    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    /// This gets just the MLIL variables - you may be interested in the union
450    /// of [`MediumLevelILFunction::aliased_variables`] and [`Function::parameter_variables`] for
451    /// all the variables used in the function
452    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    /// This returns a list of Variables that are taken reference to and used
459    /// elsewhere. You may also wish to consider [`MediumLevelILFunction::variables`]
460    /// and [`Function::parameter_variables`]
461    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    /// This gets the MLIL SSA variables for a given [`Variable`].
468    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            // TODO: We really need to document this better, or have some other facility for this.
590            // NOTE: We take this as a ref to increment the function ref.
591            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}