Skip to content

Commit 11cc166

Browse files
committed
[Rust] Add LowLevelILSSARegister to correctly refer to the full ssa register when querying
1 parent e9904f2 commit 11cc166

File tree

4 files changed

+156
-37
lines changed

4 files changed

+156
-37
lines changed

rust/src/low_level_il.rs

Lines changed: 115 additions & 18 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::{Display, Formatter};
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)
@@ -89,6 +89,12 @@ impl fmt::Debug for LowLevelILTempRegister {
8989
}
9090
}
9191

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

@@ -146,54 +152,145 @@ impl<R: ArchReg> fmt::Debug for LowLevelILRegisterKind<R> {
146152
fn fmt(&self, f: &mut fmt::Formatter) -> 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) => fmt::Debug::fmt(id, f),
150156
}
151157
}
152158
}
153159

160+
impl<R: ArchReg> fmt::Display for LowLevelILRegisterKind<R> {
161+
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
162+
fmt::Debug::fmt(self, f)
163+
}
164+
}
165+
154166
impl From<LowLevelILTempRegister> for LowLevelILRegisterKind<CoreRegister> {
155167
fn from(reg: LowLevelILTempRegister) -> Self {
156168
LowLevelILRegisterKind::Temp(reg)
157169
}
158170
}
159171

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

173230
impl<R: ArchReg> LowLevelILSSARegisterKind<R> {
174231
pub fn new_full(kind: LowLevelILRegisterKind<R>, version: u32) -> Self {
175-
Self::Full { kind, version }
232+
Self::Full(LowLevelILSSARegister::new(kind, version))
176233
}
177234

178-
pub fn new_partial(full_reg: CoreRegister, partial_reg: CoreRegister, version: u32) -> Self {
235+
pub fn new_partial(
236+
full_reg: LowLevelILRegisterKind<R>,
237+
version: u32,
238+
partial_reg: CoreRegister,
239+
) -> Self {
179240
Self::Partial {
180-
full_reg,
241+
full_reg: LowLevelILSSARegister::new(full_reg, version),
181242
partial_reg,
182-
version,
183243
}
184244
}
185245

186-
pub fn version(&self) -> u32 {
246+
/// This is the non-aliased register used. This should be called when you intend to actually
247+
/// query for SSA dataflow information, as a partial register is prohibited from being used.
248+
///
249+
/// # Example
250+
///
251+
/// On x86 `al` in the LLIL SSA will have a physical register of `eax`.
252+
pub fn physical_reg(&self) -> LowLevelILSSARegister<R> {
187253
match *self {
188-
LowLevelILSSARegisterKind::Full { version, .. }
189-
| LowLevelILSSARegisterKind::Partial { version, .. } => version,
254+
LowLevelILSSARegisterKind::Full(reg) => reg,
255+
LowLevelILSSARegisterKind::Partial { full_reg, .. } => full_reg,
190256
}
191257
}
192258

193-
pub fn id(&self) -> RegisterId {
259+
/// Gets the displayable register, for partial this will be the partial register name.
260+
///
261+
/// # Example
262+
///
263+
/// On x86 this will display "al" not "eax".
264+
pub fn name(&self) -> Cow<'_, str> {
194265
match *self {
195-
LowLevelILSSARegisterKind::Full { kind, .. } => kind.id(),
196-
LowLevelILSSARegisterKind::Partial { partial_reg, .. } => partial_reg.id(),
266+
LowLevelILSSARegisterKind::Full(ref reg) => reg.reg.name(),
267+
LowLevelILSSARegisterKind::Partial {
268+
ref partial_reg, ..
269+
} => partial_reg.name(),
270+
}
271+
}
272+
}
273+
274+
impl<R: ArchReg> AsRef<LowLevelILSSARegister<R>> for LowLevelILSSARegisterKind<R> {
275+
fn as_ref(&self) -> &LowLevelILSSARegister<R> {
276+
match self {
277+
LowLevelILSSARegisterKind::Full(reg) => reg,
278+
LowLevelILSSARegisterKind::Partial { full_reg, .. } => full_reg,
279+
}
280+
}
281+
}
282+
283+
impl<R: ArchReg> From<LowLevelILSSARegister<R>> for LowLevelILSSARegisterKind<R> {
284+
fn from(value: LowLevelILSSARegister<R>) -> Self {
285+
LowLevelILSSARegisterKind::Full(value)
286+
}
287+
}
288+
289+
impl<R: ArchReg> From<LowLevelILSSARegisterKind<R>> for LowLevelILSSARegister<R> {
290+
fn from(value: LowLevelILSSARegisterKind<R>) -> Self {
291+
match value {
292+
LowLevelILSSARegisterKind::Full(reg) => reg,
293+
LowLevelILSSARegisterKind::Partial { full_reg, .. } => full_reg,
197294
}
198295
}
199296
}

rust/src/low_level_il/function.rs

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -264,15 +264,16 @@ impl<M: FunctionMutability> LowLevelILFunction<M, SSA> {
264264
#[must_use]
265265
pub fn get_ssa_register_uses<R: ArchReg>(
266266
&self,
267-
reg: LowLevelILSSARegisterKind<R>,
267+
reg: impl AsRef<LowLevelILSSARegister<R>>,
268268
) -> Vec<LowLevelILInstruction<'_, M, SSA>> {
269269
use binaryninjacore_sys::BNGetLowLevelILSSARegisterUses;
270+
let reg = reg.as_ref();
270271
let mut count = 0;
271272
let instrs = unsafe {
272273
BNGetLowLevelILSSARegisterUses(
273274
self.handle,
274275
reg.id().into(),
275-
reg.version() as usize,
276+
reg.version as usize,
276277
&mut count,
277278
)
278279
};
@@ -288,15 +289,12 @@ impl<M: FunctionMutability> LowLevelILFunction<M, SSA> {
288289
#[must_use]
289290
pub fn get_ssa_register_definition<R: ArchReg>(
290291
&self,
291-
reg: &LowLevelILSSARegisterKind<R>,
292+
reg: impl AsRef<LowLevelILSSARegister<R>>,
292293
) -> Option<LowLevelILInstruction<'_, M, SSA>> {
293294
use binaryninjacore_sys::BNGetLowLevelILSSARegisterDefinition;
295+
let reg = reg.as_ref();
294296
let instr_idx = unsafe {
295-
BNGetLowLevelILSSARegisterDefinition(
296-
self.handle,
297-
reg.id().into(),
298-
reg.version() as usize,
299-
)
297+
BNGetLowLevelILSSARegisterDefinition(self.handle, reg.id().into(), reg.version as usize)
300298
};
301299
self.instruction_from_index(LowLevelInstructionIndex(instr_idx))
302300
}
@@ -305,10 +303,11 @@ impl<M: FunctionMutability> LowLevelILFunction<M, SSA> {
305303
#[must_use]
306304
pub fn get_ssa_register_value<R: ArchReg>(
307305
&self,
308-
reg: &LowLevelILSSARegisterKind<R>,
306+
reg: impl AsRef<LowLevelILSSARegister<R>>,
309307
) -> Option<RegisterValue> {
308+
let reg = reg.as_ref();
310309
let value = unsafe {
311-
BNGetLowLevelILSSARegisterValue(self.handle, reg.id().into(), reg.version() as usize)
310+
BNGetLowLevelILSSARegisterValue(self.handle, reg.id().into(), reg.version as usize)
312311
};
313312
if value.state == BNRegisterValueType::UndeterminedValue {
314313
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)