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 | |
| 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.
| -rw-r--r-- | arch/riscv/src/lib.rs | 8 | ||||
| -rw-r--r-- | plugins/dwarf/dwarfdump/src/lib.rs | 3 | ||||
| -rw-r--r-- | rust/examples/flowgraph.rs | 3 | ||||
| -rw-r--r-- | rust/src/architecture.rs | 1484 | ||||
| -rw-r--r-- | rust/src/architecture/basic_block.rs | 261 | ||||
| -rw-r--r-- | rust/src/architecture/branches.rs | 153 | ||||
| -rw-r--r-- | rust/src/architecture/flag.rs | 447 | ||||
| -rw-r--r-- | rust/src/architecture/instruction.rs | 114 | ||||
| -rw-r--r-- | rust/src/architecture/intrinsic.rs | 150 | ||||
| -rw-r--r-- | rust/src/architecture/register.rs | 333 | ||||
| -rw-r--r-- | rust/src/basic_block.rs | 100 | ||||
| -rw-r--r-- | rust/src/binary_view.rs | 6 | ||||
| -rw-r--r-- | rust/src/flowgraph.rs | 1 | ||||
| -rw-r--r-- | rust/src/flowgraph/edge.rs | 6 | ||||
| -rw-r--r-- | rust/src/flowgraph/node.rs | 3 | ||||
| -rw-r--r-- | rust/src/function.rs | 4 | ||||
| -rw-r--r-- | rust/src/lib.rs | 5 | ||||
| -rw-r--r-- | rust/src/llvm.rs | 100 | ||||
| -rw-r--r-- | rust/src/variable.rs | 55 | ||||
| -rw-r--r-- | view/minidump/src/command.rs | 2 | ||||
| -rw-r--r-- | view/minidump/src/view.rs | 4 |
21 files changed, 1671 insertions, 1571 deletions
diff --git a/arch/riscv/src/lib.rs b/arch/riscv/src/lib.rs index ef9e12e1..6ce25406 100644 --- a/arch/riscv/src/lib.rs +++ b/arch/riscv/src/lib.rs @@ -9,10 +9,9 @@ use binaryninja::relocation::{Relocation, RelocationHandlerExt}; use binaryninja::{ add_optional_plugin_dependency, architecture, architecture::{ - llvm_assemble, Architecture, ArchitectureExt, CoreArchitecture, CustomArchitectureHandle, - ImplicitRegisterExtend, InstructionInfo, LlvmServicesCodeModel, LlvmServicesDialect, - LlvmServicesRelocMode, Register as Reg, RegisterInfo, UnusedFlag, UnusedRegisterStack, - UnusedRegisterStackInfo, + Architecture, ArchitectureExt, CoreArchitecture, CustomArchitectureHandle, + ImplicitRegisterExtend, InstructionInfo, Register as Reg, RegisterInfo, UnusedFlag, + UnusedRegisterStack, UnusedRegisterStackInfo, }, binary_view::{BinaryView, BinaryViewExt}, calling_convention::{register_calling_convention, CallingConvention, ConventionBuilder}, @@ -20,6 +19,7 @@ use binaryninja::{ disassembly::{InstructionTextToken, InstructionTextTokenKind}, function::Function, function_recognizer::FunctionRecognizer, + llvm::{llvm_assemble, LlvmServicesCodeModel, LlvmServicesDialect, LlvmServicesRelocMode}, rc::Ref, relocation::{ CoreRelocationHandler, CustomRelocationHandlerHandle, RelocationHandler, RelocationInfo, diff --git a/plugins/dwarf/dwarfdump/src/lib.rs b/plugins/dwarf/dwarfdump/src/lib.rs index 1d0343f1..7610c3f2 100644 --- a/plugins/dwarf/dwarfdump/src/lib.rs +++ b/plugins/dwarf/dwarfdump/src/lib.rs @@ -13,10 +13,11 @@ // limitations under the License. use binaryninja::{ + architecture::BranchType, binary_view::{BinaryView, BinaryViewExt}, command::{register_command, Command}, disassembly::{DisassemblyTextLine, InstructionTextToken, InstructionTextTokenKind}, - flowgraph::{BranchType, EdgeStyle, FlowGraph, FlowGraphNode, FlowGraphOption}, + flowgraph::{EdgeStyle, FlowGraph, FlowGraphNode, FlowGraphOption}, }; use dwarfreader::is_valid; diff --git a/rust/examples/flowgraph.rs b/rust/examples/flowgraph.rs index ede7ce84..75c854a7 100644 --- a/rust/examples/flowgraph.rs +++ b/rust/examples/flowgraph.rs @@ -7,9 +7,10 @@ use binaryninja::interaction::handler::{ }; use binaryninja::interaction::{MessageBoxButtonResult, MessageBoxButtonSet, MessageBoxIcon}; use binaryninja::{ + architecture::BranchType, binary_view::{BinaryView, BinaryViewExt}, disassembly::{DisassemblyTextLine, InstructionTextToken, InstructionTextTokenKind}, - flowgraph::{BranchType, EdgePenStyle, FlowGraph, ThemeColor}, + flowgraph::{EdgePenStyle, FlowGraph, ThemeColor}, }; pub struct GraphPrinter; 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) - } -} diff --git a/rust/src/architecture/basic_block.rs b/rust/src/architecture/basic_block.rs new file mode 100644 index 00000000..1867f50a --- /dev/null +++ b/rust/src/architecture/basic_block.rs @@ -0,0 +1,261 @@ +use crate::architecture::{CoreArchitecture, IndirectBranchInfo}; +use crate::basic_block::BasicBlock; +use crate::function::{Function, Location, NativeBlock}; +use crate::rc::Ref; +use binaryninjacore_sys::*; +use std::collections::{HashMap, HashSet}; + +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) }; + } +} diff --git a/rust/src/architecture/branches.rs b/rust/src/architecture/branches.rs new file mode 100644 index 00000000..ff0ab516 --- /dev/null +++ b/rust/src/architecture/branches.rs @@ -0,0 +1,153 @@ +use crate::architecture::CoreArchitecture; +use crate::function::Location; +use crate::rc::{CoreArrayProvider, CoreArrayProviderInner}; +use binaryninjacore_sys::*; + +pub use binaryninjacore_sys::BNBranchType as BranchType; + +#[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, + } + } +} + +#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] +pub struct IndirectBranchInfo { + pub source: Location, + pub dest: Location, + pub auto_defined: bool, +} + +impl From<BNIndirectBranchInfo> for IndirectBranchInfo { + fn from(value: BNIndirectBranchInfo) -> Self { + Self { + source: Location::from_raw(value.sourceAddr, value.sourceArch), + dest: Location::from_raw(value.destAddr, value.destArch), + auto_defined: value.autoDefined, + } + } +} + +impl From<IndirectBranchInfo> for BNIndirectBranchInfo { + fn from(value: IndirectBranchInfo) -> Self { + let source_arch = value + .source + .arch + .map(|a| a.handle) + .unwrap_or(std::ptr::null_mut()); + let dest_arch = value + .source + .arch + .map(|a| a.handle) + .unwrap_or(std::ptr::null_mut()); + Self { + sourceArch: source_arch, + sourceAddr: value.source.addr, + destArch: dest_arch, + destAddr: value.dest.addr, + autoDefined: value.auto_defined, + } + } +} + +impl CoreArrayProvider for IndirectBranchInfo { + type Raw = BNIndirectBranchInfo; + type Context = (); + type Wrapped<'a> = Self; +} + +unsafe impl CoreArrayProviderInner for IndirectBranchInfo { + unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { + BNFreeIndirectBranchList(raw) + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + Self::from(*raw) + } +} 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<u32>? +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<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>; +} + +/// 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<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!() + } +} + +#[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 + } + } +} diff --git a/rust/src/architecture/instruction.rs b/rust/src/architecture/instruction.rs new file mode 100644 index 00000000..94cb788b --- /dev/null +++ b/rust/src/architecture/instruction.rs @@ -0,0 +1,114 @@ +use crate::architecture::{BranchInfo, BranchKind, CoreArchitecture}; +use binaryninjacore_sys::*; + +/// 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(), + } + } + + /// Add a branch to this [`InstructionInfo`], maximum of 3 branches may be added (as per [`NUM_BRANCH_INFO`]). + 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()), + ], + } + } +} diff --git a/rust/src/architecture/intrinsic.rs b/rust/src/architecture/intrinsic.rs new file mode 100644 index 00000000..4ad5ee2a --- /dev/null +++ b/rust/src/architecture/intrinsic.rs @@ -0,0 +1,150 @@ +use crate::architecture::CoreArchitecture; +use crate::confidence::Conf; +use crate::rc::Ref; +use crate::types::{NameAndType, Type}; +use binaryninjacore_sys::{ + BNFreeNameAndTypeList, BNFreeOutputTypeList, BNFreeString, BNGetArchitectureIntrinsicClass, + BNGetArchitectureIntrinsicInputs, BNGetArchitectureIntrinsicName, + BNGetArchitectureIntrinsicOutputs, BNIntrinsicClass, +}; +use std::borrow::Cow; +use std::ffi::CStr; +use std::fmt::{Debug, Formatter}; + +crate::new_id_type!(IntrinsicId, u32); + +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>>>; +} + +/// 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(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() + } +} 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<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; +} + +/// 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<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!() + } +} + +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; +} + +/// 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<R: Register> { + _reg: std::marker::PhantomData<R>, +} + +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 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<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 + } +} diff --git a/rust/src/basic_block.rs b/rust/src/basic_block.rs index 90c93ad4..0bc2d95e 100644 --- a/rust/src/basic_block.rs +++ b/rust/src/basic_block.rs @@ -12,10 +12,9 @@ // See the License for the specific language governing permissions and // limitations under the License. -use crate::architecture::CoreArchitecture; +use crate::architecture::{BranchType, CoreArchitecture}; use crate::function::Function; use crate::rc::*; -use crate::BranchType; use binaryninjacore_sys::*; use std::fmt; use std::fmt::Debug; @@ -26,13 +25,6 @@ enum EdgeDirection { Outgoing, } -pub struct PendingBasicBlockEdge { - pub branch_type: BranchType, - pub target: u64, - pub arch: CoreArchitecture, - pub fallthrough: bool, -} - pub struct Edge<'a, C: 'a + BlockContext> { pub branch: BranchType, pub back_edge: bool, @@ -40,7 +32,7 @@ pub struct Edge<'a, C: 'a + BlockContext> { pub target: Guard<'a, BasicBlock<C>>, } -impl<'a, C: 'a + fmt::Debug + BlockContext> fmt::Debug for Edge<'a, C> { +impl<'a, C: 'a + Debug + BlockContext> Debug for Edge<'a, C> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!( f, @@ -96,6 +88,60 @@ unsafe impl<'a, C: 'a + BlockContext> CoreArrayProviderInner for Edge<'a, C> { } } +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct PendingBasicBlockEdge { + pub branch_type: BranchType, + pub target: u64, + pub arch: CoreArchitecture, + pub fallthrough: bool, +} + +impl PendingBasicBlockEdge { + pub fn new( + branch_type: BranchType, + target: u64, + arch: CoreArchitecture, + fallthrough: bool, + ) -> Self { + Self { + branch_type, + target, + arch, + fallthrough, + } + } +} + +impl From<BNPendingBasicBlockEdge> for PendingBasicBlockEdge { + fn from(edge: BNPendingBasicBlockEdge) -> Self { + Self { + branch_type: edge.type_, + target: edge.target, + arch: unsafe { CoreArchitecture::from_raw(edge.arch) }, + fallthrough: edge.fallThrough, + } + } +} + +impl CoreArrayProvider for PendingBasicBlockEdge { + type Raw = BNPendingBasicBlockEdge; + type Context = (); + type Wrapped<'a> + = PendingBasicBlockEdge + where + Self: 'a; +} + +unsafe impl CoreArrayProviderInner for PendingBasicBlockEdge { + unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { + BNFreePendingBasicBlockEdgeList(raw); + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + PendingBasicBlockEdge::from(*raw) + } +} + pub trait BlockContext: Clone + Sync + Send + Sized { type Instruction; type InstructionIndex: Debug + From<u64>; @@ -243,36 +289,24 @@ impl<C: BlockContext> BasicBlock<C> { } } - pub fn pending_outgoing_edges(&self) -> Vec<PendingBasicBlockEdge> { + /// Pending outgoing edges for the basic block. These are edges that have not yet been resolved. + pub fn pending_outgoing_edges(&self) -> Array<PendingBasicBlockEdge> { unsafe { let mut count = 0; let edges_ptr = BNGetBasicBlockPendingOutgoingEdges(self.handle, &mut count); - let edges = std::slice::from_raw_parts(edges_ptr, count); - - let mut result = Vec::with_capacity(count); - for edge in edges { - result.push(PendingBasicBlockEdge { - branch_type: edge.type_, - target: edge.target, - arch: CoreArchitecture::from_raw(edge.arch), - fallthrough: edge.fallThrough, - }); - } - - BNFreePendingBasicBlockEdgeList(edges_ptr); - result + Array::new(edges_ptr, count, ()) } } - pub fn add_pending_outgoing_edge( - &self, - typ: BranchType, - addr: u64, - arch: CoreArchitecture, - fallthrough: bool, - ) { + pub fn add_pending_outgoing_edge(&self, edge: &PendingBasicBlockEdge) { unsafe { - BNBasicBlockAddPendingOutgoingEdge(self.handle, typ, addr, arch.handle, fallthrough); + BNBasicBlockAddPendingOutgoingEdge( + self.handle, + edge.branch_type, + edge.target, + edge.arch.handle, + edge.fallthrough, + ); } } diff --git a/rust/src/binary_view.rs b/rust/src/binary_view.rs index e1b7a5c0..31bdeb89 100644 --- a/rust/src/binary_view.rs +++ b/rust/src/binary_view.rs @@ -1466,12 +1466,12 @@ pub trait BinaryViewExt: BinaryViewBase { } } - fn read_buffer(&self, offset: u64, len: usize) -> Result<DataBuffer> { + fn read_buffer(&self, offset: u64, len: usize) -> Option<DataBuffer> { let read_buffer = unsafe { BNReadViewBuffer(self.as_ref().handle, offset, len) }; if read_buffer.is_null() { - Err(()) + None } else { - Ok(DataBuffer::from_raw(read_buffer)) + Some(DataBuffer::from_raw(read_buffer)) } } diff --git a/rust/src/flowgraph.rs b/rust/src/flowgraph.rs index 4489d950..02a79fa3 100644 --- a/rust/src/flowgraph.rs +++ b/rust/src/flowgraph.rs @@ -32,7 +32,6 @@ pub use edge::EdgeStyle; pub use edge::FlowGraphEdge; pub use node::FlowGraphNode; -pub type BranchType = BNBranchType; pub type EdgePenStyle = BNEdgePenStyle; pub type ThemeColor = BNThemeColor; pub type FlowGraphOption = BNFlowGraphOption; diff --git a/rust/src/flowgraph/edge.rs b/rust/src/flowgraph/edge.rs index 7862f156..38e0ae9e 100644 --- a/rust/src/flowgraph/edge.rs +++ b/rust/src/flowgraph/edge.rs @@ -1,8 +1,8 @@ -use binaryninjacore_sys::*; - +use crate::architecture::BranchType; use crate::flowgraph::node::FlowGraphNode; -use crate::flowgraph::{BranchType, EdgePenStyle, ThemeColor}; +use crate::flowgraph::{EdgePenStyle, ThemeColor}; use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Ref}; +use binaryninjacore_sys::*; #[derive(Clone, Debug, PartialEq)] pub struct FlowGraphEdge { diff --git a/rust/src/flowgraph/node.rs b/rust/src/flowgraph/node.rs index 5c2b9248..28233670 100644 --- a/rust/src/flowgraph/node.rs +++ b/rust/src/flowgraph/node.rs @@ -1,7 +1,8 @@ +use crate::architecture::BranchType; use crate::basic_block::{BasicBlock, BlockContext}; use crate::disassembly::DisassemblyTextLine; use crate::flowgraph::edge::{EdgeStyle, FlowGraphEdge}; -use crate::flowgraph::{BranchType, FlowGraph}; +use crate::flowgraph::FlowGraph; use crate::function::HighlightColor; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; use binaryninjacore_sys::*; diff --git a/rust/src/function.rs b/rust/src/function.rs index 1ef0a377..29d4fc3c 100644 --- a/rust/src/function.rs +++ b/rust/src/function.rs @@ -37,7 +37,7 @@ pub use binaryninjacore_sys::BNFunctionAnalysisSkipOverride as FunctionAnalysisS pub use binaryninjacore_sys::BNFunctionUpdateType as FunctionUpdateType; pub use binaryninjacore_sys::BNHighlightStandardColor as HighlightStandardColor; -use crate::architecture::RegisterId; +use crate::architecture::{IndirectBranchInfo, RegisterId}; use crate::binary_view::AddressRange; use crate::confidence::Conf; use crate::high_level_il::HighLevelILFunction; @@ -46,7 +46,7 @@ use crate::low_level_il::LowLevelILRegularFunction; use crate::medium_level_il::MediumLevelILFunction; use crate::metadata::Metadata; use crate::variable::{ - IndirectBranchInfo, MergedVariable, NamedVariableWithType, RegisterValue, RegisterValueType, + MergedVariable, NamedVariableWithType, RegisterValue, RegisterValueType, StackVariableReference, Variable, }; use crate::workflow::Workflow; diff --git a/rust/src/lib.rs b/rust/src/lib.rs index 476cd04c..50913ed8 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -94,6 +94,8 @@ pub mod workflow; use crate::file_metadata::FileMetadata; use crate::function::Function; +use crate::progress::{NoProgressCallback, ProgressCallback}; +use crate::string::raw_to_string; use binary_view::BinaryView; use binaryninjacore_sys::*; use metadata::Metadata; @@ -107,9 +109,6 @@ use string::BnString; use string::IntoCStr; use string::IntoJson; -use crate::progress::{NoProgressCallback, ProgressCallback}; -use crate::string::raw_to_string; -pub use binaryninjacore_sys::BNBranchType as BranchType; pub use binaryninjacore_sys::BNDataFlowQueryOption as DataFlowQueryOption; pub use binaryninjacore_sys::BNEndianness as Endianness; pub use binaryninjacore_sys::BNILBranchDependence as ILBranchDependence; diff --git a/rust/src/llvm.rs b/rust/src/llvm.rs index 1296db65..d2a0292a 100644 --- a/rust/src/llvm.rs +++ b/rust/src/llvm.rs @@ -1,8 +1,102 @@ -use binaryninjacore_sys::BNLlvmServicesDisasmInstruction; +use binaryninjacore_sys::{ + BNLlvmServicesAssemble, BNLlvmServicesAssembleFree, BNLlvmServicesDisasmInstruction, + BNLlvmServicesInit, +}; use std::ffi::{CStr, CString}; use std::os::raw::{c_char, c_int}; -pub fn disas_instruction(triplet: &str, data: &[u8], address64: u64) -> Option<(usize, String)> { +#[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, + triplet: &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(triplet) + .map_err(|_| "Invalid encoding in architecture triple string".to_string())?; + let mut out_bytes: *mut std::ffi::c_char = std::ptr::null_mut(); + let mut out_bytes_len: std::ffi::c_int = 0; + let mut err_bytes: *mut std::ffi::c_char = std::ptr::null_mut(); + let mut err_len: std::ffi::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 std::ffi::c_char, + &mut out_bytes_len as *mut std::ffi::c_int, + &mut err_bytes as *mut *mut std::ffi::c_char, + &mut err_len as *mut std::ffi::c_int, + ) + }; + + let out = if out_bytes_len == 0 { + Vec::new() + } else { + unsafe { + std::slice::from_raw_parts( + out_bytes as *const std::ffi::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 std::ffi::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) + } +} + +pub fn llvm_disassemble(triplet: &str, data: &[u8], address: u64) -> Option<(usize, String)> { unsafe { let triplet = CString::new(triplet).ok()?; let mut src = data.to_vec(); @@ -11,7 +105,7 @@ pub fn disas_instruction(triplet: &str, data: &[u8], address64: u64) -> Option<( triplet.as_ptr(), src.as_mut_ptr(), src.len() as c_int, - address64, + address, buf.as_mut_ptr() as *mut c_char, buf.len(), ); diff --git a/rust/src/variable.rs b/rust/src/variable.rs index fc6febfb..d224a45b 100644 --- a/rust/src/variable.rs +++ b/rust/src/variable.rs @@ -911,58 +911,3 @@ impl PossibleValueSet { } } } - -#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)] -pub struct IndirectBranchInfo { - pub source: Location, - pub dest: Location, - pub auto_defined: bool, -} - -impl From<BNIndirectBranchInfo> for IndirectBranchInfo { - fn from(value: BNIndirectBranchInfo) -> Self { - Self { - source: Location::from_raw(value.sourceAddr, value.sourceArch), - dest: Location::from_raw(value.destAddr, value.destArch), - auto_defined: value.autoDefined, - } - } -} - -impl From<IndirectBranchInfo> for BNIndirectBranchInfo { - fn from(value: IndirectBranchInfo) -> Self { - let source_arch = value - .source - .arch - .map(|a| a.handle) - .unwrap_or(std::ptr::null_mut()); - let dest_arch = value - .source - .arch - .map(|a| a.handle) - .unwrap_or(std::ptr::null_mut()); - Self { - sourceArch: source_arch, - sourceAddr: value.source.addr, - destArch: dest_arch, - destAddr: value.dest.addr, - autoDefined: value.auto_defined, - } - } -} - -impl CoreArrayProvider for IndirectBranchInfo { - type Raw = BNIndirectBranchInfo; - type Context = (); - type Wrapped<'a> = Self; -} - -unsafe impl CoreArrayProviderInner for IndirectBranchInfo { - unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) { - BNFreeIndirectBranchList(raw) - } - - unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - Self::from(*raw) - } -} diff --git a/view/minidump/src/command.rs b/view/minidump/src/command.rs index 818b1c59..81629f55 100644 --- a/view/minidump/src/command.rs +++ b/view/minidump/src/command.rs @@ -8,7 +8,7 @@ use binaryninja::binary_view::{BinaryView, BinaryViewBase, BinaryViewExt}; pub fn print_memory_information(bv: &BinaryView) { debug!("Printing memory information"); if let Some(minidump_bv) = bv.parent_view() { - if let Ok(read_buffer) = minidump_bv.read_buffer(0, minidump_bv.len() as usize) { + if let Some(read_buffer) = minidump_bv.read_buffer(0, minidump_bv.len() as usize) { if let Ok(minidump_obj) = Minidump::read(read_buffer.get_data()) { if let Ok(memory_info_list) = minidump_obj.get_stream::<MinidumpMemoryInfoList>() { let mut memory_info_list_writer = Vec::new(); diff --git a/view/minidump/src/view.rs b/view/minidump/src/view.rs index ff85fe07..413fafa1 100644 --- a/view/minidump/src/view.rs +++ b/view/minidump/src/view.rs @@ -117,7 +117,9 @@ impl MinidumpBinaryView { fn init(&self) -> BinaryViewResult<()> { let parent_view = self.parent_view().ok_or(())?; - let read_buffer = parent_view.read_buffer(0, parent_view.len() as usize)?; + let read_buffer = parent_view + .read_buffer(0, parent_view.len() as usize) + .ok_or(())?; if let Ok(minidump_obj) = Minidump::read(read_buffer.get_data()) { // Architecture, platform information |
