summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMason Reed <mason@vector35.com>2025-12-07 15:03:44 -0500
committerMason Reed <35282038+emesare@users.noreply.github.com>2025-12-10 17:35:19 -0500
commit501fe3ec7a63a534fca3926a5fc4267f6886f089 (patch)
tree0e3e64fcef0a3180950f3e8fb3758ebcbf5d02e7
parent7090d904513c3e80321bb6a8aba9c3b37d809bac (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.rs8
-rw-r--r--plugins/dwarf/dwarfdump/src/lib.rs3
-rw-r--r--rust/examples/flowgraph.rs3
-rw-r--r--rust/src/architecture.rs1484
-rw-r--r--rust/src/architecture/basic_block.rs261
-rw-r--r--rust/src/architecture/branches.rs153
-rw-r--r--rust/src/architecture/flag.rs447
-rw-r--r--rust/src/architecture/instruction.rs114
-rw-r--r--rust/src/architecture/intrinsic.rs150
-rw-r--r--rust/src/architecture/register.rs333
-rw-r--r--rust/src/basic_block.rs100
-rw-r--r--rust/src/binary_view.rs6
-rw-r--r--rust/src/flowgraph.rs1
-rw-r--r--rust/src/flowgraph/edge.rs6
-rw-r--r--rust/src/flowgraph/node.rs3
-rw-r--r--rust/src/function.rs4
-rw-r--r--rust/src/lib.rs5
-rw-r--r--rust/src/llvm.rs100
-rw-r--r--rust/src/variable.rs55
-rw-r--r--view/minidump/src/command.rs2
-rw-r--r--view/minidump/src/view.rs4
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