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/register.rs | 333 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 333 insertions(+) create mode 100644 rust/src/architecture/register.rs (limited to 'rust/src/architecture/register.rs') diff --git a/rust/src/architecture/register.rs b/rust/src/architecture/register.rs new file mode 100644 index 00000000..50bb38ba --- /dev/null +++ b/rust/src/architecture/register.rs @@ -0,0 +1,333 @@ +use crate::architecture::CoreArchitecture; +use crate::rc::{CoreArrayProvider, CoreArrayProviderInner}; +use binaryninjacore_sys::*; +use std::borrow::Cow; +use std::ffi::CStr; +use std::fmt::{Debug, Formatter}; +use std::hash::Hash; + +pub use binaryninjacore_sys::BNImplicitRegisterExtend as ImplicitRegisterExtend; + +crate::new_id_type!(RegisterId, u32); + +impl RegisterId { + pub fn is_temporary(&self) -> bool { + self.0 & 0x8000_0000 != 0 + } +} + +crate::new_id_type!(RegisterStackId, u32); + +pub trait RegisterInfo: Sized { + type RegType: Register; + + fn parent(&self) -> Option; + fn size(&self) -> usize; + fn offset(&self) -> usize; + fn implicit_extend(&self) -> ImplicitRegisterExtend; +} + +pub trait Register: Debug + Sized + Clone + Copy + Hash + Eq { + type InfoType: RegisterInfo; + + fn name(&self) -> Cow<'_, str>; + fn info(&self) -> Self::InfoType; + + /// Unique identifier for this `Register`. + /// + /// *MUST* be in the range [0, 0x7fff_ffff] + fn id(&self) -> RegisterId; +} + +pub trait RegisterStackInfo: Sized { + type RegStackType: RegisterStack; + type RegType: Register; + type RegInfoType: RegisterInfo; + + fn storage_regs(&self) -> (Self::RegType, usize); + fn top_relative_regs(&self) -> Option<(Self::RegType, usize)>; + fn stack_top_reg(&self) -> Self::RegType; +} + +/// Type for architectures that do not use register stacks. Will panic if accessed as a register stack. +#[derive(Clone, Copy, PartialEq, Eq, Hash)] +pub struct UnusedRegisterStackInfo { + _reg: std::marker::PhantomData, +} + +impl RegisterStackInfo for UnusedRegisterStackInfo { + type RegStackType = UnusedRegisterStack; + type RegType = R; + type RegInfoType = R::InfoType; + + fn storage_regs(&self) -> (Self::RegType, usize) { + unreachable!() + } + fn top_relative_regs(&self) -> Option<(Self::RegType, usize)> { + unreachable!() + } + fn stack_top_reg(&self) -> Self::RegType { + unreachable!() + } +} + +pub trait RegisterStack: Debug + Sized + Clone + Copy { + type InfoType: RegisterStackInfo< + RegType = Self::RegType, + RegInfoType = Self::RegInfoType, + RegStackType = Self, + >; + type RegType: Register; + type RegInfoType: RegisterInfo; + + fn name(&self) -> Cow<'_, str>; + fn info(&self) -> Self::InfoType; + + /// Unique identifier for this `RegisterStack`. + /// + /// *MUST* be in the range [0, 0x7fff_ffff] + fn id(&self) -> RegisterStackId; +} + +/// Type for architectures that do not use register stacks. Will panic if accessed as a register stack. +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct UnusedRegisterStack { + _reg: std::marker::PhantomData, +} + +impl RegisterStack for UnusedRegisterStack { + type InfoType = UnusedRegisterStackInfo; + type RegType = R; + type RegInfoType = R::InfoType; + + fn name(&self) -> Cow<'_, str> { + unreachable!() + } + fn info(&self) -> Self::InfoType { + unreachable!() + } + fn id(&self) -> RegisterStackId { + unreachable!() + } +} + +#[derive(Debug, Copy, Clone)] +pub struct CoreRegisterInfo { + arch: CoreArchitecture, + id: RegisterId, + info: BNRegisterInfo, +} + +impl CoreRegisterInfo { + pub fn new(arch: CoreArchitecture, id: RegisterId, info: BNRegisterInfo) -> Self { + Self { arch, id, info } + } +} + +impl RegisterInfo for CoreRegisterInfo { + type RegType = CoreRegister; + + fn parent(&self) -> Option { + if self.id != RegisterId::from(self.info.fullWidthRegister) { + Some(CoreRegister::new( + self.arch, + RegisterId::from(self.info.fullWidthRegister), + )?) + } else { + None + } + } + + fn size(&self) -> usize { + self.info.size + } + + fn offset(&self) -> usize { + self.info.offset + } + + fn implicit_extend(&self) -> ImplicitRegisterExtend { + self.info.extend + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Hash)] +pub struct CoreRegister { + arch: CoreArchitecture, + id: RegisterId, +} + +impl CoreRegister { + pub fn new(arch: CoreArchitecture, id: RegisterId) -> Option { + let register = Self { arch, id }; + register.is_valid().then_some(register) + } + + fn is_valid(&self) -> bool { + // We check the name to see if the register is actually valid. + let name = unsafe { BNGetArchitectureRegisterName(self.arch.handle, self.id.into()) }; + match name.is_null() { + true => false, + false => { + unsafe { BNFreeString(name) }; + true + } + } + } +} + +impl Register for CoreRegister { + type InfoType = CoreRegisterInfo; + + fn name(&self) -> Cow<'_, str> { + unsafe { + let name = BNGetArchitectureRegisterName(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 info(&self) -> CoreRegisterInfo { + CoreRegisterInfo::new(self.arch, self.id, unsafe { + BNGetArchitectureRegisterInfo(self.arch.handle, self.id.into()) + }) + } + + fn id(&self) -> RegisterId { + self.id + } +} + +impl Debug for CoreRegister { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("CoreRegister") + .field("id", &self.id) + .field("name", &self.name()) + .finish() + } +} + +impl CoreArrayProvider for CoreRegister { + type Raw = u32; + type Context = CoreArchitecture; + type Wrapped<'a> = Self; +} + +unsafe impl CoreArrayProviderInner for CoreRegister { + unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { + BNFreeRegisterList(raw) + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> { + Self::new(*context, RegisterId::from(*raw)).expect("Register list contains valid registers") + } +} + +#[derive(Debug, Copy, Clone)] +pub struct CoreRegisterStackInfo { + arch: CoreArchitecture, + // TODO: Wrap BNRegisterStackInfo + info: BNRegisterStackInfo, +} + +impl CoreRegisterStackInfo { + pub fn new(arch: CoreArchitecture, info: BNRegisterStackInfo) -> Self { + Self { arch, info } + } +} + +impl RegisterStackInfo for CoreRegisterStackInfo { + type RegStackType = CoreRegisterStack; + type RegType = CoreRegister; + type RegInfoType = CoreRegisterInfo; + + fn storage_regs(&self) -> (Self::RegType, usize) { + ( + CoreRegister::new(self.arch, RegisterId::from(self.info.firstStorageReg)) + .expect("Storage register is valid"), + self.info.storageCount as usize, + ) + } + + fn top_relative_regs(&self) -> Option<(Self::RegType, usize)> { + if self.info.topRelativeCount == 0 { + None + } else { + Some(( + CoreRegister::new(self.arch, RegisterId::from(self.info.firstTopRelativeReg)) + .expect("Top relative register is valid"), + self.info.topRelativeCount as usize, + )) + } + } + + fn stack_top_reg(&self) -> Self::RegType { + CoreRegister::new(self.arch, RegisterId::from(self.info.stackTopReg)) + .expect("Stack top register is valid") + } +} + +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] +pub struct CoreRegisterStack { + arch: CoreArchitecture, + id: RegisterStackId, +} + +impl CoreRegisterStack { + pub fn new(arch: CoreArchitecture, id: RegisterStackId) -> Option { + let register_stack = Self { arch, id }; + register_stack.is_valid().then_some(register_stack) + } + + fn is_valid(&self) -> bool { + // We check the name to see if the stack register is actually valid. + let name = unsafe { BNGetArchitectureRegisterStackName(self.arch.handle, self.id.into()) }; + match name.is_null() { + true => false, + false => { + unsafe { BNFreeString(name) }; + true + } + } + } +} + +impl RegisterStack for CoreRegisterStack { + type InfoType = CoreRegisterStackInfo; + type RegType = CoreRegister; + type RegInfoType = CoreRegisterInfo; + + fn name(&self) -> Cow<'_, str> { + unsafe { + let name = BNGetArchitectureRegisterStackName(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 info(&self) -> CoreRegisterStackInfo { + CoreRegisterStackInfo::new(self.arch, unsafe { + BNGetArchitectureRegisterStackInfo(self.arch.handle, self.id.into()) + }) + } + + fn id(&self) -> RegisterStackId { + self.id + } +} -- cgit v1.3.1