binaryninja/
low_level_il.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::borrow::Cow;
16use std::collections::HashSet;
17use std::fmt;
18use std::fmt::{Debug, Display};
19// TODO : provide some way to forbid emitting register reads for certain registers
20// also writing for certain registers (e.g. zero register must prohibit il.set_reg and il.reg
21// (replace with nop or const(0) respectively)
22// requirements on load/store memory address sizes?
23// can reg/set_reg be used with sizes that differ from what is in BNRegisterInfo?
24
25use crate::architecture::{Architecture, Flag, RegisterId};
26use crate::architecture::{CoreRegister, Register as ArchReg};
27use crate::function::Location;
28
29pub mod block;
30pub mod expression;
31pub mod function;
32pub mod instruction;
33pub mod lifting;
34pub mod operation;
35
36use self::expression::*;
37use self::function::*;
38use self::instruction::*;
39
40/// Regular low-level IL, if you are not modifying the functions IL or needing SSA, use this.
41pub type LowLevelILRegularFunction = LowLevelILFunction<Finalized, NonSSA>;
42pub type LowLevelILRegularInstruction<'a> = LowLevelILInstruction<'a, Finalized, NonSSA>;
43pub type LowLevelILRegularInstructionKind<'a> = LowLevelILInstructionKind<'a, Finalized, NonSSA>;
44pub type LowLevelILRegularExpression<'a, ReturnType> =
45    LowLevelILExpression<'a, Finalized, NonSSA, ReturnType>;
46pub type LowLevelILRegularExpressionKind<'a> = LowLevelILExpressionKind<'a, Finalized, NonSSA>;
47
48/// Mutable low-level IL, used when lifting in architectures and modifying IL in workflow activities.
49pub type LowLevelILMutableFunction = LowLevelILFunction<Mutable, NonSSA>;
50pub type LowLevelILMutableExpression<'a, ReturnType> =
51    LowLevelILExpression<'a, Mutable, NonSSA, ReturnType>;
52
53/// SSA Variant of low-level IL, this can never be mutated directly.
54pub type LowLevelILSSAFunction = LowLevelILFunction<Finalized, SSA>;
55
56#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
57pub struct LowLevelILTempRegister {
58    /// The temporary id for the register, this will **NOT** be the referenced id in the core.
59    ///
60    /// Do not attempt to pass this to the core. Use [`LowLevelILTempRegister::id`] instead.
61    temp_id: RegisterId,
62}
63
64impl LowLevelILTempRegister {
65    pub fn new(temp_id: u32) -> Self {
66        Self {
67            temp_id: RegisterId(temp_id),
68        }
69    }
70
71    pub fn from_id(id: RegisterId) -> Option<Self> {
72        match id.is_temporary() {
73            true => {
74                let temp_id = RegisterId(id.0 & 0x7fff_ffff);
75                Some(Self { temp_id })
76            }
77            false => None,
78        }
79    }
80
81    /// The temporary registers core id, with the temporary bit set.
82    pub fn id(&self) -> RegisterId {
83        RegisterId(self.temp_id.0 | 0x8000_0000)
84    }
85}
86
87impl Debug for LowLevelILTempRegister {
88    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
89        write!(f, "temp{}", self.temp_id)
90    }
91}
92
93impl Display for LowLevelILTempRegister {
94    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
95        Debug::fmt(self, f)
96    }
97}
98
99impl TryFrom<RegisterId> for LowLevelILTempRegister {
100    type Error = ();
101
102    fn try_from(value: RegisterId) -> Result<Self, Self::Error> {
103        Self::from_id(value).ok_or(())
104    }
105}
106
107impl From<u32> for LowLevelILTempRegister {
108    fn from(value: u32) -> Self {
109        Self::new(value)
110    }
111}
112
113#[derive(Copy, Clone, PartialEq, Eq)]
114pub enum LowLevelILRegisterKind<R: ArchReg> {
115    Arch(R),
116    Temp(LowLevelILTempRegister),
117}
118
119impl<R: ArchReg> LowLevelILRegisterKind<R> {
120    pub fn from_raw(arch: &impl Architecture<Register = R>, val: RegisterId) -> Option<Self> {
121        match val.is_temporary() {
122            true => {
123                let temp_reg = LowLevelILTempRegister::from_id(val)?;
124                Some(LowLevelILRegisterKind::Temp(temp_reg))
125            }
126            false => {
127                let arch_reg = arch.register_from_id(val)?;
128                Some(LowLevelILRegisterKind::Arch(arch_reg))
129            }
130        }
131    }
132
133    pub fn from_temp(temp: impl Into<LowLevelILTempRegister>) -> Self {
134        LowLevelILRegisterKind::Temp(temp.into())
135    }
136
137    pub fn id(&self) -> RegisterId {
138        match *self {
139            LowLevelILRegisterKind::Arch(ref r) => r.id(),
140            LowLevelILRegisterKind::Temp(temp) => temp.id(),
141        }
142    }
143
144    pub fn name(&self) -> Cow<'_, str> {
145        match *self {
146            LowLevelILRegisterKind::Arch(ref r) => r.name(),
147            LowLevelILRegisterKind::Temp(temp) => Cow::Owned(format!("temp{}", temp.temp_id)),
148        }
149    }
150}
151
152impl<R: ArchReg> Debug for LowLevelILRegisterKind<R> {
153    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
154        match *self {
155            LowLevelILRegisterKind::Arch(ref r) => r.fmt(f),
156            LowLevelILRegisterKind::Temp(ref id) => Debug::fmt(id, f),
157        }
158    }
159}
160
161impl<R: ArchReg> Display for LowLevelILRegisterKind<R> {
162    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163        match *self {
164            LowLevelILRegisterKind::Arch(ref r) => write!(f, "{}", r.name()),
165            LowLevelILRegisterKind::Temp(ref id) => Display::fmt(id, f),
166        }
167    }
168}
169
170impl From<LowLevelILTempRegister> for LowLevelILRegisterKind<CoreRegister> {
171    fn from(reg: LowLevelILTempRegister) -> Self {
172        LowLevelILRegisterKind::Temp(reg)
173    }
174}
175
176#[derive(Copy, Clone, Debug)]
177pub struct LowLevelILSSARegister<R: ArchReg> {
178    pub reg: LowLevelILRegisterKind<R>,
179    /// The SSA version of the register.
180    pub version: u32,
181}
182
183impl<R: ArchReg> LowLevelILSSARegister<R> {
184    pub fn new(reg: LowLevelILRegisterKind<R>, version: u32) -> Self {
185        Self { reg, version }
186    }
187
188    pub fn name(&self) -> Cow<'_, str> {
189        self.reg.name()
190    }
191
192    pub fn id(&self) -> RegisterId {
193        self.reg.id()
194    }
195}
196
197impl<R: ArchReg> Display for LowLevelILSSARegister<R> {
198    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
199        write!(f, "{}#{}", self.reg, self.version)
200    }
201}
202
203/// The kind of SSA register.
204///
205/// An SSA register can exist in two states:
206///
207/// - Full, e.g. `eax` on x86
208/// - Partial, e.g. `al` on x86
209///
210/// If you intend to query for the ssa uses or definitions you must retrieve the physical register
211/// using the function [`LowLevelILSSARegisterKind::physical_reg`] which will give you the actual
212/// [`LowLevelILSSARegister`].
213#[derive(Copy, Clone, Debug)]
214pub enum LowLevelILSSARegisterKind<R: ArchReg> {
215    /// A full register is one that is not aliasing another, such as `eax` on x86 or `rax` on x86_64.
216    Full(LowLevelILSSARegister<R>),
217    Partial {
218        /// This is the non-aliased register.
219        ///
220        /// This register is what is used for dataflow, otherwise the backing storage of aliased registers
221        /// like `al` on x86 would contain separate value information from the physical register `eax`.
222        ///
223        /// NOTE: While this is a [`LowLevelILSSARegister`] temporary registers are not allowed in partial
224        /// assignments, so this will always be an actual architecture register.
225        full_reg: LowLevelILSSARegister<R>,
226        /// This is the aliased register.
227        ///
228        /// On x86 if the register `al` is used that would be considered a partial register, with the
229        /// full register `eax` being used as the backing storage.
230        partial_reg: CoreRegister,
231    },
232}
233
234impl<R: ArchReg> LowLevelILSSARegisterKind<R> {
235    pub fn new_full(kind: LowLevelILRegisterKind<R>, version: u32) -> Self {
236        Self::Full(LowLevelILSSARegister::new(kind, version))
237    }
238
239    pub fn new_partial(
240        full_reg: LowLevelILRegisterKind<R>,
241        version: u32,
242        partial_reg: CoreRegister,
243    ) -> Self {
244        Self::Partial {
245            full_reg: LowLevelILSSARegister::new(full_reg, version),
246            partial_reg,
247        }
248    }
249
250    /// This is the non-aliased register used. This should be called when you intend to actually
251    /// query for SSA dataflow information, as a partial register is prohibited from being used.
252    ///
253    /// # Example
254    ///
255    /// On x86 `al` in the LLIL SSA will have a physical register of `eax`.
256    pub fn physical_reg(&self) -> LowLevelILSSARegister<R> {
257        match *self {
258            LowLevelILSSARegisterKind::Full(reg) => reg,
259            LowLevelILSSARegisterKind::Partial { full_reg, .. } => full_reg,
260        }
261    }
262
263    /// Gets the displayable register, for partial this will be the partial register name.
264    ///
265    /// # Example
266    ///
267    /// On x86 this will display "al" not "eax".
268    pub fn name(&self) -> Cow<'_, str> {
269        match *self {
270            LowLevelILSSARegisterKind::Full(ref reg) => reg.reg.name(),
271            LowLevelILSSARegisterKind::Partial {
272                ref partial_reg, ..
273            } => partial_reg.name(),
274        }
275    }
276}
277
278impl<R: ArchReg> AsRef<LowLevelILSSARegister<R>> for LowLevelILSSARegisterKind<R> {
279    fn as_ref(&self) -> &LowLevelILSSARegister<R> {
280        match self {
281            LowLevelILSSARegisterKind::Full(reg) => reg,
282            LowLevelILSSARegisterKind::Partial { full_reg, .. } => full_reg,
283        }
284    }
285}
286
287impl<R: ArchReg> From<LowLevelILSSARegister<R>> for LowLevelILSSARegisterKind<R> {
288    fn from(value: LowLevelILSSARegister<R>) -> Self {
289        LowLevelILSSARegisterKind::Full(value)
290    }
291}
292
293impl<R: ArchReg> From<LowLevelILSSARegisterKind<R>> for LowLevelILSSARegister<R> {
294    fn from(value: LowLevelILSSARegisterKind<R>) -> Self {
295        match value {
296            LowLevelILSSARegisterKind::Full(reg) => reg,
297            LowLevelILSSARegisterKind::Partial { full_reg, .. } => full_reg,
298        }
299    }
300}
301
302#[derive(Copy, Clone, Debug)]
303pub struct LowLevelILSSAFlag<F: Flag> {
304    pub flag: F,
305    pub version: u32,
306}
307
308impl<F: Flag> LowLevelILSSAFlag<F> {
309    pub fn new(flag: F, version: u32) -> Self {
310        Self { flag, version }
311    }
312}
313
314#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
315pub enum VisitorAction {
316    Descend,
317    Sibling,
318    Halt,
319}
320
321#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
322pub enum ILInstructionAttribute {
323    ILAllowDeadStoreElimination,
324    ILPreventDeadStoreElimination,
325    MLILAssumePossibleUse,
326    MLILUnknownSize,
327    SrcInstructionUsesPointerAuth,
328    ILPreventAliasAnalysis,
329    ILIsCFGProtected,
330    MLILPossiblyUnusedIntermediate,
331    HLILFoldableExpr,
332    HLILInvertableCondition,
333    HLILEarlyReturnPossible,
334    HLILSwitchRecoveryPossible,
335    ILTransparentCopy,
336}
337
338impl ILInstructionAttribute {
339    pub fn value(&self) -> u32 {
340        match self {
341            Self::ILAllowDeadStoreElimination => 1,
342            Self::ILPreventDeadStoreElimination => 2,
343            Self::MLILAssumePossibleUse => 4,
344            Self::MLILUnknownSize => 8,
345            Self::SrcInstructionUsesPointerAuth => 16,
346            Self::ILPreventAliasAnalysis => 32,
347            Self::ILIsCFGProtected => 64,
348            Self::MLILPossiblyUnusedIntermediate => 128,
349            Self::HLILFoldableExpr => 256,
350            Self::HLILInvertableCondition => 512,
351            Self::HLILEarlyReturnPossible => 1024,
352            Self::HLILSwitchRecoveryPossible => 2048,
353            Self::ILTransparentCopy => 4096,
354        }
355    }
356}
357
358pub type ILInstructionAttributeSet = HashSet<ILInstructionAttribute>;