summaryrefslogtreecommitdiff
path: root/rust/src/architecture
diff options
context:
space:
mode:
Diffstat (limited to 'rust/src/architecture')
-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
6 files changed, 1458 insertions, 0 deletions
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
+ }
+}