diff --git a/.gitignore b/.gitignore index 02e5388cc7..e838baf529 100644 --- a/.gitignore +++ b/.gitignore @@ -90,6 +90,7 @@ rust/examples/*/target/ rust/examples/dwarf/*/target/ # Allow the test binaries !/rust/fixtures/bin/** +!/rust/src/types # Debugger docs docs/img/debugger diff --git a/rust/examples/bndb_to_type_library.rs b/rust/examples/bndb_to_type_library.rs index 40a184cabb..84515cf7da 100644 --- a/rust/examples/bndb_to_type_library.rs +++ b/rust/examples/bndb_to_type_library.rs @@ -1,8 +1,7 @@ // Usage: cargo run --example bndb_to_type_library use binaryninja::binary_view::BinaryViewExt; -use binaryninja::type_library::TypeLibrary; -use binaryninja::types::QualifiedName; +use binaryninja::types::{QualifiedName, TypeLibrary}; fn main() { let bndb_path_str = std::env::args().nth(1).expect("No header provided"); diff --git a/rust/examples/create_type_library.rs b/rust/examples/create_type_library.rs index 35e9b11c0e..74d93765af 100644 --- a/rust/examples/create_type_library.rs +++ b/rust/examples/create_type_library.rs @@ -1,8 +1,7 @@ // Usage: cargo run --example create_type_library use binaryninja::platform::Platform; -use binaryninja::type_library::TypeLibrary; -use binaryninja::type_parser::{CoreTypeParser, TypeParser}; +use binaryninja::types::{CoreTypeParser, TypeLibrary, TypeParser}; fn main() { let header_path_str = std::env::args().nth(1).expect("No header provided"); diff --git a/rust/examples/dump_type_library.rs b/rust/examples/dump_type_library.rs index 34ca1baf4e..f7398d28eb 100644 --- a/rust/examples/dump_type_library.rs +++ b/rust/examples/dump_type_library.rs @@ -2,8 +2,8 @@ use binaryninja::binary_view::BinaryView; use binaryninja::file_metadata::FileMetadata; -use binaryninja::type_library::TypeLibrary; -use binaryninja::type_printer::{CoreTypePrinter, TokenEscapingType}; +use binaryninja::types::library::TypeLibrary; +use binaryninja::types::printer::{CoreTypePrinter, TokenEscapingType}; fn main() { let type_lib_str = std::env::args().nth(1).expect("No type library provided"); diff --git a/rust/examples/type_printer.rs b/rust/examples/type_printer.rs index 846e1a0908..b88a023304 100644 --- a/rust/examples/type_printer.rs +++ b/rust/examples/type_printer.rs @@ -1,4 +1,4 @@ -use binaryninja::type_printer::{CoreTypePrinter, TokenEscapingType}; +use binaryninja::types::printer::{CoreTypePrinter, TokenEscapingType}; use binaryninja::types::{MemberAccess, MemberScope, Structure, StructureMember, Type}; fn main() { diff --git a/rust/src/binary_view.rs b/rust/src/binary_view.rs index a48d0264b2..459253b9dc 100644 --- a/rust/src/binary_view.rs +++ b/rust/src/binary_view.rs @@ -30,10 +30,12 @@ pub use crate::workflow::AnalysisContext; use crate::architecture::{Architecture, CoreArchitecture}; use crate::base_detection::BaseAddressDetection; use crate::basic_block::BasicBlock; +use crate::binary_view::search::SearchQuery; use crate::component::Component; use crate::confidence::Conf; use crate::data_buffer::DataBuffer; use crate::debuginfo::DebugInfo; +use crate::disassembly::DisassemblySettings; use crate::external_library::{ExternalLibrary, ExternalLocation}; use crate::file_accessor::{Accessor, FileAccessor}; use crate::file_metadata::FileMetadata; @@ -53,12 +55,12 @@ use crate::settings::Settings; use crate::string::*; use crate::symbol::{Symbol, SymbolType}; use crate::tags::{Tag, TagType}; -use crate::type_container::TypeContainer; -use crate::type_library::TypeLibrary; use crate::types::{ NamedTypeReference, QualifiedName, QualifiedNameAndType, QualifiedNameTypeAndId, Type, + TypeArchive, TypeArchiveId, TypeContainer, TypeLibrary, }; use crate::variable::DataVariable; +use crate::workflow::Workflow; use crate::{Endianness, BN_FULL_CONFIDENCE}; use std::collections::HashMap; use std::ffi::{c_char, c_void, CString}; @@ -67,17 +69,12 @@ use std::ops::Range; use std::path::{Path, PathBuf}; use std::ptr::NonNull; use std::{result, slice}; -// TODO : general reorg of modules related to bv pub mod memory_map; pub mod reader; pub mod search; pub mod writer; -use crate::binary_view::search::SearchQuery; -use crate::disassembly::DisassemblySettings; -use crate::type_archive::{TypeArchive, TypeArchiveId}; -use crate::workflow::Workflow; pub use memory_map::MemoryMap; pub use reader::BinaryReader; pub use writer::BinaryWriter; diff --git a/rust/src/collaboration/sync.rs b/rust/src/collaboration/sync.rs index a8006b8b3c..72c57a46c6 100644 --- a/rust/src/collaboration/sync.rs +++ b/rust/src/collaboration/sync.rs @@ -13,7 +13,7 @@ use crate::progress::{NoProgressCallback, ProgressCallback}; use crate::project::file::ProjectFile; use crate::rc::Ref; use crate::string::{raw_to_string, BnString, IntoCStr}; -use crate::type_archive::{TypeArchive, TypeArchiveMergeConflict}; +use crate::types::archive::{TypeArchive, TypeArchiveMergeConflict}; /// Get the default directory path for a remote Project. This is based off the Setting for /// collaboration.directory, the project's id, and the project's remote's id. diff --git a/rust/src/component.rs b/rust/src/component.rs index 4c5d5992d2..f038d5c39c 100644 --- a/rust/src/component.rs +++ b/rust/src/component.rs @@ -2,7 +2,7 @@ use crate::binary_view::{BinaryView, BinaryViewExt}; use crate::function::Function; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; use crate::string::{BnString, IntoCStr}; -use crate::types::ComponentReferencedType; +use crate::types::Type; use std::ffi::c_char; use std::fmt::Debug; use std::ptr::NonNull; @@ -304,3 +304,24 @@ unsafe impl CoreArrayProviderInner for Component { Guard::new(Self::from_raw(raw_ptr), context) } } + +// TODO: Remove this struct, or make it not a ZST with a terrible array provider. +/// ZST used only for `Array`. +pub struct ComponentReferencedType; + +impl CoreArrayProvider for ComponentReferencedType { + type Raw = *mut BNType; + type Context = (); + type Wrapped<'a> = &'a Type; +} + +unsafe impl CoreArrayProviderInner for ComponentReferencedType { + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { + BNComponentFreeReferencedTypes(raw, count) + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + // SAFETY: &*mut BNType == &Type (*mut BNType == Type) + std::mem::transmute(raw) + } +} diff --git a/rust/src/data_notification.rs b/rust/src/data_notification.rs index 7749e79246..d01a5ed32a 100644 --- a/rust/src/data_notification.rs +++ b/rust/src/data_notification.rs @@ -15,8 +15,7 @@ use crate::section::Section; use crate::segment::Segment; use crate::symbol::Symbol; use crate::tags::{TagReference, TagType}; -use crate::type_archive::TypeArchive; -use crate::types::{QualifiedName, Type}; +use crate::types::{QualifiedName, Type, TypeArchive}; use crate::variable::DataVariable; macro_rules! trait_handler { diff --git a/rust/src/language_representation.rs b/rust/src/language_representation.rs index 34524b96b7..d39cb95f10 100644 --- a/rust/src/language_representation.rs +++ b/rust/src/language_representation.rs @@ -14,8 +14,7 @@ use crate::high_level_il::{HighLevelExpressionIndex, HighLevelILFunction}; use crate::line_formatter::CoreLineFormatter; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref, RefCountable}; use crate::string::{BnString, IntoCStr}; -use crate::type_parser::CoreTypeParser; -use crate::type_printer::CoreTypePrinter; +use crate::types::{CoreTypeParser, CoreTypePrinter}; pub type InstructionTextTokenContext = BNInstructionTextTokenContext; pub type ScopeType = BNScopeType; diff --git a/rust/src/lib.rs b/rust/src/lib.rs index f316603345..ba654f099d 100644 --- a/rust/src/lib.rs +++ b/rust/src/lib.rs @@ -68,6 +68,7 @@ pub mod metadata; pub mod platform; pub mod progress; pub mod project; +pub mod qualified_name; pub mod rc; pub mod references; pub mod relocation; @@ -81,11 +82,6 @@ pub mod string; pub mod symbol; pub mod tags; pub mod template_simplifier; -pub mod type_archive; -pub mod type_container; -pub mod type_library; -pub mod type_parser; -pub mod type_printer; pub mod types; pub mod update; pub mod variable; diff --git a/rust/src/platform.rs b/rust/src/platform.rs index 609f4edc2c..ed4f49f48d 100644 --- a/rust/src/platform.rs +++ b/rust/src/platform.rs @@ -14,15 +14,15 @@ //! Contains all information related to the execution environment of the binary, mainly the calling conventions used -use crate::type_container::TypeContainer; -use crate::type_parser::{TypeParserError, TypeParserErrorSeverity, TypeParserResult}; use crate::{ architecture::{Architecture, CoreArchitecture}, calling_convention::CoreCallingConvention, rc::*, string::*, - type_library::TypeLibrary, - types::QualifiedNameAndType, + types::{ + QualifiedNameAndType, TypeContainer, TypeLibrary, TypeParserError, TypeParserErrorSeverity, + TypeParserResult, + }, }; use binaryninjacore_sys::*; use std::fmt::Debug; diff --git a/rust/src/qualified_name.rs b/rust/src/qualified_name.rs new file mode 100644 index 0000000000..fc362cf64c --- /dev/null +++ b/rust/src/qualified_name.rs @@ -0,0 +1,264 @@ +//! The [`QualifiedName`] is the canonical way to represent structured names in Binary Ninja. + +use crate::rc::{CoreArrayProvider, CoreArrayProviderInner}; +use crate::string::{raw_to_string, strings_to_string_list, BnString}; +use binaryninjacore_sys::*; +use std::borrow::Cow; +use std::fmt::{Display, Formatter}; +use std::ops::{Index, IndexMut}; + +/// A [`QualifiedName`] represents a name composed of multiple components, typically used for symbols +/// and type names within namespaces, classes, or modules. +/// +/// # Creating a Qualified Name +/// +/// ``` +/// use binaryninja::qualified_name::QualifiedName; +/// +/// // Uses the default separator "::" +/// let qn_vec = QualifiedName::new(vec!["my", "namespace", "func"]); +/// assert_eq!(qn_vec.to_string(), "my::namespace::func"); +/// +/// // Using `QualifiedName::from` will not split on the default separator "::". +/// let qn_from = QualifiedName::from("std::string"); +/// assert_eq!(qn_from.len(), 1); +/// assert_eq!(qn_from.to_string(), "std::string"); +/// ``` +/// +/// # Using a Custom Separator +/// +/// While `::` is the default, you can specify a custom separator: +/// +/// ``` +/// use binaryninja::qualified_name::QualifiedName; +/// +/// let qn = QualifiedName::new_with_separator(["a", "b", "c"], "."); +/// assert_eq!(qn.to_string(), "a.b.c"); +/// ``` +#[derive(Default, Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)] +pub struct QualifiedName { + // TODO: Make this Option where default is "::". + pub separator: String, + pub items: Vec, +} + +impl QualifiedName { + pub fn from_raw(value: &BNQualifiedName) -> Self { + // TODO: This could be improved... + let raw_names = unsafe { std::slice::from_raw_parts(value.name, value.nameCount) }; + let items = raw_names + .iter() + .filter_map(|&raw_name| raw_to_string(raw_name as *const _)) + .collect(); + let separator = raw_to_string(value.join).unwrap(); + Self { items, separator } + } + + pub fn from_owned_raw(value: BNQualifiedName) -> Self { + let result = Self::from_raw(&value); + Self::free_raw(value); + result + } + + pub fn into_raw(value: Self) -> BNQualifiedName { + let bn_join = BnString::new(&value.separator); + BNQualifiedName { + // NOTE: Leaking string list must be freed by core or us! + name: strings_to_string_list(&value.items), + // NOTE: Leaking string must be freed by core or us! + join: BnString::into_raw(bn_join), + nameCount: value.items.len(), + } + } + + pub fn free_raw(value: BNQualifiedName) { + unsafe { BnString::free_raw(value.join) }; + unsafe { BNFreeStringList(value.name, value.nameCount) }; + } + + /// Creates a new [`QualifiedName`] with the default separator `::`. + pub fn new(items: I) -> Self + where + I: IntoIterator, + S: Into, + { + Self::new_with_separator(items, "::") + } + + /// Creates a new `QualifiedName` with a custom separator. + pub fn new_with_separator(items: I, separator: impl Into) -> Self + where + I: IntoIterator, + S: Into, + { + let items = items.into_iter().map(Into::into).collect::>(); + Self { + items, + separator: separator.into(), + } + } + + pub fn with_item(&self, item: impl Into) -> Self { + let mut items = self.items.clone(); + items.push(item.into()); + Self::new_with_separator(items, self.separator.clone()) + } + + pub fn push(&mut self, item: String) { + self.items.push(item); + } + + pub fn pop(&mut self) -> Option { + self.items.pop() + } + + pub fn insert(&mut self, index: usize, item: String) { + if index <= self.items.len() { + self.items.insert(index, item); + } + } + + pub fn split_last(&self) -> Option<(String, QualifiedName)> { + self.items.split_last().map(|(a, b)| { + ( + a.to_owned(), + QualifiedName::new_with_separator(b.to_vec(), self.separator.clone()), + ) + }) + } + + /// Replaces all occurrences of a substring with another string in all items of the `QualifiedName` + /// and returns an owned version of the modified `QualifiedName`. + /// + /// # Example + /// + /// ``` + /// use binaryninja::qualified_name::QualifiedName; + /// + /// let qualified_name = + /// QualifiedName::new(vec!["my::namespace".to_string(), "mytype".to_string()]); + /// let replaced = qualified_name.replace("my", "your"); + /// assert_eq!( + /// replaced.items, + /// vec!["your::namespace".to_string(), "yourtype".to_string()] + /// ); + /// ``` + pub fn replace(&self, from: &str, to: &str) -> Self { + Self { + items: self + .items + .iter() + .map(|item| item.replace(from, to)) + .collect(), + separator: self.separator.clone(), + } + } + + /// Returns the last item, or `None` if it is empty. + pub fn last(&self) -> Option<&String> { + self.items.last() + } + + /// Returns a mutable reference to the last item, or `None` if it is empty. + pub fn last_mut(&mut self) -> Option<&mut String> { + self.items.last_mut() + } + + pub fn len(&self) -> usize { + self.items.len() + } + + /// A [`QualifiedName`] is empty if it has no items. + /// + /// If you want to know if the unqualified name is empty (i.e. no characters) + /// you must first convert the qualified name to unqualified via the `to_string` method. + pub fn is_empty(&self) -> bool { + self.items.is_empty() + } +} + +impl From for QualifiedName { + fn from(value: String) -> Self { + Self { + items: vec![value], + // TODO: See comment in struct def. + separator: String::from("::"), + } + } +} + +impl From<&str> for QualifiedName { + fn from(value: &str) -> Self { + Self::from(value.to_string()) + } +} + +impl From<&String> for QualifiedName { + fn from(value: &String) -> Self { + Self::from(value.to_owned()) + } +} + +impl From> for QualifiedName { + fn from(value: Cow<'_, str>) -> Self { + Self::from(value.to_string()) + } +} + +impl From> for QualifiedName { + fn from(value: Vec) -> Self { + Self::new(value) + } +} + +impl From> for QualifiedName { + fn from(value: Vec<&str>) -> Self { + value + .iter() + .map(ToString::to_string) + .collect::>() + .into() + } +} + +impl From for String { + fn from(value: QualifiedName) -> Self { + value.to_string() + } +} + +impl Index for QualifiedName { + type Output = String; + + fn index(&self, index: usize) -> &Self::Output { + &self.items[index] + } +} + +impl IndexMut for QualifiedName { + fn index_mut(&mut self, index: usize) -> &mut Self::Output { + &mut self.items[index] + } +} + +impl Display for QualifiedName { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.items.join(&self.separator)) + } +} + +impl CoreArrayProvider for QualifiedName { + type Raw = BNQualifiedName; + type Context = (); + type Wrapped<'a> = Self; +} + +unsafe impl CoreArrayProviderInner for QualifiedName { + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { + BNFreeTypeNameList(raw, count); + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + QualifiedName::from_raw(raw) + } +} diff --git a/rust/src/string.rs b/rust/src/string.rs index c7d541ce2a..8505cbf5f5 100644 --- a/rust/src/string.rs +++ b/rust/src/string.rs @@ -24,7 +24,6 @@ use std::ops::Deref; use std::path::{Path, PathBuf}; use crate::rc::*; -use crate::type_archive::TypeArchiveSnapshotId; use crate::types::QualifiedName; // TODO: Remove or refactor this. @@ -288,14 +287,6 @@ impl IntoCStr for &Path { } } -impl IntoCStr for TypeArchiveSnapshotId { - type Result = CString; - - fn to_cstr(self) -> Self::Result { - self.to_string().to_cstr() - } -} - pub trait IntoJson { type Output: IntoCStr; diff --git a/rust/src/types.rs b/rust/src/types.rs index 1387ffb32e..417dc554aa 100644 --- a/rust/src/types.rs +++ b/rust/src/types.rs @@ -11,16 +11,31 @@ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. -#![allow(unused)] - -// TODO : More widely enforce the use of ref_from_raw vs just from_raw to simplify internal binding usage? Perhaps remove from_raw functions? -// TODO : Add documentation and fix examples -// TODO : Test the get_enumeration and get_structure methods +//! The model for representing types in Binary Ninja. +//! +//! [`Type`]'s are fundamental to analysis. With types, you can influence how decompilation resolves accesses, +//! renders data, and tell the analysis of properties such as volatility and constness. +//! +//! Types are typically stored within a [`BinaryView`], [`TypeArchive`] or a [`TypeLibrary`]. +//! +//! Types can be created using the [`TypeBuilder`] or one of the convenience functions. Another way +//! to create a type is with a [`TypeParser`] if you have C type definitions. +//! +//! Some interfaces may expect to be passed a [`TypeContainer`] which itself does not store any type +//! information, rather a generic interface to query for types by name or by id. + +pub mod archive; +pub mod container; +pub mod enumeration; +pub mod library; +pub mod parser; +pub mod printer; +pub mod structure; use binaryninjacore_sys::*; use crate::{ - architecture::{Architecture, CoreArchitecture}, + architecture::Architecture, binary_view::{BinaryView, BinaryViewExt}, calling_convention::CoreCallingConvention, rc::*, @@ -28,20 +43,33 @@ use crate::{ }; use crate::confidence::{Conf, MAX_CONFIDENCE, MIN_CONFIDENCE}; -use crate::string::{raw_to_string, strings_to_string_list}; -use crate::type_container::TypeContainer; +use crate::string::raw_to_string; use crate::variable::{Variable, VariableSourceType}; -use std::borrow::Cow; use std::num::NonZeroUsize; -use std::ops::{Index, IndexMut}; use std::{ collections::HashSet, - ffi::CStr, fmt::{Debug, Display, Formatter}, hash::{Hash, Hasher}, iter::IntoIterator, }; +pub use archive::{TypeArchive, TypeArchiveId, TypeArchiveSnapshotId}; +pub use container::TypeContainer; +pub use enumeration::{Enumeration, EnumerationBuilder, EnumerationMember}; +pub use library::TypeLibrary; +pub use parser::{ + CoreTypeParser, ParsedType, TypeParser, TypeParserError, TypeParserErrorSeverity, + TypeParserResult, +}; +pub use printer::{CoreTypePrinter, TypePrinter}; +pub use structure::{ + BaseStructure, InheritedStructureMember, Structure, StructureBuilder, StructureMember, +}; + +#[deprecated(note = "Use crate::qualified_name::QualifiedName instead")] +// Re-export QualifiedName so that we do not break public consumers. +pub use crate::qualified_name::QualifiedName; + pub type StructureType = BNStructureVariant; pub type ReferenceType = BNReferenceType; pub type TypeClass = BNTypeClass; @@ -66,13 +94,11 @@ impl TypeBuilder { Self { handle } } - // Chainable terminal + /// Turn the [`TypeBuilder`] into a [`Type`]. pub fn finalize(&self) -> Ref { unsafe { Type::ref_from_raw(BNFinalizeTypeBuilder(self.handle)) } } - // Settable properties - pub fn set_can_return>>(&self, value: T) -> &Self { let mut bool_with_confidence = value.into().into(); unsafe { BNSetFunctionTypeBuilderCanReturn(self.handle, &mut bool_with_confidence) }; @@ -270,18 +296,22 @@ impl TypeBuilder { // TODO : This and properties // pub fn tokens(&self) -> ? {} + /// Create a void [`TypeBuilder`]. Analogous to [`Type::void`]. pub fn void() -> Self { unsafe { Self::from_raw(BNCreateVoidTypeBuilder()) } } + /// Create a bool [`TypeBuilder`]. Analogous to [`Type::bool`]. pub fn bool() -> Self { unsafe { Self::from_raw(BNCreateBoolTypeBuilder()) } } + /// Create a signed one byte integer [`TypeBuilder`]. Analogous to [`Type::char`]. pub fn char() -> Self { Self::int(1, true) } + /// Create an integer [`TypeBuilder`] with the given width and signedness. Analogous to [`Type::int`]. pub fn int(width: usize, is_signed: bool) -> Self { let mut is_signed = Conf::new(is_signed, MAX_CONFIDENCE).into(); @@ -289,15 +319,16 @@ impl TypeBuilder { Self::from_raw(BNCreateIntegerTypeBuilder( width, &mut is_signed, - BnString::new("").as_ptr() as *mut _, + c"".as_ptr() as _, )) } } + /// Create an integer [`TypeBuilder`] with the given width and signedness and an alternative name. + /// Analogous to [`Type::named_int`]. pub fn named_int(width: usize, is_signed: bool, alt_name: &str) -> Self { let mut is_signed = Conf::new(is_signed, MAX_CONFIDENCE).into(); - // let alt_name = BnString::new(alt_name); - let alt_name = alt_name.to_cstr(); // This segfaulted once, so the above version is there if we need to change to it, but in theory this is copied into a `const string&` on the C++ side; I'm just not 100% confident that a constant reference copies data + let alt_name = alt_name.to_cstr(); unsafe { Self::from_raw(BNCreateIntegerTypeBuilder( @@ -308,20 +339,25 @@ impl TypeBuilder { } } + /// Create a float [`TypeBuilder`] with the given width. Analogous to [`Type::float`]. pub fn float(width: usize) -> Self { unsafe { Self::from_raw(BNCreateFloatTypeBuilder(width, c"".as_ptr())) } } + /// Create a float [`TypeBuilder`] with the given width and alternative name. Analogous to [`Type::named_float`]. pub fn named_float(width: usize, alt_name: &str) -> Self { let alt_name = alt_name.to_cstr(); unsafe { Self::from_raw(BNCreateFloatTypeBuilder(width, alt_name.as_ptr())) } } + /// Create an array [`TypeBuilder`] with the given element type and count. Analogous to [`Type::array`]. pub fn array<'a, T: Into>>(ty: T, count: u64) -> Self { let owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); unsafe { Self::from_raw(BNCreateArrayTypeBuilder(&owned_raw_ty, count)) } } + /// Create an enumeration [`TypeBuilder`] with the given width and signedness. Analogous to [`Type::enumeration`]. + /// /// ## NOTE /// /// The C/C++ APIs require an associated architecture, but in the core we only query the default_int_size if the given width is 0. @@ -343,10 +379,12 @@ impl TypeBuilder { } } + /// Create a structure [`TypeBuilder`]. Analogous to [`Type::structure`]. pub fn structure(structure_type: &Structure) -> Self { unsafe { Self::from_raw(BNCreateStructureTypeBuilder(structure_type.handle)) } } + /// Create a named type reference [`TypeBuilder`]. Analogous to [`Type::named_type`]. pub fn named_type(type_reference: NamedTypeReference) -> Self { let mut is_const = Conf::new(false, MIN_CONFIDENCE).into(); let mut is_volatile = Conf::new(false, MIN_CONFIDENCE).into(); @@ -361,6 +399,7 @@ impl TypeBuilder { } } + /// Create a named type reference [`TypeBuilder`] from a type and name. Analogous to [`Type::named_type_from_type`]. pub fn named_type_from_type>(name: T, t: &Type) -> Self { let mut raw_name = QualifiedName::into_raw(name.into()); let id = c""; @@ -378,60 +417,30 @@ impl TypeBuilder { // TODO : BNCreateFunctionTypeBuilder + /// Create a pointer [`TypeBuilder`] with the given target type. Analogous to [`Type::pointer`]. pub fn pointer<'a, A: Architecture, T: Into>>(arch: &A, ty: T) -> Self { - let mut is_const = Conf::new(false, MIN_CONFIDENCE).into(); - let mut is_volatile = Conf::new(false, MIN_CONFIDENCE).into(); - let owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); - unsafe { - Self::from_raw(BNCreatePointerTypeBuilder( - arch.as_ref().handle, - &owned_raw_ty, - &mut is_const, - &mut is_volatile, - ReferenceType::PointerReferenceType, - )) - } + Self::pointer_with_options(arch, ty, false, false, None) } + /// Create a const pointer [`TypeBuilder`] with the given target type. Analogous to [`Type::const_pointer`]. pub fn const_pointer<'a, A: Architecture, T: Into>>(arch: &A, ty: T) -> Self { - let mut is_const = Conf::new(true, MAX_CONFIDENCE).into(); - let mut is_volatile = Conf::new(false, MIN_CONFIDENCE).into(); - let owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); - unsafe { - Self::from_raw(BNCreatePointerTypeBuilder( - arch.as_ref().handle, - &owned_raw_ty, - &mut is_const, - &mut is_volatile, - ReferenceType::PointerReferenceType, - )) - } + Self::pointer_with_options(arch, ty, true, false, None) } - pub fn pointer_of_width<'a, T: Into>>( + pub fn pointer_with_options<'a, A: Architecture, T: Into>>( + arch: &A, ty: T, - size: usize, is_const: bool, is_volatile: bool, ref_type: Option, ) -> Self { - let mut is_const = Conf::new(is_const, MAX_CONFIDENCE).into(); - let mut is_volatile = Conf::new(is_volatile, MAX_CONFIDENCE).into(); - let owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); - unsafe { - Self::from_raw(BNCreatePointerTypeBuilderOfWidth( - size, - &owned_raw_ty, - &mut is_const, - &mut is_volatile, - ref_type.unwrap_or(ReferenceType::PointerReferenceType), - )) - } + let arch_ptr_size = arch.address_size(); + Self::pointer_of_width(ty, arch_ptr_size, is_const, is_volatile, ref_type) } - pub fn pointer_with_options<'a, A: Architecture, T: Into>>( - arch: &A, + pub fn pointer_of_width<'a, T: Into>>( ty: T, + size: usize, is_const: bool, is_volatile: bool, ref_type: Option, @@ -440,8 +449,8 @@ impl TypeBuilder { let mut is_volatile = Conf::new(is_volatile, MAX_CONFIDENCE).into(); let owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); unsafe { - Self::from_raw(BNCreatePointerTypeBuilder( - arch.as_ref().handle, + Self::from_raw(BNCreatePointerTypeBuilderOfWidth( + size, &owned_raw_ty, &mut is_const, &mut is_volatile, @@ -465,11 +474,32 @@ impl Drop for TypeBuilder { } } -#[repr(transparent)] -pub struct Type { - pub handle: *mut BNType, -} - +/// The core model for types in Binary Ninja. +/// +/// A [`Type`] is how we model the storage of a [`Variable`] or [`crate::variable::DataVariable`] as +/// well as propagate information such as the constness of a variable. Types are also used to declare +/// function signatures, such as the [`FunctionParameter`]'s and return type. +/// +/// Types are immutable. To change a type, you must create a new one either using [`TypeBuilder`] or +/// one of the helper functions: +/// +/// - [`Type::void`] +/// - [`Type::bool`] +/// - [`Type::char`] +/// - [`Type::wide_char`] +/// - [`Type::int`], [`Type::named_int`] +/// - [`Type::float`], [`Type::named_float`] +/// - [`Type::array`] +/// - [`Type::enumeration`] +/// - [`Type::structure`] +/// - [`Type::named_type`], [`Type::named_type_from_type`] +/// - [`Type::function`], [`Type::function_with_opts`] +/// - [`Type::pointer`], [`Type::const_pointer`], [`Type::pointer_of_width`], [`Type::pointer_with_options`] +/// +/// # Example +/// +/// As an example, defining a _named_ type within a [`BinaryView`]: +/// /// ```no_run /// # use crate::binaryninja::binary_view::BinaryViewExt; /// # use binaryninja::types::Type; @@ -479,6 +509,11 @@ pub struct Type { /// bv.define_user_type("int_1", &my_custom_type_1); /// bv.define_user_type("int_2", &my_custom_type_2); /// ``` +#[repr(transparent)] +pub struct Type { + pub handle: *mut BNType, +} + impl Type { pub unsafe fn from_raw(handle: *mut BNType) -> Self { debug_assert!(!handle.is_null()); @@ -871,62 +906,30 @@ impl Type { } pub fn pointer<'a, A: Architecture, T: Into>>(arch: &A, ty: T) -> Ref { - let mut is_const = Conf::new(false, MIN_CONFIDENCE).into(); - let mut is_volatile = Conf::new(false, MIN_CONFIDENCE).into(); - let owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); - unsafe { - Self::ref_from_raw(BNCreatePointerType( - arch.as_ref().handle, - &owned_raw_ty, - &mut is_const, - &mut is_volatile, - ReferenceType::PointerReferenceType, - )) - } + Self::pointer_with_options(arch, ty, false, false, None) } pub fn const_pointer<'a, A: Architecture, T: Into>>( arch: &A, ty: T, ) -> Ref { - let mut is_const = Conf::new(true, MAX_CONFIDENCE).into(); - let mut is_volatile = Conf::new(false, MIN_CONFIDENCE).into(); - let owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); - unsafe { - Self::ref_from_raw(BNCreatePointerType( - arch.as_ref().handle, - &owned_raw_ty, - &mut is_const, - &mut is_volatile, - ReferenceType::PointerReferenceType, - )) - } + Self::pointer_with_options(arch, ty, true, false, None) } - pub fn pointer_of_width<'a, T: Into>>( + pub fn pointer_with_options<'a, A: Architecture, T: Into>>( + arch: &A, ty: T, - size: usize, is_const: bool, is_volatile: bool, ref_type: Option, ) -> Ref { - let mut is_const = Conf::new(is_const, MAX_CONFIDENCE).into(); - let mut is_volatile = Conf::new(is_volatile, MAX_CONFIDENCE).into(); - let owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); - unsafe { - Self::ref_from_raw(BNCreatePointerTypeOfWidth( - size, - &owned_raw_ty, - &mut is_const, - &mut is_volatile, - ref_type.unwrap_or(ReferenceType::PointerReferenceType), - )) - } + let arch_pointer_size = arch.address_size(); + Self::pointer_of_width(ty, arch_pointer_size, is_const, is_volatile, ref_type) } - pub fn pointer_with_options<'a, A: Architecture, T: Into>>( - arch: &A, + pub fn pointer_of_width<'a, T: Into>>( ty: T, + size: usize, is_const: bool, is_volatile: bool, ref_type: Option, @@ -935,8 +938,8 @@ impl Type { let mut is_volatile = Conf::new(is_volatile, MAX_CONFIDENCE).into(); let owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); unsafe { - Self::ref_from_raw(BNCreatePointerType( - arch.as_ref().handle, + Self::ref_from_raw(BNCreatePointerTypeOfWidth( + size, &owned_raw_ty, &mut is_const, &mut is_volatile, @@ -970,7 +973,7 @@ impl Debug for Type { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { // You might be tempted to rip this atrocity out and make this more "sensible". READ BELOW! // Type is a one-size fits all structure, these are actually its fields! If we wanted to - // omit some fields for different type classes what you really want to do is implement your + // omit some fields for different type classes, what you really want to do is implement your // own formatter. This is supposed to represent the structure entirely, it's not supposed to be pretty! f.debug_struct("Type") .field("type_class", &self.type_class()) @@ -1048,27 +1051,6 @@ unsafe impl CoreArrayProviderInner for Type { } } -// TODO: Remove this struct, or make it not a ZST with a terrible array provider. -/// ZST used only for `Array`. -pub struct ComponentReferencedType; - -impl CoreArrayProvider for ComponentReferencedType { - type Raw = *mut BNType; - type Context = (); - type Wrapped<'a> = &'a Type; -} - -unsafe impl CoreArrayProviderInner for ComponentReferencedType { - unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { - BNComponentFreeReferencedTypes(raw, count) - } - - unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - // SAFETY: &*mut BNType == &Type (*mut BNType == Type) - std::mem::transmute(raw) - } -} - #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct FunctionParameter { pub ty: Conf>, @@ -1105,6 +1087,7 @@ impl FunctionParameter { } } + #[allow(unused)] pub(crate) fn from_owned_raw(value: BNFunctionParameter) -> Self { let owned = Self::from_raw(&value); Self::free_raw(value); @@ -1136,987 +1119,118 @@ impl FunctionParameter { } } -#[derive(Debug, Clone, Hash, PartialEq, Eq)] -pub struct EnumerationMember { - pub name: String, - /// The associated constant value for the member. - pub value: u64, - /// Whether this is the default member for the associated [`Enumeration`]. - pub default: bool, -} - -impl EnumerationMember { - pub(crate) fn from_raw(value: &BNEnumerationMember) -> Self { - Self { - name: raw_to_string(value.name).unwrap(), - value: value.value, - default: value.isDefault, - } - } - - pub(crate) fn from_owned_raw(value: BNEnumerationMember) -> Self { - let owned = Self::from_raw(&value); - Self::free_raw(value); - owned - } - - pub(crate) fn into_raw(value: Self) -> BNEnumerationMember { - let bn_name = BnString::new(value.name); - BNEnumerationMember { - name: BnString::into_raw(bn_name), - value: value.value, - isDefault: value.default, - } - } - - pub(crate) fn free_raw(value: BNEnumerationMember) { - unsafe { BnString::free_raw(value.name) }; - } - - pub fn new(name: String, value: u64, default: bool) -> Self { - Self { - name, - value, - default, - } - } -} - #[derive(PartialEq, Eq, Hash)] -pub struct EnumerationBuilder { - pub(crate) handle: *mut BNEnumerationBuilder, +pub struct NamedTypeReference { + pub(crate) handle: *mut BNNamedTypeReference, } -impl EnumerationBuilder { - pub fn new() -> Self { - Self { - handle: unsafe { BNCreateEnumerationBuilder() }, - } - } - - pub(crate) unsafe fn from_raw(handle: *mut BNEnumerationBuilder) -> Self { +impl NamedTypeReference { + pub(crate) unsafe fn from_raw(handle: *mut BNNamedTypeReference) -> Self { + debug_assert!(!handle.is_null()); Self { handle } } - pub fn finalize(&self) -> Ref { - unsafe { Enumeration::ref_from_raw(BNFinalizeEnumerationBuilder(self.handle)) } + pub(crate) unsafe fn ref_from_raw(handle: *mut BNNamedTypeReference) -> Ref { + debug_assert!(!handle.is_null()); + Ref::new(Self { handle }) } - pub fn append(&mut self, name: &str) -> &mut Self { - let name = name.to_cstr(); - unsafe { - BNAddEnumerationBuilderMember(self.handle, name.as_ref().as_ptr() as _); - } - self + /// Create an NTR to a type that did not come directly from a BinaryView's types list. + /// That is to say, if you're referencing a new type you're GOING to add, use this. + /// You should not assign type ids yourself, that is the responsibility of the BinaryView + /// implementation after your types have been added. Just make sure the names match up and + /// the core will do the id stuff for you. + pub fn new>(type_class: NamedTypeReferenceClass, name: T) -> Ref { + let mut raw_name = QualifiedName::into_raw(name.into()); + let result = unsafe { + Self::ref_from_raw(BNCreateNamedType( + type_class, + std::ptr::null(), + &mut raw_name, + )) + }; + QualifiedName::free_raw(raw_name); + result } - pub fn insert(&mut self, name: &str, value: u64) -> &mut Self { - let name = name.to_cstr(); - unsafe { - BNAddEnumerationBuilderMemberWithValue(self.handle, name.as_ref().as_ptr() as _, value); - } - self + /// Create an NTR to a type with an existing type id, which generally means it came directly + /// from a BinaryView's types list and its id was looked up using `BinaryView::get_type_id`. + /// You should not assign type ids yourself: if you use this to reference a type you are going + /// to create but have not yet created, you may run into problems when giving your types to + /// a BinaryView. + pub fn new_with_id>( + type_class: NamedTypeReferenceClass, + type_id: &str, + name: T, + ) -> Ref { + let type_id = type_id.to_cstr(); + let mut raw_name = QualifiedName::into_raw(name.into()); + let result = unsafe { + Self::ref_from_raw(BNCreateNamedType( + type_class, + type_id.as_ref().as_ptr() as _, + &mut raw_name, + )) + }; + QualifiedName::free_raw(raw_name); + result } - pub fn replace(&mut self, id: usize, name: &str, value: u64) -> &mut Self { - let name = name.to_cstr(); - unsafe { - BNReplaceEnumerationBuilderMember(self.handle, id, name.as_ref().as_ptr() as _, value); - } - self + pub fn name(&self) -> QualifiedName { + let raw_name = unsafe { BNGetTypeReferenceName(self.handle) }; + QualifiedName::from_owned_raw(raw_name) } - pub fn remove(&mut self, id: usize) -> &mut Self { - unsafe { - BNRemoveEnumerationBuilderMember(self.handle, id); - } + pub fn id(&self) -> String { + unsafe { BnString::into_string(BNGetTypeReferenceId(self.handle)) } + } - self + pub fn class(&self) -> NamedTypeReferenceClass { + unsafe { BNGetTypeReferenceClass(self.handle) } } - pub fn members(&self) -> Vec { - unsafe { - let mut count = 0; - let members_raw_ptr = BNGetEnumerationBuilderMembers(self.handle, &mut count); - let members_raw: &[BNEnumerationMember] = - std::slice::from_raw_parts(members_raw_ptr, count); - let members = members_raw - .iter() - .map(EnumerationMember::from_raw) - .collect(); - BNFreeEnumerationMemberList(members_raw_ptr, count); - members + fn target_helper(&self, bv: &BinaryView, visited: &mut HashSet) -> Option> { + let ty = bv.type_by_id(&self.id())?; + match ty.type_class() { + TypeClass::NamedTypeReferenceClass => { + // Recurse into the NTR type until we get the target type. + let ntr = ty + .get_named_type_reference() + .expect("NTR type class should always have a valid NTR"); + match visited.insert(ntr.id()) { + true => ntr.target_helper(bv, visited), + // Cyclic reference, return None. + false => None, + } + } + // Found target type + _ => Some(ty), } } -} -impl Default for EnumerationBuilder { - fn default() -> Self { - Self::new() + /// Type referenced by this [`NamedTypeReference`]. + /// + /// Will return `None` if the reference is cyclic, or the target type does not exist. + pub fn target(&self, bv: &BinaryView) -> Option> { + self.target_helper(bv, &mut HashSet::new()) } } -impl From<&Enumeration> for EnumerationBuilder { - fn from(enumeration: &Enumeration) -> Self { - unsafe { - Self::from_raw(BNCreateEnumerationBuilderFromEnumeration( - enumeration.handle, - )) - } - } -} +impl ToOwned for NamedTypeReference { + type Owned = Ref; -impl Drop for EnumerationBuilder { - fn drop(&mut self) { - unsafe { BNFreeEnumerationBuilder(self.handle) }; + fn to_owned(&self) -> Self::Owned { + unsafe { RefCountable::inc_ref(self) } } } -#[derive(PartialEq, Eq, Hash)] -pub struct Enumeration { - pub(crate) handle: *mut BNEnumeration, -} - -impl Enumeration { - pub(crate) unsafe fn ref_from_raw(handle: *mut BNEnumeration) -> Ref { - debug_assert!(!handle.is_null()); - Ref::new(Self { handle }) +unsafe impl RefCountable for NamedTypeReference { + unsafe fn inc_ref(handle: &Self) -> Ref { + Self::ref_from_raw(BNNewNamedTypeReference(handle.handle)) } - pub fn builder() -> EnumerationBuilder { - EnumerationBuilder::new() - } - - pub fn members(&self) -> Vec { - unsafe { - let mut count = 0; - let members_raw_ptr = BNGetEnumerationMembers(self.handle, &mut count); - debug_assert!(!members_raw_ptr.is_null()); - let members_raw: &[BNEnumerationMember] = - std::slice::from_raw_parts(members_raw_ptr, count); - let members = members_raw - .iter() - .map(EnumerationMember::from_raw) - .collect(); - BNFreeEnumerationMemberList(members_raw_ptr, count); - members - } - } -} - -impl Debug for Enumeration { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Enumeration") - .field("members", &self.members()) - .finish() - } -} - -unsafe impl RefCountable for Enumeration { - unsafe fn inc_ref(handle: &Self) -> Ref { - Self::ref_from_raw(BNNewEnumerationReference(handle.handle)) - } - - unsafe fn dec_ref(handle: &Self) { - BNFreeEnumeration(handle.handle); - } -} - -impl ToOwned for Enumeration { - type Owned = Ref; - - fn to_owned(&self) -> Self::Owned { - unsafe { RefCountable::inc_ref(self) } - } -} - -#[derive(PartialEq, Eq, Hash)] -pub struct StructureBuilder { - pub(crate) handle: *mut BNStructureBuilder, -} - -/// ```no_run -/// // Includes -/// # use binaryninja::binary_view::BinaryViewExt; -/// use binaryninja::types::{MemberAccess, MemberScope, Structure, StructureBuilder, Type}; -/// -/// // Types to use in the members -/// let field_1_ty = Type::named_int(5, false, "my_weird_int_type"); -/// let field_2_ty = Type::int(4, false); -/// let field_3_ty = Type::int(8, false); -/// -/// // Assign those fields -/// let mut my_custom_struct = StructureBuilder::new(); -/// my_custom_struct -/// .insert( -/// &field_1_ty, -/// "field_1", -/// 0, -/// false, -/// MemberAccess::PublicAccess, -/// MemberScope::NoScope, -/// ) -/// .insert( -/// &field_2_ty, -/// "field_2", -/// 5, -/// false, -/// MemberAccess::PublicAccess, -/// MemberScope::NoScope, -/// ) -/// .insert( -/// &field_3_ty, -/// "field_3", -/// 9, -/// false, -/// MemberAccess::PublicAccess, -/// MemberScope::NoScope, -/// ) -/// .append( -/// &field_1_ty, -/// "field_4", -/// MemberAccess::PublicAccess, -/// MemberScope::NoScope, -/// ); -/// -/// // Convert structure to type -/// let my_custom_structure_type = Type::structure(&my_custom_struct.finalize()); -/// -/// // Add the struct to the binary view to use in analysis -/// let bv = binaryninja::load("example").unwrap(); -/// bv.define_user_type("my_custom_struct", &my_custom_structure_type); -/// ``` -impl StructureBuilder { - pub fn new() -> Self { - Self { - handle: unsafe { BNCreateStructureBuilder() }, - } - } - - pub(crate) unsafe fn from_raw(handle: *mut BNStructureBuilder) -> Self { - debug_assert!(!handle.is_null()); - Self { handle } - } - - // TODO: Document the width adjustment with alignment. - pub fn finalize(&self) -> Ref { - let raw_struct_ptr = unsafe { BNFinalizeStructureBuilder(self.handle) }; - unsafe { Structure::ref_from_raw(raw_struct_ptr) } - } - - /// Sets the width of the [`StructureBuilder`] to the new width. - /// - /// This will remove all previously inserted members outside the new width. This is done by computing - /// the member access range (member offset + member width) and if it is larger than the new width - /// it will be removed. - pub fn width(&mut self, width: u64) -> &mut Self { - unsafe { - BNSetStructureBuilderWidth(self.handle, width); - } - self - } - - pub fn alignment(&mut self, alignment: usize) -> &mut Self { - unsafe { - BNSetStructureBuilderAlignment(self.handle, alignment); - } - self - } - - /// Sets whether the [`StructureBuilder`] is packed. - /// - /// If set the alignment of the structure will be `1`. You do not need to set the alignment to `1`. - pub fn packed(&mut self, packed: bool) -> &mut Self { - unsafe { - BNSetStructureBuilderPacked(self.handle, packed); - } - self - } - - pub fn structure_type(&mut self, t: StructureType) -> &mut Self { - unsafe { BNSetStructureBuilderType(self.handle, t) }; - self - } - - pub fn pointer_offset(&mut self, offset: i64) -> &mut Self { - unsafe { BNSetStructureBuilderPointerOffset(self.handle, offset) }; - self - } - - pub fn propagates_data_var_refs(&mut self, propagates: bool) -> &mut Self { - unsafe { BNSetStructureBuilderPropagatesDataVariableReferences(self.handle, propagates) }; - self - } - - pub fn base_structures(&mut self, bases: &[BaseStructure]) -> &mut Self { - let raw_base_structs: Vec = - bases.iter().map(BaseStructure::into_owned_raw).collect(); - unsafe { - BNSetBaseStructuresForStructureBuilder( - self.handle, - raw_base_structs.as_ptr() as *mut _, - raw_base_structs.len(), - ) - }; - self - } - - /// Append a member at the next available byte offset. - /// - /// Otherwise, consider using: - /// - /// - [`StructureBuilder::insert_member`] - /// - [`StructureBuilder::insert`] - /// - [`StructureBuilder::insert_bitwise`] - pub fn append<'a, T: Into>>( - &mut self, - ty: T, - name: &str, - access: MemberAccess, - scope: MemberScope, - ) -> &mut Self { - let name = name.to_cstr(); - let owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); - unsafe { - BNAddStructureBuilderMember( - self.handle, - &owned_raw_ty, - name.as_ref().as_ptr() as _, - access, - scope, - ); - } - self - } - - /// Insert an already constructed [`StructureMember`]. - /// - /// Otherwise, consider using: - /// - /// - [`StructureBuilder::append`] - /// - [`StructureBuilder::insert`] - /// - [`StructureBuilder::insert_bitwise`] - pub fn insert_member( - &mut self, - member: StructureMember, - overwrite_existing: bool, - ) -> &mut Self { - self.insert_bitwise( - &member.ty, - &member.name, - member.bit_offset(), - member.bit_width, - overwrite_existing, - member.access, - member.scope, - ); - self - } - - /// Inserts a member at the `offset` (in bytes). - /// - /// If you need to insert a member at a specific bit within a given byte (like a bitfield), you - /// can use [`StructureBuilder::insert_bitwise`]. - pub fn insert<'a, T: Into>>( - &mut self, - ty: T, - name: &str, - offset: u64, - overwrite_existing: bool, - access: MemberAccess, - scope: MemberScope, - ) -> &mut Self { - self.insert_bitwise( - ty, - name, - offset * 8, - None, - overwrite_existing, - access, - scope, - ) - } - - /// Inserts a member at `bit_offset` with an optional `bit_width`. - /// - /// NOTE: The `bit_offset` is relative to the start of the structure, for example, passing `8` will place - /// the field at the start of the byte `0x1`. - pub fn insert_bitwise<'a, T: Into>>( - &mut self, - ty: T, - name: &str, - bit_offset: u64, - bit_width: Option, - overwrite_existing: bool, - access: MemberAccess, - scope: MemberScope, - ) -> &mut Self { - let name = name.to_cstr(); - let owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); - let byte_offset = bit_offset / 8; - let bit_position = bit_offset % 8; - unsafe { - BNAddStructureBuilderMemberAtOffset( - self.handle, - &owned_raw_ty, - name.as_ref().as_ptr() as _, - byte_offset, - overwrite_existing, - access, - scope, - bit_position as u8, - bit_width.unwrap_or(0), - ); - } - self - } - - pub fn replace<'a, T: Into>>( - &mut self, - index: usize, - ty: T, - name: &str, - overwrite_existing: bool, - ) -> &mut Self { - let name = name.to_cstr(); - let owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); - unsafe { - BNReplaceStructureBuilderMember( - self.handle, - index, - &owned_raw_ty, - name.as_ref().as_ptr() as _, - overwrite_existing, - ) - } - self - } - - /// Removes the member at a given index. - pub fn remove(&mut self, index: usize) -> &mut Self { - unsafe { BNRemoveStructureBuilderMember(self.handle, index) }; - self - } - - // TODO: We should add BNGetStructureBuilderAlignedWidth - /// Gets the current **unaligned** width of the structure. - /// - /// This cannot be used to accurately get the width of a non-packed structure. - pub fn current_width(&self) -> u64 { - unsafe { BNGetStructureBuilderWidth(self.handle) } - } -} - -impl From<&Structure> for StructureBuilder { - fn from(structure: &Structure) -> StructureBuilder { - unsafe { Self::from_raw(BNCreateStructureBuilderFromStructure(structure.handle)) } - } -} - -impl From> for StructureBuilder { - fn from(members: Vec) -> StructureBuilder { - let mut builder = StructureBuilder::new(); - for member in members { - builder.insert_member(member, false); - } - builder - } -} - -impl Drop for StructureBuilder { - fn drop(&mut self) { - unsafe { BNFreeStructureBuilder(self.handle) }; - } -} - -impl Default for StructureBuilder { - fn default() -> Self { - Self::new() - } -} - -#[derive(PartialEq, Eq, Hash)] -pub struct Structure { - pub(crate) handle: *mut BNStructure, -} - -impl Structure { - pub(crate) unsafe fn ref_from_raw(handle: *mut BNStructure) -> Ref { - debug_assert!(!handle.is_null()); - Ref::new(Self { handle }) - } - - pub fn builder() -> StructureBuilder { - StructureBuilder::new() - } - - pub fn width(&self) -> u64 { - unsafe { BNGetStructureWidth(self.handle) } - } - - pub fn structure_type(&self) -> StructureType { - unsafe { BNGetStructureType(self.handle) } - } - - /// Retrieve the members that are accessible at a given offset. - /// - /// The reason for this being plural is that members may overlap and the offset is in bytes - /// where a bitfield may contain multiple members at the given byte. - /// - /// Unions are also represented as structures and will cause this function to return - /// **all** members that can reach that offset. - /// - /// We must pass a [`TypeContainer`] here so that we can resolve base structure members, as they - /// are treated as members through this function. Typically, you get the [`TypeContainer`] - /// through the binary view with [`BinaryViewExt::type_container`]. - pub fn members_at_offset( - &self, - container: &TypeContainer, - offset: u64, - ) -> Vec { - self.members_including_inherited(container) - .into_iter() - .filter(|m| m.member.is_offset_valid(offset)) - .map(|m| m.member) - .collect() - } - - /// Return the list of non-inherited structure members. - /// - /// If you want to get all members, including ones inherited from base structures, - /// use [`Structure::members_including_inherited`] instead. - pub fn members(&self) -> Vec { - unsafe { - let mut count = 0; - let members_raw_ptr: *mut BNStructureMember = - BNGetStructureMembers(self.handle, &mut count); - debug_assert!(!members_raw_ptr.is_null()); - let members_raw = std::slice::from_raw_parts(members_raw_ptr, count); - let members = members_raw.iter().map(StructureMember::from_raw).collect(); - BNFreeStructureMemberList(members_raw_ptr, count); - members - } - } - - /// Returns the list of all structure members, including inherited ones. - /// - /// Because we must traverse through base structures, we have to provide the [`TypeContainer`]; - /// in most cases it is ok to provide the binary views container via [`BinaryViewExt::type_container`]. - pub fn members_including_inherited( - &self, - container: &TypeContainer, - ) -> Vec { - unsafe { - let mut count = 0; - let members_raw_ptr: *mut BNInheritedStructureMember = - BNGetStructureMembersIncludingInherited( - self.handle, - container.handle.as_ptr(), - &mut count, - ); - debug_assert!(!members_raw_ptr.is_null()); - let members_raw = std::slice::from_raw_parts(members_raw_ptr, count); - let members = members_raw - .iter() - .map(InheritedStructureMember::from_raw) - .collect(); - BNFreeInheritedStructureMemberList(members_raw_ptr, count); - members - } - } - - /// Retrieve the list of base structures for the structure. These base structures are what give - /// a structure inherited members. - pub fn base_structures(&self) -> Vec { - let mut count = 0; - let bases_raw_ptr = unsafe { BNGetBaseStructuresForStructure(self.handle, &mut count) }; - debug_assert!(!bases_raw_ptr.is_null()); - let bases_raw = unsafe { std::slice::from_raw_parts(bases_raw_ptr, count) }; - let bases = bases_raw.iter().map(BaseStructure::from_raw).collect(); - unsafe { BNFreeBaseStructureList(bases_raw_ptr, count) }; - bases - } - - /// Whether the structure is packed or not. - pub fn is_packed(&self) -> bool { - unsafe { BNIsStructurePacked(self.handle) } - } - - pub fn alignment(&self) -> usize { - unsafe { BNGetStructureAlignment(self.handle) } - } -} - -impl Debug for Structure { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.debug_struct("Structure") - .field("width", &self.width()) - .field("alignment", &self.alignment()) - .field("packed", &self.is_packed()) - .field("structure_type", &self.structure_type()) - .field("base_structures", &self.base_structures()) - .field("members", &self.members()) - .finish() - } -} - -unsafe impl RefCountable for Structure { - unsafe fn inc_ref(handle: &Self) -> Ref { - Self::ref_from_raw(BNNewStructureReference(handle.handle)) - } - - unsafe fn dec_ref(handle: &Self) { - BNFreeStructure(handle.handle); - } -} - -impl ToOwned for Structure { - type Owned = Ref; - - fn to_owned(&self) -> Self::Owned { - unsafe { RefCountable::inc_ref(self) } - } -} - -#[derive(Debug, Clone, Hash, PartialEq, Eq)] -pub struct StructureMember { - pub ty: Conf>, - // TODO: Shouldnt this be a QualifiedName? The ffi says no... - pub name: String, - /// The byte offset of the member. - pub offset: u64, - pub access: MemberAccess, - pub scope: MemberScope, - /// The bit position relative to the byte offset. - pub bit_position: Option, - pub bit_width: Option, -} - -impl StructureMember { - pub(crate) fn from_raw(value: &BNStructureMember) -> Self { - Self { - ty: Conf::new( - unsafe { Type::from_raw(value.type_) }.to_owned(), - value.typeConfidence, - ), - // TODO: I dislike using this function here. - name: raw_to_string(value.name as *mut _).unwrap(), - offset: value.offset, - access: value.access, - scope: value.scope, - bit_position: match value.bitPosition { - 0 => None, - _ => Some(value.bitPosition), - }, - bit_width: match value.bitWidth { - 0 => None, - _ => Some(value.bitWidth), - }, - } - } - - pub(crate) fn from_owned_raw(value: BNStructureMember) -> Self { - let owned = Self::from_raw(&value); - Self::free_raw(value); - owned - } - - pub(crate) fn into_raw(value: Self) -> BNStructureMember { - let bn_name = BnString::new(value.name); - BNStructureMember { - type_: unsafe { Ref::into_raw(value.ty.contents) }.handle, - name: BnString::into_raw(bn_name), - offset: value.offset, - typeConfidence: value.ty.confidence, - access: value.access, - scope: value.scope, - bitPosition: value.bit_position.unwrap_or(0), - bitWidth: value.bit_width.unwrap_or(0), - } - } - - pub(crate) fn free_raw(value: BNStructureMember) { - let _ = unsafe { Type::ref_from_raw(value.type_) }; - unsafe { BnString::free_raw(value.name) }; - } - - pub fn new( - ty: Conf>, - name: String, - offset: u64, - access: MemberAccess, - scope: MemberScope, - ) -> Self { - Self { - ty, - name, - offset, - access, - scope, - bit_position: None, - bit_width: None, - } - } - - pub fn new_bitfield( - ty: Conf>, - name: String, - bit_offset: u64, - bit_width: u8, - access: MemberAccess, - scope: MemberScope, - ) -> Self { - Self { - ty, - name, - offset: bit_offset / 8, - access, - scope, - bit_position: Some((bit_offset % 8) as u8), - bit_width: Some(bit_width), - } - } - - // TODO: Do we count bitwidth here? - /// Whether the offset within the accessible range of the member. - pub fn is_offset_valid(&self, offset: u64) -> bool { - self.offset <= offset && offset < self.offset + self.ty.contents.width() - } - - /// Member offset in bits. - pub fn bit_offset(&self) -> u64 { - (self.offset * 8) + self.bit_position.unwrap_or(0) as u64 - } -} - -impl CoreArrayProvider for StructureMember { - type Raw = BNStructureMember; - type Context = (); - type Wrapped<'a> = Self; -} - -unsafe impl CoreArrayProviderInner for StructureMember { - unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { - BNFreeStructureMemberList(raw, count) - } - - unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - Self::from_raw(raw) - } -} - -#[derive(Debug, Clone, Hash, PartialEq, Eq)] -pub struct InheritedStructureMember { - pub base: Ref, - pub base_offset: u64, - pub member: StructureMember, - pub member_index: usize, -} - -impl InheritedStructureMember { - pub(crate) fn from_raw(value: &BNInheritedStructureMember) -> Self { - Self { - base: unsafe { NamedTypeReference::from_raw(value.base) }.to_owned(), - base_offset: value.baseOffset, - member: StructureMember::from_raw(&value.member), - member_index: value.memberIndex, - } - } - - pub(crate) fn from_owned_raw(value: BNInheritedStructureMember) -> Self { - let owned = Self::from_raw(&value); - Self::free_raw(value); - owned - } - - pub(crate) fn into_raw(value: Self) -> BNInheritedStructureMember { - BNInheritedStructureMember { - base: unsafe { Ref::into_raw(value.base) }.handle, - baseOffset: value.base_offset, - member: StructureMember::into_raw(value.member), - memberIndex: value.member_index, - } - } - - pub(crate) fn free_raw(value: BNInheritedStructureMember) { - let _ = unsafe { NamedTypeReference::ref_from_raw(value.base) }; - StructureMember::free_raw(value.member); - } - - pub fn new( - base: Ref, - base_offset: u64, - member: StructureMember, - member_index: usize, - ) -> Self { - Self { - base, - base_offset, - member, - member_index, - } - } -} - -#[derive(Debug, Clone, Hash, PartialEq, Eq)] -pub struct BaseStructure { - pub ty: Ref, - pub offset: u64, - pub width: u64, -} - -impl BaseStructure { - pub(crate) fn from_raw(value: &BNBaseStructure) -> Self { - Self { - ty: unsafe { NamedTypeReference::from_raw(value.type_) }.to_owned(), - offset: value.offset, - width: value.width, - } - } - - pub(crate) fn from_owned_raw(value: BNBaseStructure) -> Self { - let owned = Self::from_raw(&value); - Self::free_raw(value); - owned - } - - pub(crate) fn into_raw(value: Self) -> BNBaseStructure { - BNBaseStructure { - type_: unsafe { Ref::into_raw(value.ty) }.handle, - offset: value.offset, - width: value.width, - } - } - - pub(crate) fn into_owned_raw(value: &Self) -> BNBaseStructure { - BNBaseStructure { - type_: value.ty.handle, - offset: value.offset, - width: value.width, - } - } - - pub(crate) fn free_raw(value: BNBaseStructure) { - let _ = unsafe { NamedTypeReference::ref_from_raw(value.type_) }; - } - - pub fn new(ty: Ref, offset: u64, width: u64) -> Self { - Self { ty, offset, width } - } -} - -#[derive(PartialEq, Eq, Hash)] -pub struct NamedTypeReference { - pub(crate) handle: *mut BNNamedTypeReference, -} - -impl NamedTypeReference { - pub(crate) unsafe fn from_raw(handle: *mut BNNamedTypeReference) -> Self { - debug_assert!(!handle.is_null()); - Self { handle } - } - - pub(crate) unsafe fn ref_from_raw(handle: *mut BNNamedTypeReference) -> Ref { - debug_assert!(!handle.is_null()); - Ref::new(Self { handle }) - } - - /// Create an NTR to a type that did not come directly from a BinaryView's types list. - /// That is to say, if you're referencing a new type you're GOING to add, use this. - /// You should not assign type ids yourself, that is the responsibility of the BinaryView - /// implementation after your types have been added. Just make sure the names match up and - /// the core will do the id stuff for you. - pub fn new>(type_class: NamedTypeReferenceClass, name: T) -> Ref { - let mut raw_name = QualifiedName::into_raw(name.into()); - let result = unsafe { - Self::ref_from_raw(BNCreateNamedType( - type_class, - std::ptr::null(), - &mut raw_name, - )) - }; - QualifiedName::free_raw(raw_name); - result - } - - /// Create an NTR to a type with an existing type id, which generally means it came directly - /// from a BinaryView's types list and its id was looked up using `BinaryView::get_type_id`. - /// You should not assign type ids yourself: if you use this to reference a type you are going - /// to create but have not yet created, you may run into problems when giving your types to - /// a BinaryView. - pub fn new_with_id>( - type_class: NamedTypeReferenceClass, - type_id: &str, - name: T, - ) -> Ref { - let type_id = type_id.to_cstr(); - let mut raw_name = QualifiedName::into_raw(name.into()); - let result = unsafe { - Self::ref_from_raw(BNCreateNamedType( - type_class, - type_id.as_ref().as_ptr() as _, - &mut raw_name, - )) - }; - QualifiedName::free_raw(raw_name); - result - } - - pub fn name(&self) -> QualifiedName { - let raw_name = unsafe { BNGetTypeReferenceName(self.handle) }; - QualifiedName::from_owned_raw(raw_name) - } - - pub fn id(&self) -> String { - unsafe { BnString::into_string(BNGetTypeReferenceId(self.handle)) } - } - - pub fn class(&self) -> NamedTypeReferenceClass { - unsafe { BNGetTypeReferenceClass(self.handle) } - } - - fn target_helper(&self, bv: &BinaryView, visited: &mut HashSet) -> Option> { - let ty = bv.type_by_id(&self.id())?; - match ty.type_class() { - TypeClass::NamedTypeReferenceClass => { - // Recurse into the NTR type until we get the target type. - let ntr = ty - .get_named_type_reference() - .expect("NTR type class should always have a valid NTR"); - match visited.insert(ntr.id()) { - true => ntr.target_helper(bv, visited), - // Cyclic reference, return None. - false => None, - } - } - // Found target type - _ => Some(ty), - } - } - - /// Type referenced by this [`NamedTypeReference`]. - /// - /// Will return `None` if the reference is cyclic, or the target type does not exist. - pub fn target(&self, bv: &BinaryView) -> Option> { - self.target_helper(bv, &mut HashSet::new()) - } -} - -impl ToOwned for NamedTypeReference { - type Owned = Ref; - - fn to_owned(&self) -> Self::Owned { - unsafe { RefCountable::inc_ref(self) } - } -} - -unsafe impl RefCountable for NamedTypeReference { - unsafe fn inc_ref(handle: &Self) -> Ref { - Self::ref_from_raw(BNNewNamedTypeReference(handle.handle)) - } - - unsafe fn dec_ref(handle: &Self) { - BNFreeNamedTypeReference(handle.handle) + unsafe fn dec_ref(handle: &Self) { + BNFreeNamedTypeReference(handle.handle) } } @@ -2126,221 +1240,6 @@ impl Debug for NamedTypeReference { } } -// TODO: Document usage, specifically how to make a qualified name and why it exists. -#[derive(Default, Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)] -pub struct QualifiedName { - // TODO: Make this Option where default is "::". - pub separator: String, - pub items: Vec, -} - -impl QualifiedName { - pub(crate) fn from_raw(value: &BNQualifiedName) -> Self { - // TODO: This could be improved... - let raw_names = unsafe { std::slice::from_raw_parts(value.name, value.nameCount) }; - let items = raw_names - .iter() - .filter_map(|&raw_name| raw_to_string(raw_name as *const _)) - .collect(); - let separator = raw_to_string(value.join).unwrap(); - Self { items, separator } - } - - pub(crate) fn from_owned_raw(value: BNQualifiedName) -> Self { - let result = Self::from_raw(&value); - Self::free_raw(value); - result - } - - pub fn into_raw(value: Self) -> BNQualifiedName { - let bn_join = BnString::new(&value.separator); - BNQualifiedName { - // NOTE: Leaking string list must be freed by core or us! - name: strings_to_string_list(&value.items), - // NOTE: Leaking string must be freed by core or us! - join: BnString::into_raw(bn_join), - nameCount: value.items.len(), - } - } - - pub(crate) fn free_raw(value: BNQualifiedName) { - unsafe { BnString::free_raw(value.join) }; - unsafe { BNFreeStringList(value.name, value.nameCount) }; - } - - pub fn new(items: Vec) -> Self { - Self::new_with_separator(items, "::".to_string()) - } - - pub fn new_with_separator(items: Vec, separator: String) -> Self { - Self { items, separator } - } - - pub fn with_item(&self, item: impl Into) -> Self { - let mut items = self.items.clone(); - items.push(item.into()); - Self::new_with_separator(items, self.separator.clone()) - } - - pub fn push(&mut self, item: String) { - self.items.push(item); - } - - pub fn pop(&mut self) -> Option { - self.items.pop() - } - - pub fn insert(&mut self, index: usize, item: String) { - if index <= self.items.len() { - self.items.insert(index, item); - } - } - - pub fn split_last(&self) -> Option<(String, QualifiedName)> { - self.items.split_last().map(|(a, b)| { - ( - a.to_owned(), - QualifiedName::new_with_separator(b.to_vec(), self.separator.clone()), - ) - }) - } - - /// Replaces all occurrences of a substring with another string in all items of the `QualifiedName` - /// and returns an owned version of the modified `QualifiedName`. - /// - /// # Example - /// - /// ``` - /// use binaryninja::types::QualifiedName; - /// - /// let qualified_name = - /// QualifiedName::new(vec!["my::namespace".to_string(), "mytype".to_string()]); - /// let replaced = qualified_name.replace("my", "your"); - /// assert_eq!( - /// replaced.items, - /// vec!["your::namespace".to_string(), "yourtype".to_string()] - /// ); - /// ``` - pub fn replace(&self, from: &str, to: &str) -> Self { - Self { - items: self - .items - .iter() - .map(|item| item.replace(from, to)) - .collect(), - separator: self.separator.clone(), - } - } - - /// Returns the last item, or `None` if it is empty. - pub fn last(&self) -> Option<&String> { - self.items.last() - } - - /// Returns a mutable reference to the last item, or `None` if it is empty. - pub fn last_mut(&mut self) -> Option<&mut String> { - self.items.last_mut() - } - - pub fn len(&self) -> usize { - self.items.len() - } - - /// A [`QualifiedName`] is empty if it has no items. - /// - /// If you want to know if the unqualified name is empty (i.e. no characters) - /// you must first convert the qualified name to unqualified via the `to_string` method. - pub fn is_empty(&self) -> bool { - self.items.is_empty() - } -} - -impl From for QualifiedName { - fn from(value: String) -> Self { - Self { - items: vec![value], - // TODO: See comment in struct def. - separator: String::from("::"), - } - } -} - -impl From<&str> for QualifiedName { - fn from(value: &str) -> Self { - Self::from(value.to_string()) - } -} - -impl From<&String> for QualifiedName { - fn from(value: &String) -> Self { - Self::from(value.to_owned()) - } -} - -impl From> for QualifiedName { - fn from(value: Cow<'_, str>) -> Self { - Self::from(value.to_string()) - } -} - -impl From> for QualifiedName { - fn from(value: Vec) -> Self { - Self::new(value) - } -} - -impl From> for QualifiedName { - fn from(value: Vec<&str>) -> Self { - value - .iter() - .map(ToString::to_string) - .collect::>() - .into() - } -} - -impl From for String { - fn from(value: QualifiedName) -> Self { - value.to_string() - } -} - -impl Index for QualifiedName { - type Output = String; - - fn index(&self, index: usize) -> &Self::Output { - &self.items[index] - } -} - -impl IndexMut for QualifiedName { - fn index_mut(&mut self, index: usize) -> &mut Self::Output { - &mut self.items[index] - } -} - -impl Display for QualifiedName { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "{}", self.items.join(&self.separator)) - } -} - -impl CoreArrayProvider for QualifiedName { - type Raw = BNQualifiedName; - type Context = (); - type Wrapped<'a> = Self; -} - -unsafe impl CoreArrayProviderInner for QualifiedName { - unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { - BNFreeTypeNameList(raw, count); - } - - unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { - QualifiedName::from_raw(raw) - } -} - #[derive(Debug, Clone, Hash, PartialEq, Eq)] pub struct QualifiedNameAndType { pub name: QualifiedName, @@ -2435,6 +1334,7 @@ impl QualifiedNameTypeAndId { } } + #[allow(unused)] pub(crate) fn from_owned_raw(value: BNQualifiedNameTypeAndId) -> Self { let owned = Self::from_raw(&value); Self::free_raw(value); @@ -2495,6 +1395,7 @@ impl NameAndType { } } + #[allow(unused)] pub(crate) fn from_owned_raw(value: BNNameAndType) -> Self { let owned = Self::from_raw(&value); Self::free_raw(value); diff --git a/rust/src/type_archive.rs b/rust/src/types/archive.rs similarity index 99% rename from rust/src/type_archive.rs rename to rust/src/types/archive.rs index 4a04720500..15f4cbfeb4 100644 --- a/rust/src/type_archive.rs +++ b/rust/src/types/archive.rs @@ -1,6 +1,6 @@ use crate::progress::{NoProgressCallback, ProgressCallback}; use binaryninjacore_sys::*; -use std::ffi::{c_char, c_void, CStr}; +use std::ffi::{c_char, c_void, CStr, CString}; use std::fmt::{Debug, Display, Formatter}; use std::hash::Hash; use std::path::{Path, PathBuf}; @@ -11,8 +11,9 @@ use crate::metadata::Metadata; use crate::platform::Platform; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable}; use crate::string::{raw_to_string, BnString, IntoCStr}; -use crate::type_container::TypeContainer; -use crate::types::{QualifiedName, QualifiedNameAndType, QualifiedNameTypeAndId, Type}; +use crate::types::{ + QualifiedName, QualifiedNameAndType, QualifiedNameTypeAndId, Type, TypeContainer, +}; #[repr(transparent)] #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] @@ -57,6 +58,14 @@ unsafe impl CoreArrayProviderInner for TypeArchiveSnapshotId { } } +impl IntoCStr for TypeArchiveSnapshotId { + type Result = CString; + + fn to_cstr(self) -> Self::Result { + self.to_string().to_cstr() + } +} + /// Type Archives are a collection of types which can be shared between different analysis /// sessions and are backed by a database file on disk. Their types can be modified, and /// a history of previous versions of types is stored in snapshots in the archive. diff --git a/rust/src/type_container.rs b/rust/src/types/container.rs similarity index 99% rename from rust/src/type_container.rs rename to rust/src/types/container.rs index e8c915dfa4..5096ac78c0 100644 --- a/rust/src/type_container.rs +++ b/rust/src/types/container.rs @@ -12,8 +12,7 @@ use crate::platform::Platform; use crate::progress::{NoProgressCallback, ProgressCallback}; use crate::rc::{Array, Ref}; use crate::string::{raw_to_string, BnString, IntoCStr}; -use crate::type_parser::{TypeParserError, TypeParserResult}; -use crate::types::{QualifiedName, QualifiedNameAndType, Type}; +use crate::types::{QualifiedName, QualifiedNameAndType, Type, TypeParserError, TypeParserResult}; use binaryninjacore_sys::*; use std::collections::HashMap; use std::ffi::{c_char, c_void}; diff --git a/rust/src/types/enumeration.rs b/rust/src/types/enumeration.rs new file mode 100644 index 0000000000..70c266e1f7 --- /dev/null +++ b/rust/src/types/enumeration.rs @@ -0,0 +1,201 @@ +use crate::rc::{Ref, RefCountable}; +use crate::string::{raw_to_string, BnString, IntoCStr}; +use binaryninjacore_sys::*; +use std::fmt::{Debug, Formatter}; + +#[derive(PartialEq, Eq, Hash)] +pub struct EnumerationBuilder { + pub(crate) handle: *mut BNEnumerationBuilder, +} + +impl EnumerationBuilder { + pub fn new() -> Self { + Self { + handle: unsafe { BNCreateEnumerationBuilder() }, + } + } + + pub(crate) unsafe fn from_raw(handle: *mut BNEnumerationBuilder) -> Self { + Self { handle } + } + + pub fn finalize(&self) -> Ref { + unsafe { Enumeration::ref_from_raw(BNFinalizeEnumerationBuilder(self.handle)) } + } + + pub fn append(&mut self, name: &str) -> &mut Self { + let name = name.to_cstr(); + unsafe { + BNAddEnumerationBuilderMember(self.handle, name.as_ref().as_ptr() as _); + } + self + } + + pub fn insert(&mut self, name: &str, value: u64) -> &mut Self { + let name = name.to_cstr(); + unsafe { + BNAddEnumerationBuilderMemberWithValue(self.handle, name.as_ref().as_ptr() as _, value); + } + self + } + + pub fn replace(&mut self, id: usize, name: &str, value: u64) -> &mut Self { + let name = name.to_cstr(); + unsafe { + BNReplaceEnumerationBuilderMember(self.handle, id, name.as_ref().as_ptr() as _, value); + } + self + } + + pub fn remove(&mut self, id: usize) -> &mut Self { + unsafe { + BNRemoveEnumerationBuilderMember(self.handle, id); + } + + self + } + + pub fn members(&self) -> Vec { + unsafe { + let mut count = 0; + let members_raw_ptr = BNGetEnumerationBuilderMembers(self.handle, &mut count); + let members_raw: &[BNEnumerationMember] = + std::slice::from_raw_parts(members_raw_ptr, count); + let members = members_raw + .iter() + .map(EnumerationMember::from_raw) + .collect(); + BNFreeEnumerationMemberList(members_raw_ptr, count); + members + } + } +} + +impl Default for EnumerationBuilder { + fn default() -> Self { + Self::new() + } +} + +impl From<&Enumeration> for EnumerationBuilder { + fn from(enumeration: &Enumeration) -> Self { + unsafe { + Self::from_raw(BNCreateEnumerationBuilderFromEnumeration( + enumeration.handle, + )) + } + } +} + +impl Drop for EnumerationBuilder { + fn drop(&mut self) { + unsafe { BNFreeEnumerationBuilder(self.handle) }; + } +} + +#[derive(PartialEq, Eq, Hash)] +pub struct Enumeration { + pub(crate) handle: *mut BNEnumeration, +} + +impl Enumeration { + pub(crate) unsafe fn ref_from_raw(handle: *mut BNEnumeration) -> Ref { + debug_assert!(!handle.is_null()); + Ref::new(Self { handle }) + } + + pub fn builder() -> EnumerationBuilder { + EnumerationBuilder::new() + } + + pub fn members(&self) -> Vec { + unsafe { + let mut count = 0; + let members_raw_ptr = BNGetEnumerationMembers(self.handle, &mut count); + debug_assert!(!members_raw_ptr.is_null()); + let members_raw: &[BNEnumerationMember] = + std::slice::from_raw_parts(members_raw_ptr, count); + let members = members_raw + .iter() + .map(EnumerationMember::from_raw) + .collect(); + BNFreeEnumerationMemberList(members_raw_ptr, count); + members + } + } +} + +impl Debug for Enumeration { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Enumeration") + .field("members", &self.members()) + .finish() + } +} + +unsafe impl RefCountable for Enumeration { + unsafe fn inc_ref(handle: &Self) -> Ref { + Self::ref_from_raw(BNNewEnumerationReference(handle.handle)) + } + + unsafe fn dec_ref(handle: &Self) { + BNFreeEnumeration(handle.handle); + } +} + +impl ToOwned for Enumeration { + type Owned = Ref; + + fn to_owned(&self) -> Self::Owned { + unsafe { RefCountable::inc_ref(self) } + } +} + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct EnumerationMember { + pub name: String, + /// The associated constant value for the member. + pub value: u64, + /// Whether this is the default member for the associated [`Enumeration`]. + pub default: bool, +} + +impl EnumerationMember { + pub(crate) fn from_raw(value: &BNEnumerationMember) -> Self { + Self { + name: raw_to_string(value.name).unwrap(), + value: value.value, + default: value.isDefault, + } + } + + #[allow(unused)] + pub(crate) fn from_owned_raw(value: BNEnumerationMember) -> Self { + let owned = Self::from_raw(&value); + Self::free_raw(value); + owned + } + + #[allow(unused)] + pub(crate) fn into_raw(value: Self) -> BNEnumerationMember { + let bn_name = BnString::new(value.name); + BNEnumerationMember { + name: BnString::into_raw(bn_name), + value: value.value, + isDefault: value.default, + } + } + + #[allow(unused)] + pub(crate) fn free_raw(value: BNEnumerationMember) { + unsafe { BnString::free_raw(value.name) }; + } + + pub fn new(name: String, value: u64, default: bool) -> Self { + Self { + name, + value, + default, + } + } +} diff --git a/rust/src/type_library.rs b/rust/src/types/library.rs similarity index 100% rename from rust/src/type_library.rs rename to rust/src/types/library.rs diff --git a/rust/src/type_parser.rs b/rust/src/types/parser.rs similarity index 99% rename from rust/src/type_parser.rs rename to rust/src/types/parser.rs index 8ad0725fb5..1ee071882c 100644 --- a/rust/src/type_parser.rs +++ b/rust/src/types/parser.rs @@ -7,8 +7,7 @@ use std::ptr::NonNull; use crate::platform::Platform; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref}; use crate::string::{raw_to_string, BnString, IntoCStr}; -use crate::type_container::TypeContainer; -use crate::types::{QualifiedName, QualifiedNameAndType, Type}; +use crate::types::{QualifiedName, QualifiedNameAndType, Type, TypeContainer}; pub type TypeParserErrorSeverity = BNTypeParserErrorSeverity; pub type TypeParserOption = BNTypeParserOption; diff --git a/rust/src/type_printer.rs b/rust/src/types/printer.rs similarity index 99% rename from rust/src/type_printer.rs rename to rust/src/types/printer.rs index a7495f1c16..d168434eff 100644 --- a/rust/src/type_printer.rs +++ b/rust/src/types/printer.rs @@ -5,8 +5,7 @@ use crate::disassembly::InstructionTextToken; use crate::platform::Platform; use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref}; use crate::string::{raw_to_string, BnString, IntoCStr}; -use crate::type_container::TypeContainer; -use crate::types::{NamedTypeReference, QualifiedName, QualifiedNameAndType, Type}; +use crate::types::{NamedTypeReference, QualifiedName, QualifiedNameAndType, Type, TypeContainer}; use binaryninjacore_sys::*; use std::ffi::{c_char, c_int, c_void}; use std::ptr::NonNull; diff --git a/rust/src/types/structure.rs b/rust/src/types/structure.rs new file mode 100644 index 0000000000..7de466a4a9 --- /dev/null +++ b/rust/src/types/structure.rs @@ -0,0 +1,696 @@ +use crate::confidence::Conf; +use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Ref, RefCountable}; +use crate::string::{raw_to_string, BnString, IntoCStr}; +use crate::types::{ + MemberAccess, MemberScope, NamedTypeReference, StructureType, Type, TypeContainer, +}; +use binaryninjacore_sys::*; +use std::fmt::{Debug, Formatter}; + +// Needed for doc comments +#[allow(unused)] +use crate::binary_view::BinaryViewExt; + +#[derive(PartialEq, Eq, Hash)] +pub struct StructureBuilder { + pub(crate) handle: *mut BNStructureBuilder, +} + +/// ```no_run +/// // Includes +/// # use binaryninja::binary_view::BinaryViewExt; +/// use binaryninja::types::{MemberAccess, MemberScope, Structure, StructureBuilder, Type}; +/// +/// // Types to use in the members +/// let field_1_ty = Type::named_int(5, false, "my_weird_int_type"); +/// let field_2_ty = Type::int(4, false); +/// let field_3_ty = Type::int(8, false); +/// +/// // Assign those fields +/// let mut my_custom_struct = StructureBuilder::new(); +/// my_custom_struct +/// .insert( +/// &field_1_ty, +/// "field_1", +/// 0, +/// false, +/// MemberAccess::PublicAccess, +/// MemberScope::NoScope, +/// ) +/// .insert( +/// &field_2_ty, +/// "field_2", +/// 5, +/// false, +/// MemberAccess::PublicAccess, +/// MemberScope::NoScope, +/// ) +/// .insert( +/// &field_3_ty, +/// "field_3", +/// 9, +/// false, +/// MemberAccess::PublicAccess, +/// MemberScope::NoScope, +/// ) +/// .append( +/// &field_1_ty, +/// "field_4", +/// MemberAccess::PublicAccess, +/// MemberScope::NoScope, +/// ); +/// +/// // Convert structure to type +/// let my_custom_structure_type = Type::structure(&my_custom_struct.finalize()); +/// +/// // Add the struct to the binary view to use in analysis +/// let bv = binaryninja::load("example").unwrap(); +/// bv.define_user_type("my_custom_struct", &my_custom_structure_type); +/// ``` +impl StructureBuilder { + pub fn new() -> Self { + Self { + handle: unsafe { BNCreateStructureBuilder() }, + } + } + + pub(crate) unsafe fn from_raw(handle: *mut BNStructureBuilder) -> Self { + debug_assert!(!handle.is_null()); + Self { handle } + } + + // TODO: Document the width adjustment with alignment. + pub fn finalize(&self) -> Ref { + let raw_struct_ptr = unsafe { BNFinalizeStructureBuilder(self.handle) }; + unsafe { Structure::ref_from_raw(raw_struct_ptr) } + } + + /// Sets the width of the [`StructureBuilder`] to the new width. + /// + /// This will remove all previously inserted members outside the new width. This is done by computing + /// the member access range (member offset + member width) and if it is larger than the new width + /// it will be removed. + pub fn width(&mut self, width: u64) -> &mut Self { + unsafe { + BNSetStructureBuilderWidth(self.handle, width); + } + self + } + + pub fn alignment(&mut self, alignment: usize) -> &mut Self { + unsafe { + BNSetStructureBuilderAlignment(self.handle, alignment); + } + self + } + + /// Sets whether the [`StructureBuilder`] is packed. + /// + /// If set the alignment of the structure will be `1`. You do not need to set the alignment to `1`. + pub fn packed(&mut self, packed: bool) -> &mut Self { + unsafe { + BNSetStructureBuilderPacked(self.handle, packed); + } + self + } + + pub fn structure_type(&mut self, t: StructureType) -> &mut Self { + unsafe { BNSetStructureBuilderType(self.handle, t) }; + self + } + + pub fn pointer_offset(&mut self, offset: i64) -> &mut Self { + unsafe { BNSetStructureBuilderPointerOffset(self.handle, offset) }; + self + } + + pub fn propagates_data_var_refs(&mut self, propagates: bool) -> &mut Self { + unsafe { BNSetStructureBuilderPropagatesDataVariableReferences(self.handle, propagates) }; + self + } + + pub fn base_structures(&mut self, bases: &[BaseStructure]) -> &mut Self { + let raw_base_structs: Vec = + bases.iter().map(BaseStructure::into_owned_raw).collect(); + unsafe { + BNSetBaseStructuresForStructureBuilder( + self.handle, + raw_base_structs.as_ptr() as *mut _, + raw_base_structs.len(), + ) + }; + self + } + + /// Append a member at the next available byte offset. + /// + /// Otherwise, consider using: + /// + /// - [`StructureBuilder::insert_member`] + /// - [`StructureBuilder::insert`] + /// - [`StructureBuilder::insert_bitwise`] + pub fn append<'a, T: Into>>( + &mut self, + ty: T, + name: &str, + access: MemberAccess, + scope: MemberScope, + ) -> &mut Self { + let name = name.to_cstr(); + let owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); + unsafe { + BNAddStructureBuilderMember( + self.handle, + &owned_raw_ty, + name.as_ref().as_ptr() as _, + access, + scope, + ); + } + self + } + + /// Insert an already constructed [`StructureMember`]. + /// + /// Otherwise, consider using: + /// + /// - [`StructureBuilder::append`] + /// - [`StructureBuilder::insert`] + /// - [`StructureBuilder::insert_bitwise`] + pub fn insert_member( + &mut self, + member: StructureMember, + overwrite_existing: bool, + ) -> &mut Self { + self.insert_bitwise( + &member.ty, + &member.name, + member.bit_offset(), + member.bit_width, + overwrite_existing, + member.access, + member.scope, + ); + self + } + + /// Inserts a member at the `offset` (in bytes). + /// + /// If you need to insert a member at a specific bit within a given byte (like a bitfield), you + /// can use [`StructureBuilder::insert_bitwise`]. + pub fn insert<'a, T: Into>>( + &mut self, + ty: T, + name: &str, + offset: u64, + overwrite_existing: bool, + access: MemberAccess, + scope: MemberScope, + ) -> &mut Self { + self.insert_bitwise( + ty, + name, + offset * 8, + None, + overwrite_existing, + access, + scope, + ) + } + + /// Inserts a member at `bit_offset` with an optional `bit_width`. + /// + /// NOTE: The `bit_offset` is relative to the start of the structure, for example, passing `8` will place + /// the field at the start of the byte `0x1`. + pub fn insert_bitwise<'a, T: Into>>( + &mut self, + ty: T, + name: &str, + bit_offset: u64, + bit_width: Option, + overwrite_existing: bool, + access: MemberAccess, + scope: MemberScope, + ) -> &mut Self { + let name = name.to_cstr(); + let owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); + let byte_offset = bit_offset / 8; + let bit_position = bit_offset % 8; + unsafe { + BNAddStructureBuilderMemberAtOffset( + self.handle, + &owned_raw_ty, + name.as_ref().as_ptr() as _, + byte_offset, + overwrite_existing, + access, + scope, + bit_position as u8, + bit_width.unwrap_or(0), + ); + } + self + } + + pub fn replace<'a, T: Into>>( + &mut self, + index: usize, + ty: T, + name: &str, + overwrite_existing: bool, + ) -> &mut Self { + let name = name.to_cstr(); + let owned_raw_ty = Conf::<&Type>::into_raw(ty.into()); + unsafe { + BNReplaceStructureBuilderMember( + self.handle, + index, + &owned_raw_ty, + name.as_ref().as_ptr() as _, + overwrite_existing, + ) + } + self + } + + /// Removes the member at a given index. + pub fn remove(&mut self, index: usize) -> &mut Self { + unsafe { BNRemoveStructureBuilderMember(self.handle, index) }; + self + } + + // TODO: We should add BNGetStructureBuilderAlignedWidth + /// Gets the current **unaligned** width of the structure. + /// + /// This cannot be used to accurately get the width of a non-packed structure. + pub fn current_width(&self) -> u64 { + unsafe { BNGetStructureBuilderWidth(self.handle) } + } +} + +impl From<&Structure> for StructureBuilder { + fn from(structure: &Structure) -> StructureBuilder { + unsafe { Self::from_raw(BNCreateStructureBuilderFromStructure(structure.handle)) } + } +} + +impl From> for StructureBuilder { + fn from(members: Vec) -> StructureBuilder { + let mut builder = StructureBuilder::new(); + for member in members { + builder.insert_member(member, false); + } + builder + } +} + +impl Drop for StructureBuilder { + fn drop(&mut self) { + unsafe { BNFreeStructureBuilder(self.handle) }; + } +} + +impl Default for StructureBuilder { + fn default() -> Self { + Self::new() + } +} + +#[derive(PartialEq, Eq, Hash)] +pub struct Structure { + pub(crate) handle: *mut BNStructure, +} + +impl Structure { + pub(crate) unsafe fn ref_from_raw(handle: *mut BNStructure) -> Ref { + debug_assert!(!handle.is_null()); + Ref::new(Self { handle }) + } + + pub fn builder() -> StructureBuilder { + StructureBuilder::new() + } + + pub fn width(&self) -> u64 { + unsafe { BNGetStructureWidth(self.handle) } + } + + pub fn structure_type(&self) -> StructureType { + unsafe { BNGetStructureType(self.handle) } + } + + /// Retrieve the members that are accessible at a given offset. + /// + /// The reason for this being plural is that members may overlap and the offset is in bytes + /// where a bitfield may contain multiple members at the given byte. + /// + /// Unions are also represented as structures and will cause this function to return + /// **all** members that can reach that offset. + /// + /// We must pass a [`TypeContainer`] here so that we can resolve base structure members, as they + /// are treated as members through this function. Typically, you get the [`TypeContainer`] + /// through the binary view with [`BinaryViewExt::type_container`]. + pub fn members_at_offset( + &self, + container: &TypeContainer, + offset: u64, + ) -> Vec { + self.members_including_inherited(container) + .into_iter() + .filter(|m| m.member.is_offset_valid(offset)) + .map(|m| m.member) + .collect() + } + + /// Return the list of non-inherited structure members. + /// + /// If you want to get all members, including ones inherited from base structures, + /// use [`Structure::members_including_inherited`] instead. + pub fn members(&self) -> Vec { + unsafe { + let mut count = 0; + let members_raw_ptr: *mut BNStructureMember = + BNGetStructureMembers(self.handle, &mut count); + debug_assert!(!members_raw_ptr.is_null()); + let members_raw = std::slice::from_raw_parts(members_raw_ptr, count); + let members = members_raw.iter().map(StructureMember::from_raw).collect(); + BNFreeStructureMemberList(members_raw_ptr, count); + members + } + } + + /// Returns the list of all structure members, including inherited ones. + /// + /// Because we must traverse through base structures, we have to provide the [`TypeContainer`]; + /// in most cases it is ok to provide the binary views container via [`BinaryViewExt::type_container`]. + pub fn members_including_inherited( + &self, + container: &TypeContainer, + ) -> Vec { + unsafe { + let mut count = 0; + let members_raw_ptr: *mut BNInheritedStructureMember = + BNGetStructureMembersIncludingInherited( + self.handle, + container.handle.as_ptr(), + &mut count, + ); + debug_assert!(!members_raw_ptr.is_null()); + let members_raw = std::slice::from_raw_parts(members_raw_ptr, count); + let members = members_raw + .iter() + .map(InheritedStructureMember::from_raw) + .collect(); + BNFreeInheritedStructureMemberList(members_raw_ptr, count); + members + } + } + + /// Retrieve the list of base structures for the structure. These base structures are what give + /// a structure inherited members. + pub fn base_structures(&self) -> Vec { + let mut count = 0; + let bases_raw_ptr = unsafe { BNGetBaseStructuresForStructure(self.handle, &mut count) }; + debug_assert!(!bases_raw_ptr.is_null()); + let bases_raw = unsafe { std::slice::from_raw_parts(bases_raw_ptr, count) }; + let bases = bases_raw.iter().map(BaseStructure::from_raw).collect(); + unsafe { BNFreeBaseStructureList(bases_raw_ptr, count) }; + bases + } + + /// Whether the structure is packed or not. + pub fn is_packed(&self) -> bool { + unsafe { BNIsStructurePacked(self.handle) } + } + + pub fn alignment(&self) -> usize { + unsafe { BNGetStructureAlignment(self.handle) } + } +} + +impl Debug for Structure { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.debug_struct("Structure") + .field("width", &self.width()) + .field("alignment", &self.alignment()) + .field("packed", &self.is_packed()) + .field("structure_type", &self.structure_type()) + .field("base_structures", &self.base_structures()) + .field("members", &self.members()) + .finish() + } +} + +unsafe impl RefCountable for Structure { + unsafe fn inc_ref(handle: &Self) -> Ref { + Self::ref_from_raw(BNNewStructureReference(handle.handle)) + } + + unsafe fn dec_ref(handle: &Self) { + BNFreeStructure(handle.handle); + } +} + +impl ToOwned for Structure { + type Owned = Ref; + + fn to_owned(&self) -> Self::Owned { + unsafe { RefCountable::inc_ref(self) } + } +} + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct StructureMember { + pub ty: Conf>, + // TODO: Shouldnt this be a QualifiedName? The ffi says no... + pub name: String, + /// The byte offset of the member. + pub offset: u64, + pub access: MemberAccess, + pub scope: MemberScope, + /// The bit position relative to the byte offset. + pub bit_position: Option, + pub bit_width: Option, +} + +impl StructureMember { + pub(crate) fn from_raw(value: &BNStructureMember) -> Self { + Self { + ty: Conf::new( + unsafe { Type::from_raw(value.type_) }.to_owned(), + value.typeConfidence, + ), + // TODO: I dislike using this function here. + name: raw_to_string(value.name as *mut _).unwrap(), + offset: value.offset, + access: value.access, + scope: value.scope, + bit_position: match value.bitPosition { + 0 => None, + _ => Some(value.bitPosition), + }, + bit_width: match value.bitWidth { + 0 => None, + _ => Some(value.bitWidth), + }, + } + } + + #[allow(unused)] + pub(crate) fn from_owned_raw(value: BNStructureMember) -> Self { + let owned = Self::from_raw(&value); + Self::free_raw(value); + owned + } + + #[allow(unused)] + pub(crate) fn into_raw(value: Self) -> BNStructureMember { + let bn_name = BnString::new(value.name); + BNStructureMember { + type_: unsafe { Ref::into_raw(value.ty.contents) }.handle, + name: BnString::into_raw(bn_name), + offset: value.offset, + typeConfidence: value.ty.confidence, + access: value.access, + scope: value.scope, + bitPosition: value.bit_position.unwrap_or(0), + bitWidth: value.bit_width.unwrap_or(0), + } + } + + #[allow(unused)] + pub(crate) fn free_raw(value: BNStructureMember) { + let _ = unsafe { Type::ref_from_raw(value.type_) }; + unsafe { BnString::free_raw(value.name) }; + } + + pub fn new( + ty: Conf>, + name: String, + offset: u64, + access: MemberAccess, + scope: MemberScope, + ) -> Self { + Self { + ty, + name, + offset, + access, + scope, + bit_position: None, + bit_width: None, + } + } + + pub fn new_bitfield( + ty: Conf>, + name: String, + bit_offset: u64, + bit_width: u8, + access: MemberAccess, + scope: MemberScope, + ) -> Self { + Self { + ty, + name, + offset: bit_offset / 8, + access, + scope, + bit_position: Some((bit_offset % 8) as u8), + bit_width: Some(bit_width), + } + } + + // TODO: Do we count bitwidth here? + /// Whether the offset within the accessible range of the member. + pub fn is_offset_valid(&self, offset: u64) -> bool { + self.offset <= offset && offset < self.offset + self.ty.contents.width() + } + + /// Member offset in bits. + pub fn bit_offset(&self) -> u64 { + (self.offset * 8) + self.bit_position.unwrap_or(0) as u64 + } +} + +impl CoreArrayProvider for StructureMember { + type Raw = BNStructureMember; + type Context = (); + type Wrapped<'a> = Self; +} + +unsafe impl CoreArrayProviderInner for StructureMember { + unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) { + BNFreeStructureMemberList(raw, count) + } + + unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> { + Self::from_raw(raw) + } +} + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct InheritedStructureMember { + pub base: Ref, + pub base_offset: u64, + pub member: StructureMember, + pub member_index: usize, +} + +impl InheritedStructureMember { + pub(crate) fn from_raw(value: &BNInheritedStructureMember) -> Self { + Self { + base: unsafe { NamedTypeReference::from_raw(value.base) }.to_owned(), + base_offset: value.baseOffset, + member: StructureMember::from_raw(&value.member), + member_index: value.memberIndex, + } + } + + #[allow(unused)] + pub(crate) fn from_owned_raw(value: BNInheritedStructureMember) -> Self { + let owned = Self::from_raw(&value); + Self::free_raw(value); + owned + } + + #[allow(unused)] + pub(crate) fn into_raw(value: Self) -> BNInheritedStructureMember { + BNInheritedStructureMember { + base: unsafe { Ref::into_raw(value.base) }.handle, + baseOffset: value.base_offset, + member: StructureMember::into_raw(value.member), + memberIndex: value.member_index, + } + } + + #[allow(unused)] + pub(crate) fn free_raw(value: BNInheritedStructureMember) { + let _ = unsafe { NamedTypeReference::ref_from_raw(value.base) }; + StructureMember::free_raw(value.member); + } + + pub fn new( + base: Ref, + base_offset: u64, + member: StructureMember, + member_index: usize, + ) -> Self { + Self { + base, + base_offset, + member, + member_index, + } + } +} + +#[derive(Debug, Clone, Hash, PartialEq, Eq)] +pub struct BaseStructure { + pub ty: Ref, + pub offset: u64, + pub width: u64, +} + +impl BaseStructure { + pub(crate) fn from_raw(value: &BNBaseStructure) -> Self { + Self { + ty: unsafe { NamedTypeReference::from_raw(value.type_) }.to_owned(), + offset: value.offset, + width: value.width, + } + } + + #[allow(unused)] + pub(crate) fn from_owned_raw(value: BNBaseStructure) -> Self { + let owned = Self::from_raw(&value); + Self::free_raw(value); + owned + } + + #[allow(unused)] + pub(crate) fn into_raw(value: Self) -> BNBaseStructure { + BNBaseStructure { + type_: unsafe { Ref::into_raw(value.ty) }.handle, + offset: value.offset, + width: value.width, + } + } + + pub(crate) fn into_owned_raw(value: &Self) -> BNBaseStructure { + BNBaseStructure { + type_: value.ty.handle, + offset: value.offset, + width: value.width, + } + } + + #[allow(unused)] + pub(crate) fn free_raw(value: BNBaseStructure) { + let _ = unsafe { NamedTypeReference::ref_from_raw(value.type_) }; + } + + pub fn new(ty: Ref, offset: u64, width: u64) -> Self { + Self { ty, offset, width } + } +} diff --git a/rust/tests/qualified_name.rs b/rust/tests/qualified_name.rs new file mode 100644 index 0000000000..c0d16a75bb --- /dev/null +++ b/rust/tests/qualified_name.rs @@ -0,0 +1,187 @@ +use binaryninja::qualified_name::QualifiedName; + +#[test] +fn test_new_from_vec_string() { + let items = vec!["std".to_string(), "vector".to_string(), "int".to_string()]; + let qn = QualifiedName::new(items); + assert_eq!(qn.len(), 3); + assert_eq!(qn.to_string(), "std::vector::int"); + assert_eq!(qn.separator, "::"); +} + +#[test] +fn test_new_from_array() { + let items = ["root", "data"]; + let qn = QualifiedName::new(items); + assert_eq!(qn.len(), 2); + assert_eq!(qn.to_string(), "root::data"); +} + +#[test] +fn test_new_empty() { + let items: Vec = Vec::new(); + let qn = QualifiedName::new(items); + assert!(qn.is_empty()); + assert_eq!(qn.to_string(), ""); +} + +#[test] +fn test_new_with_custom_separator() { + let items = vec!["a".to_string(), "b".to_string(), "c".to_string()]; + let separator = ".".to_string(); + let qn = QualifiedName::new_with_separator(items, separator); + assert_eq!(qn.to_string(), "a.b.c"); + assert_eq!(qn.separator, "."); +} + +#[test] +fn test_from_string() { + let qn: QualifiedName = "SingleName".to_string().into(); + assert_eq!(qn.len(), 1); + assert_eq!(qn[0], "SingleName"); + assert_eq!(qn.to_string(), "SingleName"); +} + +#[test] +fn test_from_str_literal() { + let qn: QualifiedName = "AnotherName".into(); + assert_eq!(qn.len(), 1); + assert_eq!(qn[0], "AnotherName"); +} + +#[test] +fn test_into_string() { + let qn = QualifiedName::new(vec!["A", "B", "C"]); + let s: String = qn.into(); + assert_eq!(s, "A::B::C"); +} + +#[test] +fn test_push_pop_and_last() { + let mut qn = QualifiedName::new(vec!["ns1"]); + + qn.push("TypeA".to_string()); + assert_eq!(qn.len(), 2); + assert_eq!(qn.to_string(), "ns1::TypeA"); + + assert_eq!(qn.last().unwrap(), "TypeA"); + *qn.last_mut().unwrap() = "TypeB".to_string(); + assert_eq!(qn.to_string(), "ns1::TypeB"); + + assert_eq!(qn.pop().unwrap(), "TypeB"); + assert_eq!(qn.len(), 1); + assert_eq!(qn.to_string(), "ns1"); + + assert_eq!(qn.pop().unwrap(), "ns1"); + assert!(qn.is_empty()); + assert_eq!(qn.pop(), None); +} + +#[test] +fn test_with_item() { + let qn_a = QualifiedName::from("ns1"); + let qn_b = qn_a.with_item("ns2"); + let qn_c = qn_b.with_item("symbol"); + + assert_eq!(qn_a.to_string(), "ns1"); + assert_eq!(qn_b.to_string(), "ns1::ns2"); + assert_eq!(qn_c.to_string(), "ns1::ns2::symbol"); + + // Ensure the original is unchanged + assert_eq!(qn_a.len(), 1); +} + +#[test] +fn test_split_last() { + let qn = QualifiedName::new(vec!["A", "B", "C"]); + + let (last, prefix) = qn.split_last().unwrap(); + assert_eq!(last, "C"); + assert_eq!(prefix.to_string(), "A::B"); + + let (last2, prefix2) = prefix.split_last().unwrap(); + assert_eq!(last2, "B"); + assert_eq!(prefix2.to_string(), "A"); + + let (last3, prefix3) = prefix2.split_last().unwrap(); + assert_eq!(last3, "A"); + assert!(prefix3.is_empty()); + + // Split on an empty name + assert_eq!(prefix3.split_last(), None); +} + +#[test] +fn test_replace() { + let qualified_name = + QualifiedName::new(vec!["my_prefix::ns".to_string(), "my_Type".to_string()]); + + let replaced = qualified_name.replace("my", "your"); + assert_eq!( + replaced.items, + vec!["your_prefix::ns".to_string(), "your_Type".to_string()] + ); + assert_eq!(replaced.to_string(), "your_prefix::ns::your_Type"); + + let qualified_name_dot = QualifiedName::new_with_separator(vec!["foo", "bar"], ".".to_string()); + let replaced_dot = qualified_name_dot.replace("foo", "baz"); + assert_eq!(replaced_dot.to_string(), "baz.bar"); +} + +#[test] +fn test_index_and_index_mut() { + let mut qn = QualifiedName::new(vec!["a", "b", "c"]); + assert_eq!(qn[0], "a"); + assert_eq!(qn[2], "c"); + qn[1] = "NEW_ITEM".to_string(); + assert_eq!(qn.to_string(), "a::NEW_ITEM::c"); +} + +#[test] +#[should_panic] +fn test_index_out_of_bounds() { + let qn = QualifiedName::new(vec!["a"]); + // This should panic + let _ = qn[1]; +} + +#[test] +fn test_insert() { + let mut qn = QualifiedName::new(vec!["start", "end"]); + + qn.insert(1, "middle".to_string()); + assert_eq!(qn.to_string(), "start::middle::end"); + + qn.insert(0, "HEAD".to_string()); + assert_eq!(qn.to_string(), "HEAD::start::middle::end"); + + qn.insert(4, "TAIL".to_string()); + assert_eq!(qn.to_string(), "HEAD::start::middle::end::TAIL"); + + let initial_len = qn.len(); + qn.insert(initial_len + 5, "NOPE".to_string()); + assert_eq!(qn.len(), initial_len); +} + +#[test] +fn test_into_and_from_raw() { + let original_qn = QualifiedName::new(["std", "vector"]); + assert_eq!(original_qn.to_string(), "std::vector"); + + let raw_qn = QualifiedName::into_raw(original_qn); + assert_eq!(raw_qn.nameCount, 2); + assert!(!raw_qn.join.is_null()); + + let restored_qn = QualifiedName::from_owned_raw(raw_qn); + assert_eq!(restored_qn.len(), 2); + assert_eq!(restored_qn.to_string(), "std::vector"); +} + +#[test] +fn test_raw_freeing() { + let qn = QualifiedName::new(["std", "vector"]); + let raw_qn = QualifiedName::into_raw(qn); + assert!(!raw_qn.join.is_null()); + assert!(!raw_qn.name.is_null()); + QualifiedName::free_raw(raw_qn); +} diff --git a/rust/tests/type_archive.rs b/rust/tests/type_archive.rs index 12fcec9dac..3e13507ee6 100644 --- a/rust/tests/type_archive.rs +++ b/rust/tests/type_archive.rs @@ -1,7 +1,6 @@ use binaryninja::headless::Session; use binaryninja::platform::Platform; -use binaryninja::type_archive::TypeArchive; -use binaryninja::types::{Type, TypeClass}; +use binaryninja::types::{Type, TypeArchive, TypeClass}; #[test] fn test_create_archive() { diff --git a/rust/tests/type_library.rs b/rust/tests/type_library.rs index 83937386e0..b8a12870aa 100644 --- a/rust/tests/type_library.rs +++ b/rust/tests/type_library.rs @@ -1,8 +1,7 @@ use binaryninja::binary_view::BinaryViewExt; use binaryninja::headless::Session; use binaryninja::platform::Platform; -use binaryninja::type_library::TypeLibrary; -use binaryninja::types::{Type, TypeClass}; +use binaryninja::types::{Type, TypeClass, TypeLibrary}; use std::path::PathBuf; #[test] diff --git a/rust/tests/type_parser.rs b/rust/tests/type_parser.rs index d08e7791d4..64771404d2 100644 --- a/rust/tests/type_parser.rs +++ b/rust/tests/type_parser.rs @@ -1,7 +1,6 @@ use binaryninja::headless::Session; use binaryninja::platform::Platform; -use binaryninja::type_parser::{CoreTypeParser, TypeParser, TypeParserError}; -use binaryninja::types::Type; +use binaryninja::types::{CoreTypeParser, Type, TypeParser, TypeParserError}; use binaryninjacore_sys::BNTypeParserErrorSeverity::ErrorSeverity; const TEST_TYPES: &str = r#" diff --git a/rust/tests/type_printer.rs b/rust/tests/type_printer.rs index 4ff67232da..6703537613 100644 --- a/rust/tests/type_printer.rs +++ b/rust/tests/type_printer.rs @@ -3,12 +3,10 @@ use binaryninja::disassembly::InstructionTextToken; use binaryninja::headless::Session; use binaryninja::platform::Platform; use binaryninja::rc::Ref; -use binaryninja::type_container::TypeContainer; -use binaryninja::type_printer::{ - register_type_printer, CoreTypePrinter, TokenEscapingType, TypeDefinitionLine, TypePrinter, -}; +use binaryninja::types::printer::{register_type_printer, TokenEscapingType, TypeDefinitionLine}; use binaryninja::types::{ - MemberAccess, MemberScope, QualifiedName, Structure, StructureMember, Type, + CoreTypePrinter, MemberAccess, MemberScope, QualifiedName, Structure, StructureMember, Type, + TypeContainer, TypePrinter, }; use std::path::PathBuf;