diff options
| author | Mason Reed <mason@vector35.com> | 2025-12-07 15:03:44 -0500 |
|---|---|---|
| committer | Mason Reed <35282038+emesare@users.noreply.github.com> | 2025-12-10 17:35:19 -0500 |
| commit | 501fe3ec7a63a534fca3926a5fc4267f6886f089 (patch) | |
| tree | 0e3e64fcef0a3180950f3e8fb3758ebcbf5d02e7 /rust/src/architecture.rs | |
| parent | 7090d904513c3e80321bb6a8aba9c3b37d809bac (diff) | |
[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.
Diffstat (limited to 'rust/src/architecture.rs')
| -rw-r--r-- | rust/src/architecture.rs | 1484 |
1 files changed, 25 insertions, 1459 deletions
diff --git a/rust/src/architecture.rs b/rust/src/architecture.rs index 2f0490f2..becf7639 100644 --- a/rust/src/architecture.rs +++ b/rust/src/architecture.rs @@ -23,44 +23,52 @@ use crate::{ calling_convention::CoreCallingConvention, data_buffer::DataBuffer, disassembly::InstructionTextToken, - function::{Function, NativeBlock}, + function::Function, platform::Platform, rc::*, relocation::CoreRelocationHandler, string::{IntoCStr, *}, types::{NameAndType, Type}, - BranchType, Endianness, + Endianness, }; use std::ops::Deref; use std::{ - borrow::{Borrow, Cow}, - collections::HashMap, - ffi::{c_char, c_int, c_void, CStr, CString}, - fmt::Display, + borrow::Borrow, + ffi::{c_char, c_void, CString}, hash::Hash, mem::MaybeUninit, }; -use crate::basic_block::BasicBlock; use crate::function_recognizer::FunctionRecognizer; use crate::relocation::{CustomRelocationHandlerHandle, RelocationHandler}; -use crate::variable::IndirectBranchInfo; use crate::confidence::Conf; -use crate::function::Location; use crate::low_level_il::expression::ValueExpr; use crate::low_level_il::lifting::{ get_default_flag_cond_llil, get_default_flag_write_llil, LowLevelILFlagWriteOp, }; use crate::low_level_il::{LowLevelILMutableExpression, LowLevelILMutableFunction}; -pub use binaryninjacore_sys::BNFlagRole as FlagRole; -pub use binaryninjacore_sys::BNImplicitRegisterExtend as ImplicitRegisterExtend; -pub use binaryninjacore_sys::BNLowLevelILFlagCondition as FlagCondition; -use std::collections::HashSet; -macro_rules! newtype { +pub mod basic_block; +pub mod branches; +pub mod flag; +pub mod instruction; +pub mod intrinsic; +pub mod register; + +// Re-export all the submodules to keep from breaking everyone's code. +// We split these out just to clarify each part, not necessarily to enforce an extra namespace. +pub use basic_block::*; +pub use branches::*; +pub use flag::*; +pub use instruction::*; +pub use intrinsic::*; +pub use register::*; + +#[macro_export] +macro_rules! new_id_type { ($name:ident, $inner_type:ty) => { - #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] + #[derive(std::fmt::Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] pub struct $name(pub $inner_type); impl From<$inner_type> for $name { @@ -75,377 +83,14 @@ macro_rules! newtype { } } - impl Display for $name { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + impl std::fmt::Display for $name { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "{}", self.0) } } }; } -newtype!(RegisterId, u32); - -impl RegisterId { - pub fn is_temporary(&self) -> bool { - self.0 & 0x8000_0000 != 0 - } -} - -newtype!(RegisterStackId, u32); -newtype!(FlagId, u32); -// TODO: Make this NonZero<u32>? -newtype!(FlagWriteId, u32); -newtype!(FlagClassId, u32); -newtype!(FlagGroupId, u32); -newtype!(IntrinsicId, u32); - -#[derive(Default, Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub enum BranchKind { - #[default] - Unresolved, - Unconditional(u64), - False(u64), - True(u64), - Call(u64), - FunctionReturn, - SystemCall, - Indirect, - Exception, - UserDefined, -} - -#[derive(Default, Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub struct BranchInfo { - /// If `None` the target architecture is the same as the branch instruction. - pub arch: Option<CoreArchitecture>, - pub kind: BranchKind, -} - -impl BranchInfo { - /// Branches to an instruction with the current architecture. - pub fn new(kind: BranchKind) -> Self { - Self { arch: None, kind } - } - - /// Branches to an instruction with an explicit architecture. - /// - /// Use this if your architecture can transition to another architecture with a branch. - pub fn new_with_arch(kind: BranchKind, arch: CoreArchitecture) -> Self { - Self { - arch: Some(arch), - kind, - } - } - - pub fn target(&self) -> Option<u64> { - match self.kind { - BranchKind::Unconditional(target) => Some(target), - BranchKind::False(target) => Some(target), - BranchKind::True(target) => Some(target), - BranchKind::Call(target) => Some(target), - _ => None, - } - } -} - -impl From<BranchInfo> for BNBranchType { - fn from(value: BranchInfo) -> Self { - match value.kind { - BranchKind::Unresolved => BNBranchType::UnresolvedBranch, - BranchKind::Unconditional(_) => BNBranchType::UnconditionalBranch, - BranchKind::False(_) => BNBranchType::FalseBranch, - BranchKind::True(_) => BNBranchType::TrueBranch, - BranchKind::Call(_) => BNBranchType::CallDestination, - BranchKind::FunctionReturn => BNBranchType::FunctionReturn, - BranchKind::SystemCall => BNBranchType::SystemCall, - BranchKind::Indirect => BNBranchType::IndirectBranch, - BranchKind::Exception => BNBranchType::ExceptionBranch, - BranchKind::UserDefined => BNBranchType::UserDefinedBranch, - } - } -} - -impl From<BranchKind> for BranchInfo { - fn from(value: BranchKind) -> Self { - Self { - arch: None, - kind: value, - } - } -} - -impl From<BranchKind> for BranchType { - fn from(value: BranchKind) -> Self { - match value { - BranchKind::Unresolved => BranchType::UnresolvedBranch, - BranchKind::Unconditional(_) => BranchType::UnconditionalBranch, - BranchKind::True(_) => BranchType::TrueBranch, - BranchKind::False(_) => BranchType::FalseBranch, - BranchKind::Call(_) => BranchType::CallDestination, - BranchKind::FunctionReturn => BranchType::FunctionReturn, - BranchKind::SystemCall => BranchType::SystemCall, - BranchKind::Indirect => BranchType::IndirectBranch, - BranchKind::Exception => BranchType::ExceptionBranch, - BranchKind::UserDefined => BranchType::UserDefinedBranch, - } - } -} - -/// This is the number of branches that can be specified in an [`InstructionInfo`]. -pub const NUM_BRANCH_INFO: usize = 3; - -#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)] -pub struct InstructionInfo { - pub length: usize, - // TODO: This field name is really long... - pub arch_transition_by_target_addr: bool, - pub delay_slots: u8, - pub branches: [Option<BranchInfo>; NUM_BRANCH_INFO], -} - -impl InstructionInfo { - // TODO: `new_with_delay_slot`? - pub fn new(length: usize, delay_slots: u8) -> Self { - Self { - length, - arch_transition_by_target_addr: false, - delay_slots, - branches: Default::default(), - } - } - - pub fn add_branch(&mut self, branch_info: impl Into<BranchInfo>) { - // Will go through each slot and attempt to add the branch info. - // TODO: Return a result with BranchInfoSlotsFilled error. - for branch in &mut self.branches { - if branch.is_none() { - *branch = Some(branch_info.into()); - return; - } - } - } -} - -impl From<BNInstructionInfo> for InstructionInfo { - fn from(value: BNInstructionInfo) -> Self { - // TODO: This is quite ugly, but we destructure the branch info so this will have to do. - let mut branch_info = [None; NUM_BRANCH_INFO]; - #[allow(clippy::needless_range_loop)] - for i in 0..value.branchCount.min(NUM_BRANCH_INFO) { - let branch_target = value.branchTarget[i]; - branch_info[i] = Some(BranchInfo { - kind: match value.branchType[i] { - BNBranchType::UnconditionalBranch => BranchKind::Unconditional(branch_target), - BNBranchType::FalseBranch => BranchKind::False(branch_target), - BNBranchType::TrueBranch => BranchKind::True(branch_target), - BNBranchType::CallDestination => BranchKind::Call(branch_target), - BNBranchType::FunctionReturn => BranchKind::FunctionReturn, - BNBranchType::SystemCall => BranchKind::SystemCall, - BNBranchType::IndirectBranch => BranchKind::Indirect, - BNBranchType::ExceptionBranch => BranchKind::Exception, - BNBranchType::UnresolvedBranch => BranchKind::Unresolved, - BNBranchType::UserDefinedBranch => BranchKind::UserDefined, - }, - arch: if value.branchArch[i].is_null() { - None - } else { - Some(unsafe { CoreArchitecture::from_raw(value.branchArch[i]) }) - }, - }); - } - Self { - length: value.length, - arch_transition_by_target_addr: value.archTransitionByTargetAddr, - delay_slots: value.delaySlots, - branches: branch_info, - } - } -} - -impl From<InstructionInfo> for BNInstructionInfo { - fn from(value: InstructionInfo) -> Self { - let branch_count = value.branches.into_iter().filter(Option::is_some).count(); - // TODO: This is quite ugly, but we destructure the branch info so this will have to do. - let branch_info_0 = value.branches[0].unwrap_or_default(); - let branch_info_1 = value.branches[1].unwrap_or_default(); - let branch_info_2 = value.branches[2].unwrap_or_default(); - Self { - length: value.length, - branchCount: branch_count, - archTransitionByTargetAddr: value.arch_transition_by_target_addr, - delaySlots: value.delay_slots, - branchType: [ - branch_info_0.into(), - branch_info_1.into(), - branch_info_2.into(), - ], - branchTarget: [ - branch_info_0.target().unwrap_or_default(), - branch_info_1.target().unwrap_or_default(), - branch_info_2.target().unwrap_or_default(), - ], - branchArch: [ - branch_info_0 - .arch - .map(|a| a.handle) - .unwrap_or(std::ptr::null_mut()), - branch_info_1 - .arch - .map(|a| a.handle) - .unwrap_or(std::ptr::null_mut()), - branch_info_2 - .arch - .map(|a| a.handle) - .unwrap_or(std::ptr::null_mut()), - ], - } - } -} - -pub trait RegisterInfo: Sized { - type RegType: Register<InfoType = Self>; - - fn parent(&self) -> Option<Self::RegType>; - 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<RegType = Self>; - - 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<InfoType = Self>; - type RegType: Register<InfoType = Self::RegInfoType>; - type RegInfoType: RegisterInfo<RegType = Self::RegType>; - - fn storage_regs(&self) -> (Self::RegType, usize); - fn top_relative_regs(&self) -> Option<(Self::RegType, usize)>; - fn stack_top_reg(&self) -> Self::RegType; -} - -pub trait RegisterStack: Debug + Sized + Clone + Copy { - type InfoType: RegisterStackInfo< - RegType = Self::RegType, - RegInfoType = Self::RegInfoType, - RegStackType = Self, - >; - type RegType: Register<InfoType = Self::RegInfoType>; - type RegInfoType: RegisterInfo<RegType = Self::RegType>; - - 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; -} - -pub trait Flag: Debug + Sized + Clone + Copy + Hash + Eq { - type FlagClass: FlagClass; - - fn name(&self) -> Cow<'_, str>; - fn role(&self, class: Option<Self::FlagClass>) -> 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<Self::FlagClass>; - - /// 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<Self::FlagType>; -} - -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<Self::FlagType>; - - /// 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<Self::FlagClass, FlagCondition>; -} - -pub trait Intrinsic: Debug + Sized + Clone + Copy { - fn name(&self) -> Cow<'_, str>; - - /// Unique identifier for this `Intrinsic`. - fn id(&self) -> IntrinsicId; - - /// The intrinsic class for this `Intrinsic`. - fn class(&self) -> BNIntrinsicClass { - BNIntrinsicClass::GeneralIntrinsicClass - } - - // TODO: Maybe just return `(String, Conf<Ref<Type>>)`? - /// List of the input names and types for this intrinsic. - fn inputs(&self) -> Vec<NameAndType>; - - /// List of the output types for this intrinsic. - fn outputs(&self) -> Vec<Conf<Ref<Type>>>; -} - pub trait Architecture: 'static + Sized + AsRef<CoreArchitecture> { type Handle: Borrow<Self> + Clone; @@ -673,742 +318,6 @@ pub trait Architecture: 'static + Sized + AsRef<CoreArchitecture> { fn handle(&self) -> Self::Handle; } -/// Type for architrectures that do not use register stacks. Will panic if accessed as a register stack. -#[derive(Clone, Copy, PartialEq, Eq, Hash)] -pub struct UnusedRegisterStackInfo<R: Register> { - _reg: std::marker::PhantomData<R>, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct UnusedRegisterStack<R: Register> { - _reg: std::marker::PhantomData<R>, -} - -impl<R: Register> RegisterStackInfo for UnusedRegisterStackInfo<R> { - type RegStackType = UnusedRegisterStack<R>; - 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!() - } -} - -impl<R: Register> RegisterStack for UnusedRegisterStack<R> { - type InfoType = UnusedRegisterStackInfo<R>; - type RegType = R; - type RegInfoType = R::InfoType; - - fn name(&self) -> Cow<'_, str> { - unreachable!() - } - fn id(&self) -> RegisterStackId { - unreachable!() - } - fn info(&self) -> Self::InfoType { - unreachable!() - } -} - -/// Type for architrectures 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<Self::FlagClass>) -> 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<Self> { - unreachable!() - } - fn id(&self) -> FlagWriteId { - unreachable!() - } - fn flags_written(&self) -> Vec<Self::FlagType> { - 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<Self::FlagType> { - unreachable!() - } - fn flag_conditions(&self) -> HashMap<Self, FlagCondition> { - unreachable!() - } -} - -/// Type for architrectures that do not use intrinsics. Will panic if accessed as an intrinsic. -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct UnusedIntrinsic; - -impl Intrinsic for UnusedIntrinsic { - fn name(&self) -> Cow<'_, str> { - unreachable!() - } - fn id(&self) -> IntrinsicId { - unreachable!() - } - fn inputs(&self) -> Vec<NameAndType> { - unreachable!() - } - fn outputs(&self) -> Vec<Conf<Ref<Type>>> { - 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<CoreRegister> { - 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<Self> { - 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<Self> { - 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 - } -} - -#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] -pub struct CoreFlag { - arch: CoreArchitecture, - id: FlagId, -} - -impl CoreFlag { - pub fn new(arch: CoreArchitecture, id: FlagId) -> Option<Self> { - 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<CoreFlagClass>) -> 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<Self> { - 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<CoreFlagClass> { - 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<CoreFlag> { - 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<Self> { - 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<Self> { - 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<CoreFlag> { - 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<CoreFlagClass, FlagCondition> { - 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 - } - } -} - -#[derive(Copy, Clone, Eq, PartialEq)] -pub struct CoreIntrinsic { - pub arch: CoreArchitecture, - pub id: IntrinsicId, -} - -impl CoreIntrinsic { - pub fn new(arch: CoreArchitecture, id: IntrinsicId) -> Option<Self> { - let intrinsic = Self { arch, id }; - intrinsic.is_valid().then_some(intrinsic) - } - - fn is_valid(&self) -> bool { - // We check the name to see if the intrinsic is actually valid. - let name = unsafe { BNGetArchitectureIntrinsicName(self.arch.handle, self.id.into()) }; - match name.is_null() { - true => false, - false => { - unsafe { BNFreeString(name) }; - true - } - } - } -} - -impl Intrinsic for CoreIntrinsic { - fn name(&self) -> Cow<'_, str> { - unsafe { - let name = BNGetArchitectureIntrinsicName(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. - // TODO: ^ the above assertion nullifies any benefit to passing back Cow tho? - let res = CStr::from_ptr(name); - let res = res.to_string_lossy().into_owned().into(); - - BNFreeString(name); - - res - } - } - - fn id(&self) -> IntrinsicId { - self.id - } - - fn class(&self) -> BNIntrinsicClass { - unsafe { BNGetArchitectureIntrinsicClass(self.arch.handle, self.id.into()) } - } - - fn inputs(&self) -> Vec<NameAndType> { - let mut count: usize = 0; - unsafe { - let inputs = - BNGetArchitectureIntrinsicInputs(self.arch.handle, self.id.into(), &mut count); - - let ret = std::slice::from_raw_parts_mut(inputs, count) - .iter() - .map(NameAndType::from_raw) - .collect(); - - BNFreeNameAndTypeList(inputs, count); - - ret - } - } - - fn outputs(&self) -> Vec<Conf<Ref<Type>>> { - let mut count: usize = 0; - unsafe { - let inputs = - BNGetArchitectureIntrinsicOutputs(self.arch.handle, self.id.into(), &mut count); - - let ret = std::slice::from_raw_parts_mut(inputs, count) - .iter() - .map(Conf::<Ref<Type>>::from_raw) - .collect(); - - BNFreeOutputTypeList(inputs, count); - - ret - } - } -} - -impl Debug for CoreIntrinsic { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("CoreIntrinsic") - .field("id", &self.id) - .field("name", &self.name()) - .field("class", &self.class()) - .field("inputs", &self.inputs()) - .field("outputs", &self.outputs()) - .finish() - } -} - // TODO: WTF?!?!?!? pub struct CoreArchitectureList(*mut *mut BNArchitecture, usize); @@ -1954,261 +863,6 @@ impl Architecture for CoreArchitecture { } } -pub struct BasicBlockAnalysisContext { - pub(crate) handle: *mut BNBasicBlockAnalysisContext, - contextual_returns_dirty: bool, - - // In - pub indirect_branches: Vec<IndirectBranchInfo>, - pub indirect_no_return_calls: HashSet<Location>, - pub analysis_skip_override: BNFunctionAnalysisSkipOverride, - pub guided_analysis_mode: bool, - pub trigger_guided_on_invalid_instruction: bool, - pub translate_tail_calls: bool, - pub disallow_branch_to_string: bool, - pub max_function_size: u64, - - // In/Out - pub max_size_reached: bool, - contextual_returns: HashMap<Location, bool>, - - // Out - direct_code_references: HashMap<u64, Location>, - direct_no_return_calls: HashSet<Location>, - halted_disassembly_addresses: HashSet<Location>, - inlined_unresolved_indirect_branches: HashSet<Location>, -} - -impl BasicBlockAnalysisContext { - pub unsafe fn from_raw(handle: *mut BNBasicBlockAnalysisContext) -> Self { - debug_assert!(!handle.is_null()); - - let ctx_ref = &*handle; - - let indirect_branches = (0..ctx_ref.indirectBranchesCount) - .map(|i| { - let raw: BNIndirectBranchInfo = - unsafe { std::ptr::read(ctx_ref.indirectBranches.add(i)) }; - IndirectBranchInfo::from(raw) - }) - .collect::<Vec<_>>(); - - let indirect_no_return_calls = (0..ctx_ref.indirectNoReturnCallsCount) - .map(|i| { - let raw = unsafe { std::ptr::read(ctx_ref.indirectNoReturnCalls.add(i)) }; - Location::from(raw) - }) - .collect::<HashSet<_>>(); - - let contextual_returns = (0..ctx_ref.contextualFunctionReturnCount) - .map(|i| { - let loc = unsafe { - let raw = std::ptr::read(ctx_ref.contextualFunctionReturnLocations.add(i)); - Location::from(raw) - }; - let val = unsafe { *ctx_ref.contextualFunctionReturnValues.add(i) }; - (loc, val) - }) - .collect::<HashMap<_, _>>(); - - let direct_code_references = (0..ctx_ref.directRefCount) - .map(|i| { - let src = unsafe { - let raw = std::ptr::read(ctx_ref.directRefSources.add(i)); - Location::from(raw) - }; - let tgt = unsafe { *ctx_ref.directRefTargets.add(i) }; - (tgt, src) - }) - .collect::<HashMap<_, _>>(); - - let direct_no_return_calls = (0..ctx_ref.directNoReturnCallsCount) - .map(|i| { - let raw = unsafe { std::ptr::read(ctx_ref.directNoReturnCalls.add(i)) }; - Location::from(raw) - }) - .collect::<HashSet<_>>(); - - let halted_disassembly_addresses = (0..ctx_ref.haltedDisassemblyAddressesCount) - .map(|i| { - let raw = unsafe { std::ptr::read(ctx_ref.haltedDisassemblyAddresses.add(i)) }; - Location::from(raw) - }) - .collect::<HashSet<_>>(); - - let inlined_unresolved_indirect_branches = (0..ctx_ref - .inlinedUnresolvedIndirectBranchCount) - .map(|i| { - let raw = - unsafe { std::ptr::read(ctx_ref.inlinedUnresolvedIndirectBranches.add(i)) }; - Location::from(raw) - }) - .collect::<HashSet<_>>(); - - BasicBlockAnalysisContext { - handle, - contextual_returns_dirty: false, - indirect_branches, - indirect_no_return_calls, - analysis_skip_override: ctx_ref.analysisSkipOverride, - guided_analysis_mode: ctx_ref.guidedAnalysisMode, - trigger_guided_on_invalid_instruction: ctx_ref.triggerGuidedOnInvalidInstruction, - translate_tail_calls: ctx_ref.translateTailCalls, - disallow_branch_to_string: ctx_ref.disallowBranchToString, - max_function_size: ctx_ref.maxFunctionSize, - max_size_reached: ctx_ref.maxSizeReached, - contextual_returns, - direct_code_references, - direct_no_return_calls, - halted_disassembly_addresses, - inlined_unresolved_indirect_branches, - } - } - - pub fn add_contextual_return(&mut self, loc: impl Into<Location>, value: bool) { - let loc = loc.into(); - if !self.contextual_returns.contains_key(&loc) { - self.contextual_returns_dirty = true; - } - - self.contextual_returns.insert(loc, value); - } - - pub fn add_direct_code_reference(&mut self, target: u64, src: impl Into<Location>) { - self.direct_code_references - .entry(target) - .or_insert(src.into()); - } - - pub fn add_direct_no_return_call(&mut self, loc: impl Into<Location>) { - self.direct_no_return_calls.insert(loc.into()); - } - - pub fn add_halted_disassembly_address(&mut self, loc: impl Into<Location>) { - self.halted_disassembly_addresses.insert(loc.into()); - } - - pub fn add_inlined_unresolved_indirect_branch(&mut self, loc: impl Into<Location>) { - self.inlined_unresolved_indirect_branches.insert(loc.into()); - } - - pub fn create_basic_block( - &self, - arch: CoreArchitecture, - start: u64, - ) -> Option<Ref<BasicBlock<NativeBlock>>> { - let raw_block = - unsafe { BNAnalyzeBasicBlocksContextCreateBasicBlock(self.handle, arch.handle, start) }; - - if raw_block.is_null() { - return None; - } - - unsafe { Some(BasicBlock::ref_from_raw(raw_block, NativeBlock::new())) } - } - - pub fn add_basic_block(&self, block: Ref<BasicBlock<NativeBlock>>) { - unsafe { - BNAnalyzeBasicBlocksContextAddBasicBlockToFunction(self.handle, block.handle); - } - } - - pub fn add_temp_outgoing_reference(&self, target: &Function) { - unsafe { - BNAnalyzeBasicBlocksContextAddTempReference(self.handle, target.handle); - } - } - - pub fn finalize(&mut self) { - if !self.direct_code_references.is_empty() { - let total = self.direct_code_references.len(); - let mut sources: Vec<BNArchitectureAndAddress> = Vec::with_capacity(total); - let mut targets: Vec<u64> = Vec::with_capacity(total); - for (target, src) in &self.direct_code_references { - sources.push(BNArchitectureAndAddress::from(src)); - targets.push(*target); - } - unsafe { - BNAnalyzeBasicBlocksContextSetDirectCodeReferences( - self.handle, - sources.as_mut_ptr(), - targets.as_mut_ptr(), - total, - ); - } - } - - if !self.direct_no_return_calls.is_empty() { - let total = self.direct_no_return_calls.len(); - let mut locations: Vec<BNArchitectureAndAddress> = Vec::with_capacity(total); - for loc in &self.direct_no_return_calls { - locations.push(BNArchitectureAndAddress::from(loc)); - } - unsafe { - BNAnalyzeBasicBlocksContextSetDirectNoReturnCalls( - self.handle, - locations.as_mut_ptr(), - total, - ); - } - } - - if !self.halted_disassembly_addresses.is_empty() { - let total = self.halted_disassembly_addresses.len(); - let mut locations: Vec<BNArchitectureAndAddress> = Vec::with_capacity(total); - for loc in &self.halted_disassembly_addresses { - locations.push(BNArchitectureAndAddress::from(loc)); - } - unsafe { - BNAnalyzeBasicBlocksContextSetHaltedDisassemblyAddresses( - self.handle, - locations.as_mut_ptr(), - total, - ); - } - } - - if !self.inlined_unresolved_indirect_branches.is_empty() { - let total = self.inlined_unresolved_indirect_branches.len(); - let mut locations: Vec<BNArchitectureAndAddress> = Vec::with_capacity(total); - for loc in &self.inlined_unresolved_indirect_branches { - locations.push(BNArchitectureAndAddress::from(loc)); - } - unsafe { - BNAnalyzeBasicBlocksContextSetInlinedUnresolvedIndirectBranches( - self.handle, - locations.as_mut_ptr(), - total, - ); - } - } - - unsafe { - (*self.handle).maxSizeReached = self.max_size_reached; - } - - if self.contextual_returns_dirty { - let total = self.contextual_returns.len(); - let mut locations: Vec<BNArchitectureAndAddress> = Vec::with_capacity(total); - let mut values: Vec<bool> = Vec::with_capacity(total); - for (loc, value) in &self.contextual_returns { - locations.push(BNArchitectureAndAddress::from(loc)); - values.push(*value); - } - unsafe { - BNAnalyzeBasicBlocksContextSetContextualFunctionReturns( - self.handle, - locations.as_mut_ptr(), - values.as_mut_ptr(), - total, - ); - } - } - - unsafe { BNAnalyzeBasicBlocksContextFinalize(self.handle) }; - } -} - impl Debug for CoreArchitecture { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { f.debug_struct("CoreArchitecture") @@ -3590,91 +2244,3 @@ where unsafe { &*self.handle } } } - -#[repr(i32)] -pub enum LlvmServicesDialect { - Unspecified = 0, - Att = 1, - Intel = 2, -} - -#[repr(i32)] -pub enum LlvmServicesCodeModel { - Default = 0, - Small = 1, - Kernel = 2, - Medium = 3, - Large = 4, -} - -#[repr(i32)] -pub enum LlvmServicesRelocMode { - Static = 0, - PIC = 1, - DynamicNoPIC = 2, -} - -pub fn llvm_assemble( - code: &str, - dialect: LlvmServicesDialect, - arch_triple: &str, - code_model: LlvmServicesCodeModel, - reloc_mode: LlvmServicesRelocMode, -) -> Result<Vec<u8>, String> { - let code = CString::new(code).map_err(|_| "Invalid encoding in code string".to_string())?; - let arch_triple = CString::new(arch_triple) - .map_err(|_| "Invalid encoding in architecture triple string".to_string())?; - let mut out_bytes: *mut c_char = std::ptr::null_mut(); - let mut out_bytes_len: c_int = 0; - let mut err_bytes: *mut c_char = std::ptr::null_mut(); - let mut err_len: c_int = 0; - - unsafe { - BNLlvmServicesInit(); - } - - let result = unsafe { - BNLlvmServicesAssemble( - code.as_ptr(), - dialect as i32, - arch_triple.as_ptr(), - code_model as i32, - reloc_mode as i32, - &mut out_bytes as *mut *mut c_char, - &mut out_bytes_len as *mut c_int, - &mut err_bytes as *mut *mut c_char, - &mut err_len as *mut c_int, - ) - }; - - let out = if out_bytes_len == 0 { - Vec::new() - } else { - unsafe { - std::slice::from_raw_parts( - out_bytes as *const c_char as *const u8, - out_bytes_len as usize, - ) - } - .to_vec() - }; - - let errors = if err_len == 0 { - "".into() - } else { - String::from_utf8_lossy(unsafe { - std::slice::from_raw_parts(err_bytes as *const c_char as *const u8, err_len as usize) - }) - .into_owned() - }; - - unsafe { - BNLlvmServicesAssembleFree(out_bytes, err_bytes); - } - - if result == 0 { - Ok(out) - } else { - Err(errors) - } -} |
