binaryninja/low_level_il/
function.rs

1// Copyright 2021-2026 Vector 35 Inc.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::fmt::Debug;
16use std::hash::{Hash, Hasher};
17use std::marker::PhantomData;
18
19use binaryninjacore_sys::*;
20
21use crate::architecture::{CoreArchitecture, CoreFlag};
22use crate::basic_block::BasicBlock;
23use crate::function::Function;
24use crate::low_level_il::block::LowLevelILBlock;
25use crate::rc::*;
26use crate::variable::RegisterValue;
27
28use super::*;
29
30#[derive(Copy, Clone, Debug)]
31pub struct Mutable;
32#[derive(Copy, Clone, Debug)]
33pub struct Finalized;
34
35pub trait FunctionMutability: 'static + Debug + Copy {}
36impl FunctionMutability for Mutable {}
37impl FunctionMutability for Finalized {}
38
39#[derive(Copy, Clone, Debug)]
40pub struct SSA;
41#[derive(Copy, Clone, Debug)]
42pub struct NonSSA;
43
44pub trait FunctionForm: 'static + Debug + Copy {}
45impl FunctionForm for SSA {}
46impl FunctionForm for NonSSA {}
47
48pub struct LowLevelILFunction<M: FunctionMutability, F: FunctionForm> {
49    pub(crate) handle: *mut BNLowLevelILFunction,
50    arch: Option<CoreArchitecture>,
51    _mutability: PhantomData<M>,
52    _form: PhantomData<F>,
53}
54
55impl<M, F> LowLevelILFunction<M, F>
56where
57    M: FunctionMutability,
58    F: FunctionForm,
59{
60    pub(crate) unsafe fn from_raw_with_arch(
61        handle: *mut BNLowLevelILFunction,
62        arch: Option<CoreArchitecture>,
63    ) -> Self {
64        debug_assert!(!handle.is_null());
65
66        Self {
67            handle,
68            arch,
69            _mutability: PhantomData,
70            _form: PhantomData,
71        }
72    }
73
74    pub unsafe fn from_raw(handle: *mut BNLowLevelILFunction) -> Self {
75        Self::from_raw_with_arch(handle, None)
76    }
77
78    pub(crate) unsafe fn ref_from_raw_with_arch(
79        handle: *mut BNLowLevelILFunction,
80        arch: Option<CoreArchitecture>,
81    ) -> Ref<Self> {
82        debug_assert!(!handle.is_null());
83        Ref::new(Self::from_raw_with_arch(handle, arch))
84    }
85
86    pub(crate) unsafe fn ref_from_raw(handle: *mut BNLowLevelILFunction) -> Ref<Self> {
87        Self::ref_from_raw_with_arch(handle, None)
88    }
89
90    pub(crate) fn arch(&self) -> CoreArchitecture {
91        // TODO: self.function() can return None under rare circumstances
92        match self.arch {
93            None => self.function().unwrap().arch(),
94            Some(arch) => arch,
95        }
96    }
97
98    pub fn instruction_at<L: Into<Location>>(
99        &self,
100        loc: L,
101    ) -> Option<LowLevelILInstruction<'_, M, F>> {
102        Some(LowLevelILInstruction::new(
103            self,
104            self.instruction_index_at(loc)?,
105        ))
106    }
107
108    /// Get all the instructions for a given location.
109    pub fn instructions_at<L: Into<Location>>(
110        &self,
111        loc: L,
112    ) -> Vec<LowLevelILInstruction<'_, M, F>> {
113        let loc = loc.into();
114        self.instruction_indexes_at(loc)
115            .iter()
116            .map(|idx| LowLevelILInstruction::new(self, idx))
117            .collect()
118    }
119
120    pub fn instruction_index_at<L: Into<Location>>(
121        &self,
122        loc: L,
123    ) -> Option<LowLevelInstructionIndex> {
124        use binaryninjacore_sys::BNLowLevelILGetInstructionStart;
125        let loc: Location = loc.into();
126        // If the location does not specify an architecture, use the function's architecture.
127        let arch = loc.arch.unwrap_or_else(|| self.arch());
128        let instr_idx =
129            unsafe { BNLowLevelILGetInstructionStart(self.handle, arch.handle, loc.addr) };
130        // `instr_idx` will equal self.instruction_count() if the instruction is not valid.
131        if instr_idx >= self.instruction_count() {
132            None
133        } else {
134            Some(LowLevelInstructionIndex(instr_idx))
135        }
136    }
137
138    pub fn instruction_indexes_at<L: Into<Location>>(
139        &self,
140        loc: L,
141    ) -> Array<LowLevelInstructionIndex> {
142        let loc: Location = loc.into();
143        // If the location does not specify an architecture, use the function's architecture.
144        let arch = loc.arch.unwrap_or_else(|| self.arch());
145        let mut count = 0;
146        let indexes = unsafe {
147            BNLowLevelILGetInstructionsAt(self.handle, arch.handle, loc.addr, &mut count)
148        };
149        unsafe { Array::new(indexes, count, ()) }
150    }
151
152    pub fn instruction_from_index(
153        &self,
154        index: LowLevelInstructionIndex,
155    ) -> Option<LowLevelILInstruction<'_, M, F>> {
156        if index.0 >= self.instruction_count() {
157            None
158        } else {
159            Some(LowLevelILInstruction::new(self, index))
160        }
161    }
162
163    pub fn instruction_count(&self) -> usize {
164        unsafe {
165            use binaryninjacore_sys::BNGetLowLevelILInstructionCount;
166            BNGetLowLevelILInstructionCount(self.handle)
167        }
168    }
169
170    pub fn expression_count(&self) -> usize {
171        unsafe {
172            use binaryninjacore_sys::BNGetLowLevelILExprCount;
173            BNGetLowLevelILExprCount(self.handle)
174        }
175    }
176
177    pub fn function(&self) -> Option<Ref<Function>> {
178        unsafe {
179            let func = BNGetLowLevelILOwnerFunction(self.handle);
180            if func.is_null() {
181                return None;
182            }
183            Some(Function::ref_from_raw(func))
184        }
185    }
186
187    pub fn basic_blocks(&self) -> Array<BasicBlock<LowLevelILBlock<'_, M, F>>> {
188        use binaryninjacore_sys::BNGetLowLevelILBasicBlockList;
189
190        unsafe {
191            let mut count = 0;
192            let blocks = BNGetLowLevelILBasicBlockList(self.handle, &mut count);
193            let context = LowLevelILBlock { function: self };
194            Array::new(blocks, count, context)
195        }
196    }
197
198    /// Returns the [`BasicBlock`] at the given instruction `index`.
199    ///
200    /// You can also retrieve this using [`LowLevelILInstruction::basic_block`].
201    pub fn basic_block_containing_index(
202        &self,
203        index: LowLevelInstructionIndex,
204    ) -> Option<Ref<BasicBlock<LowLevelILBlock<'_, M, F>>>> {
205        let block = unsafe { BNGetLowLevelILBasicBlockForInstruction(self.handle, index.0) };
206        if block.is_null() {
207            None
208        } else {
209            Some(unsafe { BasicBlock::ref_from_raw(block, LowLevelILBlock { function: self }) })
210        }
211    }
212
213    pub fn set_indirect_branches(&self, branches: &[Location]) {
214        let mut bn_branches: Box<[BNArchitectureAndAddress]> = branches
215            .iter()
216            .map(|loc| BNArchitectureAndAddress {
217                address: loc.addr,
218                arch: loc.arch.unwrap_or_else(|| self.arch()).handle,
219            })
220            .collect();
221
222        unsafe {
223            BNLowLevelILSetIndirectBranches(self.handle, bn_branches.as_mut_ptr(), branches.len());
224        }
225    }
226}
227
228impl<M: FunctionMutability> LowLevelILFunction<M, NonSSA> {
229    /// Retrieve the SSA form of the function.
230    ///
231    /// If the function has not had the SSA form generated you may call `generate_ssa_form`.
232    pub fn ssa_form(&self) -> Option<Ref<LowLevelILFunction<M, SSA>>> {
233        let handle = unsafe { BNGetLowLevelILSSAForm(self.handle) };
234        if handle.is_null() {
235            return None;
236        }
237        Some(unsafe { LowLevelILFunction::ref_from_raw(handle) })
238    }
239
240    /// Generates the SSA form of the function. Typically called **after** `finalize`.
241    ///
242    /// If you created a freestanding [`LowLevelILFunction`] with no [`LowLevelILFunction::function`]
243    /// than this function will **not** generate the SSA form, as it is currently impossible.
244    ///
245    /// # Example
246    ///
247    /// ```no_run
248    /// use binaryninja::low_level_il::LowLevelILMutableFunction;
249    /// use binaryninja::rc::Ref;
250    /// # let mutable_llil: Ref<LowLevelILMutableFunction> = unimplemented!();
251    /// // ... modify the IL
252    /// let finalized_llil = mutable_llil.finalized();
253    /// finalized_llil.generate_ssa_form();
254    /// ```
255    pub fn generate_ssa_form(&self) {
256        use binaryninjacore_sys::BNGenerateLowLevelILSSAForm;
257        // SSA form may only be generated if there is an owning function, otherwise it will crash.
258        if self.function().is_some() {
259            unsafe { BNGenerateLowLevelILSSAForm(self.handle) };
260        }
261    }
262}
263
264// Allow instantiating Lifted IL functions for querying Lifted IL from Architectures
265impl LowLevelILFunction<Mutable, NonSSA> {
266    // TODO: Document what happens when you pass None for `source_func`.
267    // TODO: Doing so would construct a LowLevelILFunction with no basic blocks
268    // TODO: Document why you would want to do that.
269    pub fn new(arch: CoreArchitecture, source_func: Option<Function>) -> Ref<Self> {
270        use binaryninjacore_sys::BNCreateLowLevelILFunction;
271
272        let handle = unsafe {
273            match source_func {
274                Some(func) => BNCreateLowLevelILFunction(arch.handle, func.handle),
275                None => BNCreateLowLevelILFunction(arch.handle, std::ptr::null_mut()),
276            }
277        };
278
279        // BNCreateLowLevelILFunction should always return a valid object.
280        assert!(!handle.is_null());
281
282        unsafe { Self::ref_from_raw_with_arch(handle, Some(arch)) }
283    }
284}
285
286impl Ref<LowLevelILFunction<Mutable, NonSSA>> {
287    /// Finalize the mutated [`LowLevelILFunction`], returning a [`LowLevelILRegularFunction`].
288    ///
289    /// This function **will not** correct the SSA related dataflow, to do that you must call
290    /// the function [`LowLevelILMutableFunction::generate_ssa_form`].
291    ///
292    /// # Example
293    ///
294    /// ```no_run
295    /// use binaryninja::low_level_il::LowLevelILMutableFunction;
296    /// use binaryninja::rc::Ref;
297    /// # let mutable_llil: Ref<LowLevelILMutableFunction> = unimplemented!();
298    /// // ... modify the IL
299    /// let finalized_llil = mutable_llil.finalized();
300    /// finalized_llil.generate_ssa_form();
301    /// ```
302    pub fn finalized(self) -> Ref<LowLevelILFunction<Finalized, NonSSA>> {
303        unsafe {
304            BNFinalizeLowLevelILFunction(self.handle);
305            // Now that we have finalized return the function as is so the caller can reference the "finalized function".
306            LowLevelILFunction::from_raw_with_arch(self.handle, self.arch).to_owned()
307        }
308    }
309}
310
311impl<M: FunctionMutability> LowLevelILFunction<M, SSA> {
312    /// Return a vector of all instructions that use the given SSA register.
313    #[must_use]
314    pub fn get_ssa_register_uses<R: ArchReg>(
315        &self,
316        reg: impl AsRef<LowLevelILSSARegister<R>>,
317    ) -> Vec<LowLevelILInstruction<'_, M, SSA>> {
318        use binaryninjacore_sys::BNGetLowLevelILSSARegisterUses;
319        let reg = reg.as_ref();
320        let mut count = 0;
321        let instrs = unsafe {
322            BNGetLowLevelILSSARegisterUses(
323                self.handle,
324                reg.id().into(),
325                reg.version as usize,
326                &mut count,
327            )
328        };
329        let result = unsafe { std::slice::from_raw_parts(instrs, count) }
330            .iter()
331            .map(|idx| LowLevelILInstruction::new(self, LowLevelInstructionIndex(*idx)))
332            .collect();
333        unsafe { BNFreeILInstructionList(instrs) };
334        result
335    }
336
337    /// Returns the instruction that defines the given SSA register.
338    #[must_use]
339    pub fn get_ssa_register_definition<R: ArchReg>(
340        &self,
341        reg: impl AsRef<LowLevelILSSARegister<R>>,
342    ) -> Option<LowLevelILInstruction<'_, M, SSA>> {
343        use binaryninjacore_sys::BNGetLowLevelILSSARegisterDefinition;
344        let reg = reg.as_ref();
345        let instr_idx = unsafe {
346            BNGetLowLevelILSSARegisterDefinition(self.handle, reg.id().into(), reg.version as usize)
347        };
348        self.instruction_from_index(LowLevelInstructionIndex(instr_idx))
349    }
350
351    /// Returns the value of the given SSA register.
352    #[must_use]
353    pub fn get_ssa_register_value<R: ArchReg>(
354        &self,
355        reg: impl AsRef<LowLevelILSSARegister<R>>,
356    ) -> Option<RegisterValue> {
357        let reg = reg.as_ref();
358        let value = unsafe {
359            BNGetLowLevelILSSARegisterValue(self.handle, reg.id().into(), reg.version as usize)
360        };
361        if value.state == BNRegisterValueType::UndeterminedValue {
362            return None;
363        }
364        Some(value.into())
365    }
366
367    /// Returns the value of the given SSA flag.
368    #[must_use]
369    pub fn get_ssa_flag_value(&self, flag: &LowLevelILSSAFlag<CoreFlag>) -> Option<RegisterValue> {
370        let value = unsafe {
371            BNGetLowLevelILSSAFlagValue(self.handle, flag.flag.id().0, flag.version as usize)
372        };
373        if value.state == BNRegisterValueType::UndeterminedValue {
374            return None;
375        }
376        Some(value.into())
377    }
378}
379
380impl<M, F> ToOwned for LowLevelILFunction<M, F>
381where
382    M: FunctionMutability,
383    F: FunctionForm,
384{
385    type Owned = Ref<Self>;
386
387    fn to_owned(&self) -> Self::Owned {
388        unsafe { RefCountable::inc_ref(self) }
389    }
390}
391
392unsafe impl<M, F> RefCountable for LowLevelILFunction<M, F>
393where
394    M: FunctionMutability,
395    F: FunctionForm,
396{
397    unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
398        Ref::new(Self {
399            handle: BNNewLowLevelILFunctionReference(handle.handle),
400            arch: handle.arch,
401            _mutability: PhantomData,
402            _form: PhantomData,
403        })
404    }
405
406    unsafe fn dec_ref(handle: &Self) {
407        BNFreeLowLevelILFunction(handle.handle);
408    }
409}
410
411impl<M, F> Debug for LowLevelILFunction<M, F>
412where
413    M: FunctionMutability,
414    F: FunctionForm,
415{
416    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
417        f.debug_struct("LowLevelILFunction")
418            .field("arch", &self.arch())
419            .field("instruction_count", &self.instruction_count())
420            .field("expression_count", &self.expression_count())
421            .finish()
422    }
423}
424
425unsafe impl<M: FunctionMutability, F: FunctionForm> Send for LowLevelILFunction<M, F> {}
426unsafe impl<M: FunctionMutability, F: FunctionForm> Sync for LowLevelILFunction<M, F> {}
427
428impl<M: FunctionMutability, F: FunctionForm> Eq for LowLevelILFunction<M, F> {}
429
430impl<M: FunctionMutability, F: FunctionForm> PartialEq for LowLevelILFunction<M, F> {
431    fn eq(&self, rhs: &Self) -> bool {
432        self.function().eq(&rhs.function())
433    }
434}
435
436impl<M: FunctionMutability, F: FunctionForm> Hash for LowLevelILFunction<M, F> {
437    fn hash<H: Hasher>(&self, state: &mut H) {
438        self.function().hash(state)
439    }
440}