From 501fe3ec7a63a534fca3926a5fc4267f6886f089 Mon Sep 17 00:00:00 2001 From: Mason Reed Date: Sun, 7 Dec 2025 15:03:44 -0500 Subject: [Rust] Move architecture module code into more reasonable files To keep backwards compatibility for commonly referenced code we re-export them within the architecture module. Also does some light refactoring of some newly added APIs to keep them more consistent with other parts of the codebase. --- rust/src/architecture/flag.rs | 447 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 447 insertions(+) create mode 100644 rust/src/architecture/flag.rs (limited to 'rust/src/architecture/flag.rs') diff --git a/rust/src/architecture/flag.rs b/rust/src/architecture/flag.rs new file mode 100644 index 00000000..7c0f5deb --- /dev/null +++ b/rust/src/architecture/flag.rs @@ -0,0 +1,447 @@ +use crate::architecture::CoreArchitecture; +use binaryninjacore_sys::*; +use std::borrow::Cow; +use std::collections::HashMap; +use std::ffi::CStr; +use std::fmt::Debug; +use std::hash::Hash; + +pub use binaryninjacore_sys::BNFlagRole as FlagRole; +pub use binaryninjacore_sys::BNLowLevelILFlagCondition as FlagCondition; + +crate::new_id_type!(FlagId, u32); +// TODO: Make this NonZero? +crate::new_id_type!(FlagWriteId, u32); +crate::new_id_type!(FlagClassId, u32); +crate::new_id_type!(FlagGroupId, u32); + +pub trait Flag: Debug + Sized + Clone + Copy + Hash + Eq { + type FlagClass: FlagClass; + + fn name(&self) -> Cow<'_, str>; + fn role(&self, class: Option) -> FlagRole; + + /// Unique identifier for this `Flag`. + /// + /// *MUST* be in the range [0, 0x7fff_ffff] + fn id(&self) -> FlagId; +} + +pub trait FlagWrite: Sized + Clone + Copy { + type FlagType: Flag; + type FlagClass: FlagClass; + + fn name(&self) -> Cow<'_, str>; + fn class(&self) -> Option; + + /// Unique identifier for this `FlagWrite`. + /// + /// *MUST NOT* be 0. + /// *MUST* be in the range [1, 0x7fff_ffff] + fn id(&self) -> FlagWriteId; + + fn flags_written(&self) -> Vec; +} + +pub trait FlagClass: Sized + Clone + Copy + Hash + Eq { + fn name(&self) -> Cow<'_, str>; + + /// Unique identifier for this `FlagClass`. + /// + /// *MUST NOT* be 0. + /// *MUST* be in the range [1, 0x7fff_ffff] + fn id(&self) -> FlagClassId; +} + +pub trait FlagGroup: Debug + Sized + Clone + Copy { + type FlagType: Flag; + type FlagClass: FlagClass; + + fn name(&self) -> Cow<'_, str>; + + /// Unique identifier for this `FlagGroup`. + /// + /// *MUST* be in the range [0, 0x7fff_ffff] + fn id(&self) -> FlagGroupId; + + /// Returns the list of flags that need to be resolved in order + /// to take the clean flag resolution path -- at time of writing, + /// all required flags must have been set by the same instruction, + /// and the 'querying' instruction must be reachable from *one* + /// instruction that sets all of these flags. + fn flags_required(&self) -> Vec; + + /// Returns the mapping of Semantic Flag Classes to Flag Conditions, + /// in the context of this Flag Group. + /// + /// Example: + /// + /// If we have a group representing `cr1_lt` (as in PowerPC), we would + /// have multiple Semantic Flag Classes used by the different Flag Write + /// Types to represent the different comparisons, so for `cr1_lt` we + /// would return a mapping along the lines of: + /// + /// ```text + /// cr1_signed -> LLFC_SLT, + /// cr1_unsigned -> LLFC_ULT, + /// ``` + /// + /// This allows the core to recover the semantics of the comparison and + /// inline it into conditional branches when appropriate. + fn flag_conditions(&self) -> HashMap; +} + +/// Type for architectures that do not use flags. Will panic if accessed as a flag. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct UnusedFlag; + +impl Flag for UnusedFlag { + type FlagClass = Self; + fn name(&self) -> Cow<'_, str> { + unreachable!() + } + fn role(&self, _class: Option) -> FlagRole { + unreachable!() + } + fn id(&self) -> FlagId { + unreachable!() + } +} + +impl FlagWrite for UnusedFlag { + type FlagType = Self; + type FlagClass = Self; + fn name(&self) -> Cow<'_, str> { + unreachable!() + } + fn class(&self) -> Option { + unreachable!() + } + fn id(&self) -> FlagWriteId { + unreachable!() + } + fn flags_written(&self) -> Vec { + unreachable!() + } +} + +impl FlagClass for UnusedFlag { + fn name(&self) -> Cow<'_, str> { + unreachable!() + } + fn id(&self) -> FlagClassId { + unreachable!() + } +} + +impl FlagGroup for UnusedFlag { + type FlagType = Self; + type FlagClass = Self; + fn name(&self) -> Cow<'_, str> { + unreachable!() + } + fn id(&self) -> FlagGroupId { + unreachable!() + } + fn flags_required(&self) -> Vec { + unreachable!() + } + fn flag_conditions(&self) -> HashMap { + unreachable!() + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct CoreFlag { + arch: CoreArchitecture, + id: FlagId, +} + +impl CoreFlag { + pub fn new(arch: CoreArchitecture, id: FlagId) -> Option { + let flag = Self { arch, id }; + flag.is_valid().then_some(flag) + } + + fn is_valid(&self) -> bool { + // We check the name to see if the flag is actually valid. + let name = unsafe { BNGetArchitectureFlagName(self.arch.handle, self.id.into()) }; + match name.is_null() { + true => false, + false => { + unsafe { BNFreeString(name) }; + true + } + } + } +} + +impl Flag for CoreFlag { + type FlagClass = CoreFlagClass; + + fn name(&self) -> Cow<'_, str> { + unsafe { + let name = BNGetArchitectureFlagName(self.arch.handle, self.id.into()); + + // We need to guarantee ownership, as if we're still + // a Borrowed variant we're about to free the underlying + // memory. + let res = CStr::from_ptr(name); + let res = res.to_string_lossy().into_owned().into(); + + BNFreeString(name); + + res + } + } + + fn role(&self, class: Option) -> FlagRole { + unsafe { + BNGetArchitectureFlagRole( + self.arch.handle, + self.id.into(), + class.map(|c| c.id.0).unwrap_or(0), + ) + } + } + + fn id(&self) -> FlagId { + self.id + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Hash)] +pub struct CoreFlagWrite { + arch: CoreArchitecture, + id: FlagWriteId, +} + +impl CoreFlagWrite { + pub fn new(arch: CoreArchitecture, id: FlagWriteId) -> Option { + let flag_write = Self { arch, id }; + flag_write.is_valid().then_some(flag_write) + } + + fn is_valid(&self) -> bool { + // We check the name to see if the flag write is actually valid. + let name = unsafe { BNGetArchitectureFlagWriteTypeName(self.arch.handle, self.id.into()) }; + match name.is_null() { + true => false, + false => { + unsafe { BNFreeString(name) }; + true + } + } + } +} + +impl FlagWrite for CoreFlagWrite { + type FlagType = CoreFlag; + type FlagClass = CoreFlagClass; + + fn name(&self) -> Cow<'_, str> { + unsafe { + let name = BNGetArchitectureFlagWriteTypeName(self.arch.handle, self.id.into()); + + // We need to guarantee ownership, as if we're still + // a Borrowed variant we're about to free the underlying + // memory. + let res = CStr::from_ptr(name); + let res = res.to_string_lossy().into_owned().into(); + + BNFreeString(name); + + res + } + } + + fn class(&self) -> Option { + let class = unsafe { + BNGetArchitectureSemanticClassForFlagWriteType(self.arch.handle, self.id.into()) + }; + + match class { + 0 => None, + class_id => Some(CoreFlagClass::new(self.arch, class_id.into())?), + } + } + + fn id(&self) -> FlagWriteId { + self.id + } + + fn flags_written(&self) -> Vec { + let mut count: usize = 0; + let regs: *mut u32 = unsafe { + BNGetArchitectureFlagsWrittenByFlagWriteType( + self.arch.handle, + self.id.into(), + &mut count, + ) + }; + + let ret = unsafe { + std::slice::from_raw_parts(regs, count) + .iter() + .map(|id| FlagId::from(*id)) + .filter_map(|reg| CoreFlag::new(self.arch, reg)) + .collect() + }; + + unsafe { + BNFreeRegisterList(regs); + } + + ret + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct CoreFlagClass { + arch: CoreArchitecture, + id: FlagClassId, +} + +impl CoreFlagClass { + pub fn new(arch: CoreArchitecture, id: FlagClassId) -> Option { + let flag = Self { arch, id }; + flag.is_valid().then_some(flag) + } + + fn is_valid(&self) -> bool { + // We check the name to see if the flag is actually valid. + let name = + unsafe { BNGetArchitectureSemanticFlagClassName(self.arch.handle, self.id.into()) }; + match name.is_null() { + true => false, + false => { + unsafe { BNFreeString(name) }; + true + } + } + } +} + +impl FlagClass for CoreFlagClass { + fn name(&self) -> Cow<'_, str> { + unsafe { + let name = BNGetArchitectureSemanticFlagClassName(self.arch.handle, self.id.into()); + + // We need to guarantee ownership, as if we're still + // a Borrowed variant we're about to free the underlying + // memory. + let res = CStr::from_ptr(name); + let res = res.to_string_lossy().into_owned().into(); + + BNFreeString(name); + + res + } + } + + fn id(&self) -> FlagClassId { + self.id + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq)] +pub struct CoreFlagGroup { + arch: CoreArchitecture, + id: FlagGroupId, +} + +impl CoreFlagGroup { + pub fn new(arch: CoreArchitecture, id: FlagGroupId) -> Option { + let flag_group = Self { arch, id }; + flag_group.is_valid().then_some(flag_group) + } + + fn is_valid(&self) -> bool { + // We check the name to see if the flag group is actually valid. + let name = + unsafe { BNGetArchitectureSemanticFlagGroupName(self.arch.handle, self.id.into()) }; + match name.is_null() { + true => false, + false => { + unsafe { BNFreeString(name) }; + true + } + } + } +} + +impl FlagGroup for CoreFlagGroup { + type FlagType = CoreFlag; + type FlagClass = CoreFlagClass; + + fn name(&self) -> Cow<'_, str> { + unsafe { + let name = BNGetArchitectureSemanticFlagGroupName(self.arch.handle, self.id.into()); + + // We need to guarantee ownership, as if we're still + // a Borrowed variant we're about to free the underlying + // memory. + let res = CStr::from_ptr(name); + let res = res.to_string_lossy().into_owned().into(); + + BNFreeString(name); + + res + } + } + + fn id(&self) -> FlagGroupId { + self.id + } + + fn flags_required(&self) -> Vec { + let mut count: usize = 0; + let regs: *mut u32 = unsafe { + BNGetArchitectureFlagsRequiredForSemanticFlagGroup( + self.arch.handle, + self.id.into(), + &mut count, + ) + }; + + let ret = unsafe { + std::slice::from_raw_parts(regs, count) + .iter() + .map(|id| FlagId::from(*id)) + .filter_map(|reg| CoreFlag::new(self.arch, reg)) + .collect() + }; + + unsafe { + BNFreeRegisterList(regs); + } + + ret + } + + fn flag_conditions(&self) -> HashMap { + let mut count: usize = 0; + + unsafe { + let flag_conds = BNGetArchitectureFlagConditionsForSemanticFlagGroup( + self.arch.handle, + self.id.into(), + &mut count, + ); + + let ret = std::slice::from_raw_parts_mut(flag_conds, count) + .iter() + .filter_map(|class_cond| { + Some(( + CoreFlagClass::new(self.arch, class_cond.semanticClass.into())?, + class_cond.condition, + )) + }) + .collect(); + + BNFreeFlagConditionsForSemanticFlagGroup(flag_conds); + + ret + } + } +} -- cgit v1.3.1