Skip to content

Commit 59b73aa

Browse files
authored
[Rust] Add LowLevelILSSARegister type (#7745)
1 parent 0ec8e7d commit 59b73aa

File tree

4 files changed

+168
-51
lines changed

4 files changed

+168
-51
lines changed

rust/src/low_level_il.rs

Lines changed: 126 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414

1515
use std::borrow::Cow;
1616
use std::fmt;
17-
17+
use std::fmt::{Debug, Display};
1818
// TODO : provide some way to forbid emitting register reads for certain registers
1919
// also writing for certain registers (e.g. zero register must prohibit il.set_reg and il.reg
2020
// (replace with nop or const(0) respectively)
@@ -83,12 +83,18 @@ impl LowLevelILTempRegister {
8383
}
8484
}
8585

86-
impl fmt::Debug for LowLevelILTempRegister {
87-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
86+
impl Debug for LowLevelILTempRegister {
87+
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
8888
write!(f, "temp{}", self.temp_id)
8989
}
9090
}
9191

92+
impl Display for LowLevelILTempRegister {
93+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
94+
Debug::fmt(self, f)
95+
}
96+
}
97+
9298
impl TryFrom<RegisterId> for LowLevelILTempRegister {
9399
type Error = ();
94100

@@ -142,11 +148,20 @@ impl<R: ArchReg> LowLevelILRegisterKind<R> {
142148
}
143149
}
144150

145-
impl<R: ArchReg> fmt::Debug for LowLevelILRegisterKind<R> {
146-
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
151+
impl<R: ArchReg> Debug for LowLevelILRegisterKind<R> {
152+
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
147153
match *self {
148154
LowLevelILRegisterKind::Arch(ref r) => r.fmt(f),
149-
LowLevelILRegisterKind::Temp(id) => id.fmt(f),
155+
LowLevelILRegisterKind::Temp(ref id) => Debug::fmt(id, f),
156+
}
157+
}
158+
}
159+
160+
impl<R: ArchReg> Display for LowLevelILRegisterKind<R> {
161+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
162+
match *self {
163+
LowLevelILRegisterKind::Arch(ref r) => write!(f, "{}", r.name()),
164+
LowLevelILRegisterKind::Temp(ref id) => Display::fmt(id, f),
150165
}
151166
}
152167
}
@@ -157,36 +172,128 @@ impl From<LowLevelILTempRegister> for LowLevelILRegisterKind<CoreRegister> {
157172
}
158173
}
159174

175+
#[derive(Copy, Clone, Debug)]
176+
pub struct LowLevelILSSARegister<R: ArchReg> {
177+
pub reg: LowLevelILRegisterKind<R>,
178+
/// The SSA version of the register.
179+
pub version: u32,
180+
}
181+
182+
impl<R: ArchReg> LowLevelILSSARegister<R> {
183+
pub fn new(reg: LowLevelILRegisterKind<R>, version: u32) -> Self {
184+
Self { reg, version }
185+
}
186+
187+
pub fn name(&self) -> Cow<'_, str> {
188+
self.reg.name()
189+
}
190+
191+
pub fn id(&self) -> RegisterId {
192+
self.reg.id()
193+
}
194+
}
195+
196+
impl<R: ArchReg> Display for LowLevelILSSARegister<R> {
197+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
198+
write!(f, "{}#{}", self.reg, self.version)
199+
}
200+
}
201+
202+
/// The kind of SSA register.
203+
///
204+
/// An SSA register can exist in two states:
205+
///
206+
/// - Full, e.g. `eax` on x86
207+
/// - Partial, e.g. `al` on x86
208+
///
209+
/// If you intend to query for the ssa uses or definitions you must retrieve the physical register
210+
/// using the function [`LowLevelILSSARegisterKind::physical_reg`] which will give you the actual
211+
/// [`LowLevelILSSARegister`].
160212
#[derive(Copy, Clone, Debug)]
161213
pub enum LowLevelILSSARegisterKind<R: ArchReg> {
162-
Full {
163-
kind: LowLevelILRegisterKind<R>,
164-
version: u32,
165-
},
214+
/// A full register is one that is not aliasing another, such as `eax` on x86 or `rax` on x86_64.
215+
Full(LowLevelILSSARegister<R>),
166216
Partial {
167-
full_reg: CoreRegister,
217+
/// This is the non-aliased register.
218+
///
219+
/// This register is what is used for dataflow, otherwise the backing storage of aliased registers
220+
/// like `al` on x86 would contain separate value information from the physical register `eax`.
221+
///
222+
/// NOTE: While this is a [`LowLevelILSSARegister`] temporary registers are not allowed in partial
223+
/// assignments, so this will always be an actual architecture register.
224+
full_reg: LowLevelILSSARegister<R>,
225+
/// This is the aliased register.
226+
///
227+
/// On x86 if the register `al` is used that would be considered a partial register, with the
228+
/// full register `eax` being used as the backing storage.
168229
partial_reg: CoreRegister,
169-
version: u32,
170230
},
171231
}
172232

