Skip to main content

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        self.instruction_from_index(MediumLevelInstructionIndex(result))
364    }
365
366    pub fn ssa_memory_definition(&self, version: usize) -> Option<MediumLevelILInstruction> {
367        let result = unsafe { BNGetMediumLevelILSSAMemoryDefinition(self.handle, version) };
368        self.instruction_from_index(MediumLevelInstructionIndex(result))
369    }
370
371    /// Gets all the instructions that use the given SSA variable.
372    pub fn ssa_variable_uses(&self, ssa_variable: &SSAVariable) -> Array<MediumLevelILInstruction> {
373        let mut count = 0;
374        let raw_var = BNVariable::from(ssa_variable.variable);
375        let uses = unsafe {
376            BNGetMediumLevelILSSAVarUses(self.handle, &raw_var, ssa_variable.version, &mut count)
377        };
378        assert!(!uses.is_null());
379        unsafe { Array::new(uses, count, self.to_owned()) }
380    }
381
382    pub fn ssa_memory_uses(&self, version: usize) -> Array<MediumLevelILInstruction> {
383        let mut count = 0;
384        let uses = unsafe { BNGetMediumLevelILSSAMemoryUses(self.handle, version, &mut count) };
385        assert!(!uses.is_null());
386        unsafe { Array::new(uses, count, self.to_owned()) }
387    }
388
389    /// Determines if `variable` is live at any point in the function
390    pub fn is_ssa_variable_live(&self, ssa_variable: &SSAVariable) -> bool {
391        let raw_var = BNVariable::from(ssa_variable.variable);
392        unsafe { BNIsMediumLevelILSSAVarLive(self.handle, &raw_var, ssa_variable.version) }
393    }
394
395    pub fn variable_definitions(&self, variable: &Variable) -> Array<MediumLevelILInstruction> {
396        let mut count = 0;
397        let raw_var = BNVariable::from(variable);
398        let defs =
399            unsafe { BNGetMediumLevelILVariableDefinitions(self.handle, &raw_var, &mut count) };
400        assert!(!defs.is_null());
401        unsafe { Array::new(defs, count, self.to_owned()) }
402    }
403
404    pub fn variable_uses(&self, variable: &Variable) -> Array<MediumLevelILInstruction> {
405        let mut count = 0;
406        let raw_var = BNVariable::from(variable);
407        let uses = unsafe { BNGetMediumLevelILVariableUses(self.handle, &raw_var, &mut count) };
408        unsafe { Array::new(uses, count, self.to_owned()) }
409    }
410
411    /// Computes the list of instructions for which `var` is live.
412    /// If `include_last_use` is false, the last use of the variable will not be included in the
413    /// list (this allows for easier computation of overlaps in liveness between two variables).
414    /// If the variable is never used, this function will return an empty list.
415    ///
416    /// `var` - the variable to query
417    /// `include_last_use` - whether to include the last use of the variable in the list of instructions
418    pub fn live_instruction_for_variable(
419        &self,
420        variable: &Variable,
421        include_last_use: bool,
422    ) -> Array<MediumLevelILInstruction> {
423        let mut count = 0;
424        let raw_var = BNVariable::from(variable);
425        let uses = unsafe {
426            BNGetMediumLevelILLiveInstructionsForVariable(
427                self.handle,
428                &raw_var,
429                include_last_use,
430                &mut count,
431            )
432        };
433        unsafe { Array::new(uses, count, self.to_owned()) }
434    }
435
436    pub fn ssa_variable_value(&self, ssa_variable: &SSAVariable) -> RegisterValue {
437        let raw_var = BNVariable::from(ssa_variable.variable);
438        unsafe { BNGetMediumLevelILSSAVarValue(self.handle, &raw_var, ssa_variable.version) }.into()
439    }
440
441    pub fn create_graph(&self, settings: Option<DisassemblySettings>) -> Ref<FlowGraph> {
442        let settings = settings.map(|x| x.handle).unwrap_or(std::ptr::null_mut());
443        let graph = unsafe { BNCreateMediumLevelILFunctionGraph(self.handle, settings) };
444        unsafe { FlowGraph::ref_from_raw(graph) }
445    }
446
447    /// This gets just the MLIL variables - you may be interested in the union
448    /// of [`MediumLevelILFunction::aliased_variables`] and [`Function::parameter_variables`] for
449    /// all the variables used in the function
450    pub fn variables(&self) -> Array<Variable> {
451        let mut count = 0;
452        let uses = unsafe { BNGetMediumLevelILVariables(self.handle, &mut count) };
453        unsafe { Array::new(uses, count, ()) }
454    }
455
456    /// This returns a list of Variables that are taken reference to and used
457    /// elsewhere. You may also wish to consider [`MediumLevelILFunction::variables`]
458    /// and [`Function::parameter_variables`]
459    pub fn aliased_variables(&self) -> Array<Variable> {
460        let mut count = 0;
461        let uses = unsafe { BNGetMediumLevelILAliasedVariables(self.handle, &mut count) };
462        unsafe { Array::new(uses, count, ()) }
463    }
464
465    /// This gets the MLIL SSA variables for a given [`Variable`].
466    pub fn ssa_variables(&self, variable: &Variable) -> Array<SSAVariable> {
467        let mut count = 0;
468        let raw_variable = BNVariable::from(variable);
469        let versions = unsafe {
470            BNGetMediumLevelILVariableSSAVersions(self.handle, &raw_variable, &mut count)
471        };
472        unsafe { Array::new(versions, count, *variable) }
473    }
474}
475
476impl ToOwned for MediumLevelILFunction {
477    type Owned = Ref<Self>;
478
479    fn to_owned(&self) -> Self::Owned {
480        unsafe { RefCountable::inc_ref(self) }
481    }
482}
483
484unsafe impl RefCountable for MediumLevelILFunction {
485    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
486        Ref::new(Self {
487            handle: BNNewMediumLevelILFunctionReference(handle.handle),
488        })
489    }
490
491    unsafe fn dec_ref(handle: &Self) {
492        BNFreeMediumLevelILFunction(handle.handle);
493    }
494}
495
496impl Debug for MediumLevelILFunction {
497    fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
498        f.debug_struct("MediumLevelILFunction")
499            .field("arch", &self.function().arch())
500            .field("instruction_count", &self.instruction_count())
501            .finish()
502    }
503}
504
505unsafe impl Send for MediumLevelILFunction {}
506unsafe impl Sync for MediumLevelILFunction {}
507
508impl Eq for MediumLevelILFunction {}
509impl PartialEq for MediumLevelILFunction {
510    fn eq(&self, rhs: &Self) -> bool {
511        self.function().eq(&rhs.function())
512    }
513}
514
515impl Hash for MediumLevelILFunction {
516    fn hash<H: Hasher>(&self, state: &mut H) {
517        self.function().hash(state)
518    }
519}
520
521pub struct ILReferenceSource {
522    pub function: Ref<Function>,
523    pub arch: CoreArchitecture,
524    pub addr: u64,
525    pub graph_type: FunctionGraphType,
526    pub instr_idx: MediumLevelInstructionIndex,
527}
528
529impl From<BNILReferenceSource> for ILReferenceSource {
530    fn from(value: BNILReferenceSource) -> Self {
531        Self {
532            function: unsafe { Function::ref_from_raw(value.func) },
533            arch: unsafe { CoreArchitecture::from_raw(value.arch) },
534            addr: value.addr,
535            graph_type: value.type_,
536            instr_idx: MediumLevelInstructionIndex(value.exprId),
537        }
538    }
539}
540
541impl From<&BNILReferenceSource> for ILReferenceSource {
542    fn from(value: &BNILReferenceSource) -> Self {
543        Self {
544            function: unsafe { Function::from_raw(value.func).to_owned() },
545            arch: unsafe { CoreArchitecture::from_raw(value.arch) },
546            addr: value.addr,
547            graph_type: value.type_,
548            instr_idx: MediumLevelInstructionIndex(value.exprId),
549        }
550    }
551}
552
553impl CoreArrayProvider for ILReferenceSource {
554    type Raw = BNILReferenceSource;
555    type Context = ();
556    type Wrapped<'a> = Self;
557}
558
559unsafe impl CoreArrayProviderInner for ILReferenceSource {
560    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
561        BNFreeILReferences(raw, count)
562    }
563
564    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
565        raw.into()
566    }
567}
568
569pub struct VariableReferenceSource {
570    pub variable: Variable,
571    pub source: ILReferenceSource,
572}
573
574impl From<BNVariableReferenceSource> for VariableReferenceSource {
575    fn from(value: BNVariableReferenceSource) -> Self {
576        Self {
577            variable: Variable::from(value.var),
578            source: value.source.into(),
579        }
580    }
581}
582
583impl From<&BNVariableReferenceSource> for VariableReferenceSource {
584    fn from(value: &BNVariableReferenceSource) -> Self {
585        Self {
586            variable: Variable::from(value.var),
587            // TODO: We really need to document this better, or have some other facility for this.
588            // NOTE: We take this as a ref to increment the function ref.
589            source: ILReferenceSource::from(&value.source),
590        }
591    }
592}
593
594impl CoreArrayProvider for VariableReferenceSource {
595    type Raw = BNVariableReferenceSource;
596    type Context = ();
597    type Wrapped<'a> = Self;
598}
599
600unsafe impl CoreArrayProviderInner for VariableReferenceSource {
601    unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
602        BNFreeVariableReferenceSourceList(raw, count)
603    }
604
605    unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
606        raw.into()
607    }
608}