173233
impl<R: ArchReg> LowLevelILSSARegisterKind<R> {
174234
pub fn new_full(kind: LowLevelILRegisterKind<R>, version: u32) -> Self {
175-
Self::Full { kind, version }
235+
Self::Full(LowLevelILSSARegister::new(kind, version))
176236
}
177237

178-
pub fn new_partial(full_reg: CoreRegister, partial_reg: CoreRegister, version: u32) -> Self {
238+
pub fn new_partial(
239+
full_reg: LowLevelILRegisterKind<R>,
240+
version: u32,
241+
partial_reg: CoreRegister,
242+
) -> Self {
179243
Self::Partial {
180-
full_reg,
244+
full_reg: LowLevelILSSARegister::new(full_reg, version),
181245
partial_reg,
182-
version,
183246
}
184247
}
185248

186-
pub fn version(&self) -> u32 {
249+
/// This is the non-aliased register used. This should be called when you intend to actually
250+
/// query for SSA dataflow information, as a partial register is prohibited from being used.
251+
///
252+
/// # Example
253+
///
254+
/// On x86 `al` in the LLIL SSA will have a physical register of `eax`.
255+
pub fn physical_reg(&self) -> LowLevelILSSARegister<R> {
187256
match *self {
188-
LowLevelILSSARegisterKind::Full { version, .. }
189-
| LowLevelILSSARegisterKind::Partial { version, .. } => version,
257+
LowLevelILSSARegisterKind::Full(reg) => reg,
258+
LowLevelILSSARegisterKind::Partial { full_reg, .. } => full_reg,
259+
}
260+
}
261+
262+
/// Gets the displayable register, for partial this will be the partial register name.
263+
///
264+
/// # Example
265+
///
266+
/// On x86 this will display "al" not "eax".
267+
pub fn name(&self) -> Cow<'_, str> {
268+
match *self {
269+
LowLevelILSSARegisterKind::Full(ref reg) => reg.reg.name(),
270+
LowLevelILSSARegisterKind::Partial {
271+
ref partial_reg, ..
272+
} => partial_reg.name(),
273+
}
274+
}
275+
}
276+
277+
impl<R: ArchReg> AsRef<LowLevelILSSARegister<R>> for LowLevelILSSARegisterKind<R> {
278+
fn as_ref(&self) -> &LowLevelILSSARegister<R> {
279+
match self {
280+
LowLevelILSSARegisterKind::Full(reg) => reg,
281+
LowLevelILSSARegisterKind::Partial { full_reg, .. } => full_reg,
282+
}
283+
}
284+
}
285+
286+
impl<R: ArchReg> From<LowLevelILSSARegister<R>> for LowLevelILSSARegisterKind<R> {
287+
fn from(value: LowLevelILSSARegister<R>) -> Self {
288+
LowLevelILSSARegisterKind::Full(value)
289+
}
290+
}
291+
292+
impl<R: ArchReg> From<LowLevelILSSARegisterKind<R>> for LowLevelILSSARegister<R> {
293+
fn from(value: LowLevelILSSARegisterKind<R>) -> Self {
294+
match value {
295+
LowLevelILSSARegisterKind::Full(reg) => reg,
296+
LowLevelILSSARegisterKind::Partial { full_reg, .. } => full_reg,
190297
}
191298
}
192299
}

rust/src/low_level_il/function.rs

Lines changed: 10 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -299,19 +299,16 @@ impl<M: FunctionMutability> LowLevelILFunction<M, SSA> {
299299
#[must_use]
300300
pub fn get_ssa_register_uses<R: ArchReg>(
301301
&self,
302-
reg: LowLevelILSSARegisterKind<R>,
302+
reg: impl AsRef<LowLevelILSSARegister<R>>,
303303
) -> Vec<LowLevelILInstruction<'_, M, SSA>> {
304304
use binaryninjacore_sys::BNGetLowLevelILSSARegisterUses;
305-
let register_id = match reg {
306-
LowLevelILSSARegisterKind::Full { kind, .. } => kind.id(),
307-
LowLevelILSSARegisterKind::Partial { partial_reg, .. } => partial_reg.id(),
308-
};
305+
let reg = reg.as_ref();
309306
let mut count = 0;
310307
let instrs = unsafe {
311308
BNGetLowLevelILSSARegisterUses(
312309
self.handle,
313-
register_id.into(),
314-
reg.version() as usize,
310+
reg.id().into(),
311+
reg.version as usize,
315312
&mut count,
316313
)
317314
};
@@ -327,19 +324,12 @@ impl<M: FunctionMutability> LowLevelILFunction<M, SSA> {
327324
#[must_use]
328325
pub fn get_ssa_register_definition<R: ArchReg>(
329326
&self,
330-
reg: &LowLevelILSSARegisterKind<R>,
327+
reg: impl AsRef<LowLevelILSSARegister<R>>,
331328
) -> Option<LowLevelILInstruction<'_, M, SSA>> {
332329
use binaryninjacore_sys::BNGetLowLevelILSSARegisterDefinition;
333-
let register_id = match reg {
334-
LowLevelILSSARegisterKind::Full { kind, .. } => kind.id(),
335-
LowLevelILSSARegisterKind::Partial { partial_reg, .. } => partial_reg.id(),
336-
};
330+
let reg = reg.as_ref();
337331
let instr_idx = unsafe {
338-
BNGetLowLevelILSSARegisterDefinition(
339-
self.handle,
340-
register_id.into(),
341-
reg.version() as usize,
342-
)
332+
BNGetLowLevelILSSARegisterDefinition(self.handle, reg.id().into(), reg.version as usize)
343333
};
344334
self.instruction_from_index(LowLevelInstructionIndex(instr_idx))
345335
}
@@ -348,14 +338,11 @@ impl<M: FunctionMutability> LowLevelILFunction<M, SSA> {
348338
#[must_use]
349339
pub fn get_ssa_register_value<R: ArchReg>(
350340
&self,
351-
reg: &LowLevelILSSARegisterKind<R>,
341+
reg: impl AsRef<LowLevelILSSARegister<R>>,
352342
) -> Option<RegisterValue> {
353-
let register_id = match reg {
354-
LowLevelILSSARegisterKind::Full { kind, .. } => kind.id(),
355-
LowLevelILSSARegisterKind::Partial { partial_reg, .. } => partial_reg.id(),
356-
};
343+
let reg = reg.as_ref();
357344
let value = unsafe {
358-
BNGetLowLevelILSSARegisterValue(self.handle, register_id.into(), reg.version() as usize)
345+
BNGetLowLevelILSSARegisterValue(self.handle, reg.id().into(), reg.version as usize)
359346
};
360347
if value.state == BNRegisterValueType::UndeterminedValue {
361348
return None;

rust/src/low_level_il/operation.rs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -413,11 +413,11 @@ where
413413
let full_raw_id = RegisterId(self.op.operands[0] as u32);
414414
let version = self.op.operands[1] as u32;
415415
let partial_raw_id = RegisterId(self.op.operands[2] as u32);
416-
let full_reg =
417-
CoreRegister::new(self.function.arch(), full_raw_id).expect("Bad register ID");
416+
let full_reg_kind = LowLevelILRegisterKind::from_raw(&self.function.arch(), full_raw_id)
417+
.expect("Bad register ID");
418418
let partial_reg =
419419
CoreRegister::new(self.function.arch(), partial_raw_id).expect("Bad register ID");
420-
LowLevelILSSARegisterKind::new_partial(full_reg, partial_reg, version)
420+
LowLevelILSSARegisterKind::new_partial(full_reg_kind, version, partial_reg)
421421
}
422422

423423
pub fn source_expr(&self) -> LowLevelILExpression<'func, M, F, ValueExpr> {
@@ -868,11 +868,11 @@ where
868868
let full_raw_id = RegisterId(self.op.operands[0] as u32);
869869
let version = self.op.operands[1] as u32;
870870
let partial_raw_id = RegisterId(self.op.operands[2] as u32);
871-
let full_reg =
872-
CoreRegister::new(self.function.arch(), full_raw_id).expect("Bad register ID");
871+
let full_reg_kind = LowLevelILRegisterKind::from_raw(&self.function.arch(), full_raw_id)
872+
.expect("Bad register ID");
873873
let partial_reg =
874874
CoreRegister::new(self.function.arch(), partial_raw_id).expect("Bad register ID");
875-
LowLevelILSSARegisterKind::new_partial(full_reg, partial_reg, version)
875+
LowLevelILSSARegisterKind::new_partial(full_reg_kind, version, partial_reg)
876876
}
877877
}
878878

rust/tests/low_level_il.rs

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -259,13 +259,19 @@ fn test_llil_ssa() {
259259
LowLevelILInstructionKind::SetRegSsa(op) => {
260260
assert_eq!(op.size(), 4);
261261
match op.dest_reg() {
262-
LowLevelILSSARegisterKind::Full { kind, version } => {
263-
assert_eq!(kind.name(), "edi");
264-
assert_eq!(version, 1);
262+
LowLevelILSSARegisterKind::Full(reg) => {
263+
assert_eq!(reg.name(), "edi");
264+
assert_eq!(reg.version, 1);
265265
}
266266
_ => panic!("Expected LowLevelILSSARegisterKind::Full"),
267267
}
268268
assert_eq!(op.source_expr().index, LowLevelExpressionIndex(0));
269+
270+
// Verify dest_reg does not have a use, so let's verify the ssa register definition.
271+
let dest_reg_def = llil_ssa_function
272+
.get_ssa_register_definition(op.dest_reg())
273+
.expect("Valid ssa reg def");
274+
assert_eq!(dest_reg_def.address(), ssa_instr_0.address());
269275
}
270276
_ => panic!("Expected SetRegSsa"),
271277
}
@@ -287,6 +293,23 @@ fn test_llil_ssa() {
287293
let dest_memory_version = op.dest_memory_version();
288294
assert_eq!(dest_memory_version, 1);
289295
assert_eq!(dest_expr.index, LowLevelExpressionIndex(4));
296+
297+
// Grab the SP register so we can verify its use.
298+
let dest_expr_kind = dest_expr.kind();
299+
let sub_expr = dest_expr_kind.as_binary_op().unwrap();
300+
match sub_expr.left().kind() {
301+
LowLevelILExpressionKind::RegSsa(reg) => {
302+
// Verify esp#0 has a single use in the next instruction (same address however).
303+
let sp_0_uses = llil_ssa_function.get_ssa_register_uses(reg.source_reg());
304+
println!("{:?}", sp_0_uses);
305+
assert_eq!(sp_0_uses.len(), 2);
306+
let _next_instr_use = sp_0_uses
307+
.iter()
308+
.find(|inst| inst.index != ssa_instr_1.index)
309+
.expect("Failed to get next instructions use of sp");
310+
}
311+
_ => panic!("Expected RegSsa"),
312+
}
290313
}
291314
_ => panic!("Expected StoreSsa"),
292315
}

0 commit comments

Comments
 (0)