Files
binaryninja-rust-api/src/types.rs
2024-01-24 13:07:27 +01:00

2608 lines
74 KiB
Rust

// Copyright 2021-2024 Vector 35 Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// 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.
// 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
use binaryninjacore_sys::*;
use crate::{
architecture::{Architecture, CoreArchitecture},
binaryview::{BinaryView, BinaryViewExt},
callingconvention::CallingConvention,
filemetadata::FileMetadata,
rc::*,
string::{raw_to_string, BnStr, BnStrCompatible, BnString},
symbol::Symbol,
};
use lazy_static::lazy_static;
use std::{
borrow::Cow,
collections::HashSet,
ffi::CStr,
fmt,
fmt::{Debug, Display, Formatter},
hash::{Hash, Hasher},
iter::{zip, IntoIterator},
mem,
mem::ManuallyDrop,
os::raw::c_char,
ptr, result, slice,
sync::Mutex,
};
pub type Result<R> = result::Result<R, ()>;
pub type ReferenceType = BNReferenceType;
pub type TypeClass = BNTypeClass;
pub type NamedTypeReferenceClass = BNNamedTypeReferenceClass;
pub type MemberAccess = BNMemberAccess;
pub type MemberScope = BNMemberScope;
////////////////
// Confidence
pub struct Conf<T> {
pub contents: T,
pub confidence: u8,
}
pub trait ConfMergable<T, O> {
type Result;
/// Merge two confidence types' values depending on whichever has higher confidence
/// In the event of a tie, the LHS (caller's) value is used.
fn merge(self, other: O) -> Self::Result;
}
impl<T> Conf<T> {
pub fn new(contents: T, confidence: u8) -> Self {
Self {
contents,
confidence,
}
}
pub fn map<U, F>(self, f: F) -> Conf<U>
where
F: FnOnce(T) -> U,
{
Conf::new(f(self.contents), self.confidence)
}
pub fn as_ref<U>(&self) -> Conf<&U>
where
T: AsRef<U>,
{
Conf::new(self.contents.as_ref(), self.confidence)
}
}
/// Returns best value or LHS on tie
///
/// `Conf<T>` + `Conf<T>` → `Conf<T>`
impl<T> ConfMergable<T, Conf<T>> for Conf<T> {
type Result = Conf<T>;
fn merge(self, other: Conf<T>) -> Conf<T> {
if other.confidence > self.confidence {
other
} else {
self
}
}
}
/// Returns LHS if RHS is None
///
/// `Conf<T>` + `Option<Conf<T>>` → `Conf<T>`
impl<T> ConfMergable<T, Option<Conf<T>>> for Conf<T> {
type Result = Conf<T>;
fn merge(self, other: Option<Conf<T>>) -> Conf<T> {
match other {
Some(c @ Conf { confidence, .. }) if confidence > self.confidence => c,
_ => self,
}
}
}
/// Returns RHS if LHS is None
///
/// `Option<Conf<T>>` + `Conf<T>` → `Conf<T>`
impl<T> ConfMergable<T, Conf<T>> for Option<Conf<T>> {
type Result = Conf<T>;
fn merge(self, other: Conf<T>) -> Conf<T> {
match self {
Some(c @ Conf { confidence, .. }) if confidence >= other.confidence => c,
_ => other,
}
}
}
/// Returns best non-None value or None
///
/// `Option<Conf<T>>` + `Option<Conf<T>>` → `Option<Conf<T>>`
impl<T> ConfMergable<T, Option<Conf<T>>> for Option<Conf<T>> {
type Result = Option<Conf<T>>;
fn merge(self, other: Option<Conf<T>>) -> Option<Conf<T>> {
match (self, other) {
(
Some(
this @ Conf {
confidence: this_confidence,
..
},
),
Some(
other @ Conf {
confidence: other_confidence,
..
},
),
) => {
if this_confidence >= other_confidence {
Some(this)
} else {
Some(other)
}
}
(None, Some(c)) => Some(c),
(Some(c), None) => Some(c),
(None, None) => None,
}
}
}
impl<T: Debug> Debug for Conf<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{:?} ({} confidence)", self.contents, self.confidence)
}
}
impl<T: Display> Display for Conf<T> {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{} ({} confidence)", self.contents, self.confidence)
}
}
impl<'a, T> From<&'a Conf<T>> for Conf<&'a T> {
fn from(c: &'a Conf<T>) -> Self {
Conf::new(&c.contents, c.confidence)
}
}
impl<'a, T: RefCountable> From<&'a Conf<Ref<T>>> for Conf<&'a T> {
fn from(c: &'a Conf<Ref<T>>) -> Self {
Conf::new(c.contents.as_ref(), c.confidence)
}
}
impl<'a, T: RefCountable> From<&'a Ref<T>> for Conf<&'a T> {
fn from(r: &'a Ref<T>) -> Self {
r.as_ref().into()
}
}
#[inline]
pub fn min_confidence() -> u8 {
u8::MIN
}
#[inline]
pub fn max_confidence() -> u8 {
u8::MAX
}
impl<T: Clone> Clone for Conf<T> {
fn clone(&self) -> Self {
Self {
contents: self.contents.clone(),
confidence: self.confidence,
}
}
}
impl<T> From<T> for Conf<T> {
fn from(contents: T) -> Self {
Self::new(contents, max_confidence())
}
}
impl From<BNTypeWithConfidence> for Conf<Ref<Type>> {
fn from(type_with_confidence: BNTypeWithConfidence) -> Self {
Self::new(
unsafe { Type::ref_from_raw(type_with_confidence.type_) },
type_with_confidence.confidence,
)
}
}
impl From<BNBoolWithConfidence> for Conf<bool> {
fn from(bool_with_confidence: BNBoolWithConfidence) -> Self {
Self::new(bool_with_confidence.value, bool_with_confidence.confidence)
}
}
impl From<BNCallingConventionWithConfidence> for Conf<Ref<CallingConvention<CoreArchitecture>>> {
fn from(cc_with_confidence: BNCallingConventionWithConfidence) -> Self {
Self::new(
unsafe {
CallingConvention::ref_from_raw(
cc_with_confidence.convention,
CoreArchitecture::from_raw(BNGetCallingConventionArchitecture(
cc_with_confidence.convention,
)),
)
},
cc_with_confidence.confidence,
)
}
}
impl From<BNOffsetWithConfidence> for Conf<i64> {
fn from(offset_with_confidence: BNOffsetWithConfidence) -> Self {
Self::new(
offset_with_confidence.value,
offset_with_confidence.confidence,
)
}
}
impl From<Conf<Ref<Type>>> for BNTypeWithConfidence {
fn from(conf: Conf<Ref<Type>>) -> Self {
Self {
type_: conf.contents.handle,
confidence: conf.confidence,
}
}
}
impl From<Conf<&Type>> for BNTypeWithConfidence {
fn from(conf: Conf<&Type>) -> Self {
Self {
type_: conf.contents.handle,
confidence: conf.confidence,
}
}
}
impl From<Conf<bool>> for BNBoolWithConfidence {
fn from(conf: Conf<bool>) -> Self {
Self {
value: conf.contents,
confidence: conf.confidence,
}
}
}
impl<A: Architecture> From<Conf<&CallingConvention<A>>> for BNCallingConventionWithConfidence {
fn from(conf: Conf<&CallingConvention<A>>) -> Self {
Self {
convention: conf.contents.handle,
confidence: conf.confidence,
}
}
}
impl From<Conf<i64>> for BNOffsetWithConfidence {
fn from(conf: Conf<i64>) -> Self {
Self {
value: conf.contents,
confidence: conf.confidence,
}
}
}
//////////////////
// Type Builder
#[derive(PartialEq, Eq, Hash)]
pub struct TypeBuilder {
pub(crate) handle: *mut BNTypeBuilder,
}
impl TypeBuilder {
pub fn new(t: &Type) -> Self {
unsafe { Self::from_raw(BNCreateTypeBuilderFromType(t.handle)) }
}
pub(crate) unsafe fn from_raw(handle: *mut BNTypeBuilder) -> Self {
debug_assert!(!handle.is_null());
Self { handle }
}
// Chainable terminal
pub fn finalize(&self) -> Ref<Type> {
unsafe { Type::ref_from_raw(BNFinalizeTypeBuilder(self.handle)) }
}
// Settable properties
pub fn set_can_return<T: Into<Conf<bool>>>(&self, value: T) -> &Self {
let mut bool_with_confidence = value.into().into();
unsafe { BNSetFunctionTypeBuilderCanReturn(self.handle, &mut bool_with_confidence) };
self
}
pub fn set_pure<T: Into<Conf<bool>>>(&self, value: T) -> &Self {
let mut bool_with_confidence = value.into().into();
unsafe { BNSetTypeBuilderPure(self.handle, &mut bool_with_confidence) };
self
}
pub fn set_const<T: Into<Conf<bool>>>(&self, value: T) -> &Self {
let mut bool_with_confidence = value.into().into();
unsafe { BNTypeBuilderSetConst(self.handle, &mut bool_with_confidence) };
self
}
pub fn set_volatile<T: Into<Conf<bool>>>(&self, value: T) -> &Self {
let mut bool_with_confidence = value.into().into();
unsafe { BNTypeBuilderSetVolatile(self.handle, &mut bool_with_confidence) };
self
}
// Readable properties
pub fn type_class(&self) -> TypeClass {
unsafe { BNGetTypeBuilderClass(self.handle) }
}
pub fn width(&self) -> u64 {
unsafe { BNGetTypeBuilderWidth(self.handle) }
}
pub fn alignment(&self) -> usize {
unsafe { BNGetTypeBuilderAlignment(self.handle) }
}
pub fn is_signed(&self) -> Conf<bool> {
unsafe { BNIsTypeBuilderSigned(self.handle).into() }
}
pub fn is_const(&self) -> Conf<bool> {
unsafe { BNIsTypeBuilderConst(self.handle).into() }
}
pub fn is_volatile(&self) -> Conf<bool> {
unsafe { BNIsTypeBuilderVolatile(self.handle).into() }
}
pub fn is_floating_point(&self) -> bool {
unsafe { BNIsTypeBuilderFloatingPoint(self.handle) }
}
pub fn target(&self) -> Result<Conf<Ref<Type>>> {
let raw_target = unsafe { BNGetTypeBuilderChildType(self.handle) };
if raw_target.type_.is_null() {
Err(())
} else {
Ok(raw_target.into())
}
}
pub fn element_type(&self) -> Result<Conf<Ref<Type>>> {
let raw_target = unsafe { BNGetTypeBuilderChildType(self.handle) };
if raw_target.type_.is_null() {
Err(())
} else {
Ok(raw_target.into())
}
}
pub fn return_value(&self) -> Result<Conf<Ref<Type>>> {
let raw_target = unsafe { BNGetTypeBuilderChildType(self.handle) };
if raw_target.type_.is_null() {
Err(())
} else {
Ok(raw_target.into())
}
}
pub fn calling_convention(&self) -> Result<Conf<Ref<CallingConvention<CoreArchitecture>>>> {
let convention_confidence = unsafe { BNGetTypeBuilderCallingConvention(self.handle) };
if convention_confidence.convention.is_null() {
Err(())
} else {
Ok(convention_confidence.into())
}
}
pub fn parameters(&self) -> Result<Vec<FunctionParameter<BnString>>> {
unsafe {
let mut count: usize = mem::zeroed();
let parameters_raw = BNGetTypeBuilderParameters(self.handle, &mut count);
if parameters_raw.is_null() {
Err(())
} else {
let parameters: &[BNFunctionParameter] =
slice::from_raw_parts(parameters_raw, count);
let result = (0..count)
.map(|i| FunctionParameter::from_raw(parameters[i]))
.collect();
BNFreeTypeParameterList(parameters_raw, count);
Ok(result)
}
}
}
pub fn has_variable_arguments(&self) -> Conf<bool> {
unsafe { BNTypeBuilderHasVariableArguments(self.handle).into() }
}
pub fn can_return(&self) -> Conf<bool> {
unsafe { BNFunctionTypeBuilderCanReturn(self.handle).into() }
}
pub fn pure(&self) -> Conf<bool> {
unsafe { BNIsTypeBuilderPure(self.handle).into() }
}
pub fn get_structure(&self) -> Result<Ref<Structure>> {
let result = unsafe { BNGetTypeBuilderStructure(self.handle) };
if result.is_null() {
Err(())
} else {
Ok(unsafe { Structure::ref_from_raw(result) })
}
}
pub fn get_enumeration(&self) -> Result<Ref<Enumeration>> {
let result = unsafe { BNGetTypeBuilderEnumeration(self.handle) };
if result.is_null() {
Err(())
} else {
Ok(unsafe { Enumeration::ref_from_raw(result) })
}
}
pub fn get_named_type_reference(&self) -> Result<Ref<NamedTypeReference>> {
let result = unsafe { BNGetTypeBuilderNamedTypeReference(self.handle) };
if result.is_null() {
Err(())
} else {
Ok(unsafe { NamedTypeReference::ref_from_raw(result) })
}
}
pub fn count(&self) -> u64 {
unsafe { BNGetTypeBuilderElementCount(self.handle) }
}
pub fn offset(&self) -> u64 {
unsafe { BNGetTypeBuilderOffset(self.handle) }
}
pub fn stack_adjustment(&self) -> Conf<i64> {
unsafe { BNGetTypeBuilderStackAdjustment(self.handle).into() }
}
// TODO : This and properties
// pub fn tokens(&self) -> ? {}
pub fn void() -> Self {
unsafe { Self::from_raw(BNCreateVoidTypeBuilder()) }
}
pub fn bool() -> Self {
unsafe { Self::from_raw(BNCreateBoolTypeBuilder()) }
}
pub fn char() -> Self {
Self::int(1, true)
}
pub fn int(width: usize, is_signed: bool) -> Self {
let mut is_signed = Conf::new(is_signed, max_confidence()).into();
unsafe {
Self::from_raw(BNCreateIntegerTypeBuilder(
width,
&mut is_signed,
BnString::new("").as_ptr() as *mut _,
))
}
}
pub fn named_int<S: BnStrCompatible>(width: usize, is_signed: bool, alt_name: S) -> Self {
let mut is_signed = Conf::new(is_signed, max_confidence()).into();
// let alt_name = BnString::new(alt_name);
let alt_name = alt_name.into_bytes_with_nul(); // 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
unsafe {
Self::from_raw(BNCreateIntegerTypeBuilder(
width,
&mut is_signed,
alt_name.as_ref().as_ptr() as _,
))
}
}
pub fn float(width: usize) -> Self {
unsafe {
Self::from_raw(BNCreateFloatTypeBuilder(
width,
BnString::new("").as_ptr() as *mut _,
))
}
}
pub fn named_float<S: BnStrCompatible>(width: usize, alt_name: S) -> Self {
// let alt_name = BnString::new(alt_name);
let alt_name = alt_name.into_bytes_with_nul(); // See same line in `named_int` above
unsafe {
Self::from_raw(BNCreateFloatTypeBuilder(
width,
alt_name.as_ref().as_ptr() as _,
))
}
}
pub fn array<'a, T: Into<Conf<&'a Type>>>(t: T, count: u64) -> Self {
unsafe { Self::from_raw(BNCreateArrayTypeBuilder(&t.into().into(), count)) }
}
/// 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
/// For simplicity's sake, that convention isn't followed and you can query the default_int_size from an arch, if you have it, if you need to
pub fn enumeration<T: Into<Conf<bool>>>(
enumeration: &Enumeration,
width: usize,
is_signed: T,
) -> Self {
unsafe {
// TODO : This is _extremely fragile_, we should change the internals of BNCreateEnumerationTypeBuilder instead of doing this
let mut fake_arch: BNArchitecture = mem::zeroed();
Self::from_raw(BNCreateEnumerationTypeBuilder(
&mut fake_arch,
enumeration.handle,
width,
&mut is_signed.into().into(),
))
}
}
pub fn structure(structure_type: &Structure) -> Self {
unsafe { Self::from_raw(BNCreateStructureTypeBuilder(structure_type.handle)) }
}
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();
unsafe {
Self::from_raw(BNCreateNamedTypeReferenceBuilder(
type_reference.handle,
0,
1,
&mut is_const,
&mut is_volatile,
))
}
}
pub fn named_type_from_type<S: BnStrCompatible>(name: S, t: &Type) -> Self {
let mut name = QualifiedName::from(name);
unsafe {
Self::from_raw(BNCreateNamedTypeReferenceBuilderFromTypeAndId(
BnString::new("").as_ptr() as *mut _,
&mut name.0,
t.handle,
))
}
}
// TODO : BNCreateFunctionTypeBuilder
pub fn pointer<'a, A: Architecture, T: Into<Conf<&'a Type>>>(arch: &A, t: T) -> Self {
let mut is_const = Conf::new(false, min_confidence()).into();
let mut is_volatile = Conf::new(false, min_confidence()).into();
unsafe {
Self::from_raw(BNCreatePointerTypeBuilder(
arch.as_ref().0,
&t.into().into(),
&mut is_const,
&mut is_volatile,
ReferenceType::PointerReferenceType,
))
}
}
pub fn const_pointer<'a, A: Architecture, T: Into<Conf<&'a Type>>>(arch: &A, t: T) -> Self {
let mut is_const = Conf::new(true, max_confidence()).into();
let mut is_volatile = Conf::new(false, min_confidence()).into();
unsafe {
Self::from_raw(BNCreatePointerTypeBuilder(
arch.as_ref().0,
&t.into().into(),
&mut is_const,
&mut is_volatile,
ReferenceType::PointerReferenceType,
))
}
}
pub fn pointer_of_width<'a, T: Into<Conf<&'a Type>>>(
t: T,
size: usize,
is_const: bool,
is_volatile: bool,
ref_type: Option<ReferenceType>,
) -> Self {
let mut is_const = Conf::new(is_const, max_confidence()).into();
let mut is_volatile = Conf::new(is_volatile, max_confidence()).into();
unsafe {
Self::from_raw(BNCreatePointerTypeBuilderOfWidth(
size,
&t.into().into(),
&mut is_const,
&mut is_volatile,
ref_type.unwrap_or(ReferenceType::PointerReferenceType),
))
}
}
pub fn pointer_with_options<'a, A: Architecture, T: Into<Conf<&'a Type>>>(
arch: &A,
t: T,
is_const: bool,
is_volatile: bool,
ref_type: Option<ReferenceType>,
) -> Self {
let mut is_const = Conf::new(is_const, max_confidence()).into();
let mut is_volatile = Conf::new(is_volatile, max_confidence()).into();
unsafe {
Self::from_raw(BNCreatePointerTypeBuilder(
arch.as_ref().0,
&t.into().into(),
&mut is_const,
&mut is_volatile,
ref_type.unwrap_or(ReferenceType::PointerReferenceType),
))
}
}
}
impl fmt::Display for TypeBuilder {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", unsafe {
BnString::from_raw(BNGetTypeBuilderString(self.handle, ptr::null_mut()))
})
}
}
impl Drop for TypeBuilder {
fn drop(&mut self) {
unsafe { BNFreeTypeBuilder(self.handle) };
}
}
//////////
// Type
pub struct Type {
pub(crate) handle: *mut BNType,
}
/// ```
/// use binaryninja::types::Type;
/// let bv = unsafe { BinaryView::from_raw(view) };
/// let my_custom_type_1 = Self::named_int(5, false, "my_w");
/// let my_custom_type_2 = Self::int(5, false);
/// bv.define_user_type("int_1", &my_custom_type_1);
/// bv.define_user_type("int_2", &my_custom_type_2);
/// ```
impl Type {
unsafe fn from_raw(handle: *mut BNType) -> Self {
debug_assert!(!handle.is_null());
Self { handle }
}
pub(crate) unsafe fn ref_from_raw(handle: *mut BNType) -> Ref<Self> {
debug_assert!(!handle.is_null());
Ref::new(Self { handle })
}
pub fn to_builder(&self) -> TypeBuilder {
TypeBuilder::new(self)
}
// Readable properties
pub fn type_class(&self) -> TypeClass {
unsafe { BNGetTypeClass(self.handle) }
}
pub fn width(&self) -> u64 {
unsafe { BNGetTypeWidth(self.handle) }
}
pub fn alignment(&self) -> usize {
unsafe { BNGetTypeAlignment(self.handle) }
}
pub fn is_signed(&self) -> Conf<bool> {
unsafe { BNIsTypeSigned(self.handle).into() }
}
pub fn is_const(&self) -> Conf<bool> {
unsafe { BNIsTypeConst(self.handle).into() }
}
pub fn is_volatile(&self) -> Conf<bool> {
unsafe { BNIsTypeVolatile(self.handle).into() }
}
pub fn is_floating_point(&self) -> bool {
unsafe { BNIsTypeFloatingPoint(self.handle) }
}
pub fn target(&self) -> Result<Conf<Ref<Type>>> {
let raw_target = unsafe { BNGetChildType(self.handle) };
if raw_target.type_.is_null() {
Err(())
} else {
Ok(raw_target.into())
}
}
pub fn element_type(&self) -> Result<Conf<Ref<Type>>> {
let raw_target = unsafe { BNGetChildType(self.handle) };
if raw_target.type_.is_null() {
Err(())
} else {
Ok(raw_target.into())
}
}
pub fn return_value(&self) -> Result<Conf<Ref<Type>>> {
let raw_target = unsafe { BNGetChildType(self.handle) };
if raw_target.type_.is_null() {
Err(())
} else {
Ok(raw_target.into())
}
}
pub fn calling_convention(&self) -> Result<Conf<Ref<CallingConvention<CoreArchitecture>>>> {
let convention_confidence = unsafe { BNGetTypeCallingConvention(self.handle) };
if convention_confidence.convention.is_null() {
Err(())
} else {
Ok(convention_confidence.into())
}
}
pub fn parameters(&self) -> Result<Vec<FunctionParameter<BnString>>> {
unsafe {
let mut count: usize = mem::zeroed();
let parameters_raw: *mut BNFunctionParameter =
BNGetTypeParameters(self.handle, &mut count);
if parameters_raw.is_null() {
Err(())
} else {
let parameters: &[BNFunctionParameter] =
slice::from_raw_parts(parameters_raw, count);
let result = (0..count)
.map(|i| FunctionParameter::from_raw(parameters[i]))
.collect();
BNFreeTypeParameterList(parameters_raw, count);
Ok(result)
}
}
}
pub fn has_variable_arguments(&self) -> Conf<bool> {
unsafe { BNTypeHasVariableArguments(self.handle).into() }
}
pub fn can_return(&self) -> Conf<bool> {
unsafe { BNFunctionTypeCanReturn(self.handle).into() }
}
pub fn pure(&self) -> Conf<bool> {
unsafe { BNIsTypePure(self.handle).into() }
}
pub fn get_structure(&self) -> Result<Ref<Structure>> {
let result = unsafe { BNGetTypeStructure(self.handle) };
if result.is_null() {
Err(())
} else {
Ok(unsafe { Structure::ref_from_raw(result) })
}
}
pub fn get_enumeration(&self) -> Result<Ref<Enumeration>> {
let result = unsafe { BNGetTypeEnumeration(self.handle) };
if result.is_null() {
Err(())
} else {
Ok(unsafe { Enumeration::ref_from_raw(result) })
}
}
pub fn get_named_type_reference(&self) -> Result<Ref<NamedTypeReference>> {
let result = unsafe { BNGetTypeNamedTypeReference(self.handle) };
if result.is_null() {
Err(())
} else {
Ok(unsafe { NamedTypeReference::ref_from_raw(result) })
}
}
pub fn count(&self) -> u64 {
unsafe { BNGetTypeElementCount(self.handle) }
}
pub fn offset(&self) -> u64 {
unsafe { BNGetTypeOffset(self.handle) }
}
pub fn stack_adjustment(&self) -> Conf<i64> {
unsafe { BNGetTypeStackAdjustment(self.handle).into() }
}
pub fn registered_name(&self) -> Result<Ref<NamedTypeReference>> {
let result = unsafe { BNGetRegisteredTypeName(self.handle) };
if result.is_null() {
Err(())
} else {
Ok(unsafe { NamedTypeReference::ref_from_raw(result) })
}
}
// TODO : This and properties
// pub fn tokens(&self) -> ? {}
pub fn void() -> Ref<Self> {
unsafe { Self::ref_from_raw(BNCreateVoidType()) }
}
pub fn bool() -> Ref<Self> {
unsafe { Self::ref_from_raw(BNCreateBoolType()) }
}
pub fn char() -> Ref<Self> {
Self::int(1, true)
}
pub fn wide_char(width: usize) -> Ref<Self> {
unsafe {
Self::ref_from_raw(BNCreateWideCharType(
width,
BnString::new("").as_ptr() as *mut _,
))
}
}
pub fn int(width: usize, is_signed: bool) -> Ref<Self> {
let mut is_signed = Conf::new(is_signed, max_confidence()).into();
unsafe {
Self::ref_from_raw(BNCreateIntegerType(
width,
&mut is_signed,
BnString::new("").as_ptr() as *mut _,
))
}
}
pub fn named_int<S: BnStrCompatible>(width: usize, is_signed: bool, alt_name: S) -> Ref<Self> {
let mut is_signed = Conf::new(is_signed, max_confidence()).into();
// let alt_name = BnString::new(alt_name);
let alt_name = alt_name.into_bytes_with_nul(); // 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
unsafe {
Self::ref_from_raw(BNCreateIntegerType(
width,
&mut is_signed,
alt_name.as_ref().as_ptr() as _,
))
}
}
pub fn float(width: usize) -> Ref<Self> {
unsafe {
Self::ref_from_raw(BNCreateFloatType(
width,
BnString::new("").as_ptr() as *mut _,
))
}
}
pub fn named_float<S: BnStrCompatible>(width: usize, alt_name: S) -> Ref<Self> {
// let alt_name = BnString::new(alt_name);
let alt_name = alt_name.into_bytes_with_nul(); // See same line in `named_int` above
unsafe { Self::ref_from_raw(BNCreateFloatType(width, alt_name.as_ref().as_ptr() as _)) }
}
pub fn array<'a, T: Into<Conf<&'a Type>>>(t: T, count: u64) -> Ref<Self> {
unsafe { Self::ref_from_raw(BNCreateArrayType(&t.into().into(), count)) }
}
/// 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
///
/// For simplicity's sake, that convention isn't followed and you can query the default_int_size from an arch, if you have it, if you need to
pub fn enumeration<T: Into<Conf<bool>>>(
enumeration: &Enumeration,
width: usize,
is_signed: T,
) -> Ref<Self> {
unsafe {
// TODO : This is _extremely fragile_, we should change the internals of BNCreateEnumerationType instead of doing this
let mut fake_arch: BNArchitecture = mem::zeroed();
Self::ref_from_raw(BNCreateEnumerationType(
&mut fake_arch,
enumeration.handle,
width,
&mut is_signed.into().into(),
))
}
}
pub fn structure(structure: &Structure) -> Ref<Self> {
unsafe { Self::ref_from_raw(BNCreateStructureType(structure.handle)) }
}
pub fn named_type(type_reference: &NamedTypeReference) -> Ref<Self> {
let mut is_const = Conf::new(false, min_confidence()).into();
let mut is_volatile = Conf::new(false, min_confidence()).into();
unsafe {
Self::ref_from_raw(BNCreateNamedTypeReference(
type_reference.handle,
0,
1,
&mut is_const,
&mut is_volatile,
))
}
}
pub fn named_type_from_type<S: BnStrCompatible>(name: S, t: &Type) -> Ref<Self> {
let mut name = QualifiedName::from(name);
unsafe {
Self::ref_from_raw(BNCreateNamedTypeReferenceFromTypeAndId(
BnString::new("").as_ptr() as *mut _,
&mut name.0,
t.handle,
))
}
}
pub fn function<'a, S: BnStrCompatible + Clone, T: Into<Conf<&'a Type>>>(
return_type: T,
parameters: &[FunctionParameter<S>],
variable_arguments: bool,
) -> Ref<Self> {
let mut return_type = return_type.into().into();
let mut variable_arguments = Conf::new(variable_arguments, max_confidence()).into();
let mut can_return = Conf::new(true, min_confidence()).into();
let mut pure = Conf::new(false, min_confidence()).into();
let mut raw_calling_convention: BNCallingConventionWithConfidence =
BNCallingConventionWithConfidence {
convention: ptr::null_mut(),
confidence: min_confidence(),
};
let mut stack_adjust = Conf::<i64>::new(0, min_confidence()).into();
let mut raw_parameters = Vec::<BNFunctionParameter>::with_capacity(parameters.len());
let mut parameter_name_references = Vec::with_capacity(parameters.len());
for parameter in parameters {
let raw_name = parameter.name.clone().into_bytes_with_nul();
let location = match &parameter.location {
Some(location) => location.raw(),
None => unsafe { mem::zeroed() },
};
raw_parameters.push(BNFunctionParameter {
name: raw_name.as_ref().as_ptr() as *mut _,
type_: parameter.t.contents.handle,
typeConfidence: parameter.t.confidence,
defaultLocation: parameter.location.is_none(),
location,
});
parameter_name_references.push(raw_name);
}
let reg_stack_adjust_regs = ptr::null_mut();
let reg_stack_adjust_values = ptr::null_mut();
let mut return_regs: BNRegisterSetWithConfidence = BNRegisterSetWithConfidence {
regs: ptr::null_mut(),
count: 0,
confidence: 0,
};
unsafe {
Self::ref_from_raw(BNNewTypeReference(BNCreateFunctionType(
&mut return_type,
&mut raw_calling_convention,
raw_parameters.as_mut_ptr(),
raw_parameters.len(),
&mut variable_arguments,
&mut can_return,
&mut stack_adjust,
reg_stack_adjust_regs,
reg_stack_adjust_values,
0,
&mut return_regs,
BNNameType::NoNameType,
&mut pure,
)))
}
}
pub fn function_with_options<
'a,
A: Architecture,
S: BnStrCompatible + Clone,
T: Into<Conf<&'a Type>>,
C: Into<Conf<&'a CallingConvention<A>>>,
>(
return_type: T,
parameters: &[FunctionParameter<S>],
variable_arguments: bool,
calling_convention: C,
stack_adjust: Conf<i64>,
) -> Ref<Self> {
let mut return_type = return_type.into().into();
let mut variable_arguments = Conf::new(variable_arguments, max_confidence()).into();
let mut can_return = Conf::new(true, min_confidence()).into();
let mut pure = Conf::new(false, min_confidence()).into();
let mut raw_calling_convention: BNCallingConventionWithConfidence =
calling_convention.into().into();
let mut stack_adjust = stack_adjust.into();
let mut raw_parameters = Vec::<BNFunctionParameter>::with_capacity(parameters.len());
let mut parameter_name_references = Vec::with_capacity(parameters.len());
let mut name_ptrs = vec![];
for parameter in parameters {
name_ptrs.push(parameter.name.clone());
}
for (name, parameter) in zip(name_ptrs, parameters) {
let raw_name = name.into_bytes_with_nul();
let location = match &parameter.location {
Some(location) => location.raw(),
None => unsafe { mem::zeroed() },
};
raw_parameters.push(BNFunctionParameter {
name: raw_name.as_ref().as_ptr() as *mut _,
type_: parameter.t.contents.handle,
typeConfidence: parameter.t.confidence,
defaultLocation: parameter.location.is_none(),
location,
});
parameter_name_references.push(raw_name);
}
// TODO: Update type signature and include these (will be a breaking change)
let reg_stack_adjust_regs = ptr::null_mut();
let reg_stack_adjust_values = ptr::null_mut();
let mut return_regs: BNRegisterSetWithConfidence = BNRegisterSetWithConfidence {
regs: ptr::null_mut(),
count: 0,
confidence: 0,
};
unsafe {
Self::ref_from_raw(BNCreateFunctionType(
&mut return_type,
&mut raw_calling_convention,
raw_parameters.as_mut_ptr(),
raw_parameters.len(),
&mut variable_arguments,
&mut can_return,
&mut stack_adjust,
reg_stack_adjust_regs,
reg_stack_adjust_values,
0,
&mut return_regs,
BNNameType::NoNameType,
&mut pure,
))
}
}
pub fn pointer<'a, A: Architecture, T: Into<Conf<&'a Type>>>(arch: &A, t: T) -> Ref<Self> {
let mut is_const = Conf::new(false, min_confidence()).into();
let mut is_volatile = Conf::new(false, min_confidence()).into();
unsafe {
Self::ref_from_raw(BNCreatePointerType(
arch.as_ref().0,
&t.into().into(),
&mut is_const,
&mut is_volatile,
ReferenceType::PointerReferenceType,
))
}
}
pub fn const_pointer<'a, A: Architecture, T: Into<Conf<&'a Type>>>(
arch: &A,
t: T,
) -> Ref<Self> {
let mut is_const = Conf::new(true, max_confidence()).into();
let mut is_volatile = Conf::new(false, min_confidence()).into();
unsafe {
Self::ref_from_raw(BNCreatePointerType(
arch.as_ref().0,
&t.into().into(),
&mut is_const,
&mut is_volatile,
ReferenceType::PointerReferenceType,
))
}
}
pub fn pointer_of_width<'a, T: Into<Conf<&'a Type>>>(
t: T,
size: usize,
is_const: bool,
is_volatile: bool,
ref_type: Option<ReferenceType>,
) -> Ref<Self> {
let mut is_const = Conf::new(is_const, max_confidence()).into();
let mut is_volatile = Conf::new(is_volatile, max_confidence()).into();
unsafe {
Self::ref_from_raw(BNCreatePointerTypeOfWidth(
size,
&t.into().into(),
&mut is_const,
&mut is_volatile,
ref_type.unwrap_or(ReferenceType::PointerReferenceType),
))
}
}
pub fn pointer_with_options<'a, A: Architecture, T: Into<Conf<&'a Type>>>(
arch: &A,
t: T,
is_const: bool,
is_volatile: bool,
ref_type: Option<ReferenceType>,
) -> Ref<Self> {
let mut is_const = Conf::new(is_const, max_confidence()).into();
let mut is_volatile = Conf::new(is_volatile, max_confidence()).into();
unsafe {
Self::ref_from_raw(BNCreatePointerType(
arch.as_ref().0,
&t.into().into(),
&mut is_const,
&mut is_volatile,
ref_type.unwrap_or(ReferenceType::PointerReferenceType),
))
}
}
pub fn generate_auto_demangled_type_id<'a, S: BnStrCompatible>(name: S) -> &'a BnStr {
let mut name = QualifiedName::from(name);
unsafe { BnStr::from_raw(BNGenerateAutoDemangledTypeId(&mut name.0)) }
}
}
impl fmt::Display for Type {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", unsafe {
BnString::from_raw(BNGetTypeString(
self.handle,
ptr::null_mut(),
BNTokenEscapingType::NoTokenEscapingType,
))
})
}
}
lazy_static! {
static ref TYPE_DEBUG_BV: Mutex<Option<Ref<BinaryView>>> =
Mutex::new(BinaryView::from_data(&FileMetadata::new(), &[]).ok());
}
impl fmt::Debug for Type {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Ok(lock) = TYPE_DEBUG_BV.lock() {
if let Some(bv) = &*lock {
let mut count: usize = 0;
let container = unsafe { BNGetAnalysisTypeContainer(bv.handle) };
let lines: *mut BNTypeDefinitionLine = unsafe {
BNGetTypeLines(
self.handle,
container,
"".as_ptr() as *const c_char,
80,
false,
BNTokenEscapingType::NoTokenEscapingType,
&mut count as *mut usize,
)
};
unsafe {
BNFreeTypeContainer(container);
}
if lines.is_null() {
return Err(fmt::Error);
}
let line_slice: &[BNTypeDefinitionLine] =
unsafe { slice::from_raw_parts(lines, count) };
for (i, line) in line_slice.iter().enumerate() {
if i > 0 {
writeln!(f)?;
}
let tokens: &[BNInstructionTextToken] =
unsafe { slice::from_raw_parts(line.tokens, line.count) };
for token in tokens {
let text: *const c_char = token.text;
let str = unsafe { CStr::from_ptr(text) };
write!(f, "{}", str.to_string_lossy())?;
}
}
unsafe {
BNFreeTypeDefinitionLineList(lines, count);
}
return Ok(());
}
}
Err(fmt::Error)
}
}
impl PartialEq for Type {
fn eq(&self, other: &Self) -> bool {
unsafe { BNTypesEqual(self.handle, other.handle) }
}
}
impl Eq for Type {}
impl Hash for Type {
fn hash<H: Hasher>(&self, state: &mut H) {
self.handle.hash(state);
}
}
unsafe impl Send for Type {}
unsafe impl Sync for Type {}
unsafe impl RefCountable for Type {
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
Self::ref_from_raw(BNNewTypeReference(handle.handle))
}
unsafe fn dec_ref(handle: &Self) {
BNFreeType(handle.handle);
}
}
impl ToOwned for Type {
type Owned = Ref<Self>;
fn to_owned(&self) -> Self::Owned {
unsafe { RefCountable::inc_ref(self) }
}
}
///////////////////////
// FunctionParameter
#[derive(Clone, Debug)]
pub struct FunctionParameter<S: BnStrCompatible> {
pub t: Conf<Ref<Type>>,
pub name: S,
pub location: Option<Variable>,
}
impl<S: BnStrCompatible> FunctionParameter<S> {
pub fn new<T: Into<Conf<Ref<Type>>>>(t: T, name: S, location: Option<Variable>) -> Self {
Self {
t: t.into(),
name,
location,
}
}
}
impl FunctionParameter<BnString> {
pub(crate) fn from_raw(member: BNFunctionParameter) -> Self {
let name: BnString = if member.name.is_null() {
if member.location.type_ == BNVariableSourceType::RegisterVariableSourceType {
BnString::new(format!("reg_{}", member.location.storage))
} else if member.location.type_ == BNVariableSourceType::StackVariableSourceType {
BnString::new(format!("arg_{}", member.location.storage))
} else {
BnString::new("")
}
} else {
BnString::new(unsafe { BnStr::from_raw(member.name) })
};
Self {
t: Conf::new(
unsafe { Type::ref_from_raw(BNNewTypeReference(member.type_)) },
member.typeConfidence,
),
name,
location: if member.defaultLocation {
None
} else {
Some(unsafe { Variable::from_raw(member.location) })
},
}
}
}
//////////////
// Variable
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
pub struct Variable {
pub t: BNVariableSourceType,
pub index: u32,
pub storage: i64,
}
impl Variable {
pub fn new(t: BNVariableSourceType, index: u32, storage: i64) -> Self {
Self { t, index, storage }
}
pub(crate) unsafe fn from_raw(var: BNVariable) -> Self {
Self {
t: var.type_,
index: var.index,
storage: var.storage,
}
}
pub(crate) fn raw(&self) -> BNVariable {
BNVariable {
type_: self.t,
index: self.index,
storage: self.storage,
}
}
}
//////////////
// SSAVariable
#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
pub struct SSAVariable {
pub variable: Variable,
pub version: usize,
}
impl SSAVariable {
pub fn new(variable: Variable, version: usize) -> Self {
Self { variable, version }
}
}
///////////////
// NamedVariable
pub struct NamedTypedVariable {
var: BNVariable,
auto_defined: bool,
type_confidence: u8,
name: *mut c_char,
ty: *mut BNType,
}
impl NamedTypedVariable {
pub fn name(&self) -> &str {
unsafe { BnStr::from_raw(self.name).as_str() }
}
pub fn var(&self) -> Variable {
unsafe { Variable::from_raw(self.var) }
}
pub fn auto_defined(&self) -> bool {
self.auto_defined
}
pub fn type_confidence(&self) -> u8 {
self.type_confidence
}
pub fn var_type(&self) -> Ref<Type> {
unsafe { Ref::new(Type::from_raw(self.ty)) }
}
}
impl CoreArrayProvider for NamedTypedVariable {
type Raw = BNVariableNameAndType;
type Context = ();
}
unsafe impl CoreOwnedArrayProvider for NamedTypedVariable {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeVariableNameAndTypeList(raw, count)
}
}
unsafe impl<'a> CoreArrayWrapper<'a> for NamedTypedVariable {
type Wrapped = ManuallyDrop<NamedTypedVariable>;
unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
ManuallyDrop::new(NamedTypedVariable {
var: raw.var,
ty: raw.type_,
name: raw.name,
auto_defined: raw.autoDefined,
type_confidence: raw.typeConfidence,
})
}
}
////////////////////////
// EnumerationBuilder
#[derive(Debug, Clone)]
pub struct EnumerationMember {
pub name: BnString,
pub value: u64,
pub is_default: bool,
}
impl EnumerationMember {
pub fn new<S: BnStrCompatible>(name: S, value: u64, is_default: bool) -> Self {
Self {
name: BnString::new(name),
value,
is_default,
}
}
pub(crate) unsafe fn from_raw(member: BNEnumerationMember) -> Self {
Self {
name: BnString::new(raw_to_string(member.name).unwrap()),
value: member.value,
is_default: member.isDefault,
}
}
}
#[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<Enumeration> {
unsafe { Enumeration::ref_from_raw(BNFinalizeEnumerationBuilder(self.handle)) }
}
pub fn append<S: BnStrCompatible>(&self, name: S) -> &Self {
let name = name.into_bytes_with_nul();
unsafe {
BNAddEnumerationBuilderMember(self.handle, name.as_ref().as_ptr() as _);
}
self
}
pub fn insert<S: BnStrCompatible>(&self, name: S, value: u64) -> &Self {
let name = name.into_bytes_with_nul();
unsafe {
BNAddEnumerationBuilderMemberWithValue(self.handle, name.as_ref().as_ptr() as _, value);
}
self
}
pub fn replace<S: BnStrCompatible>(&self, id: usize, name: S, value: u64) -> &Self {
let name = name.into_bytes_with_nul();
unsafe {
BNReplaceEnumerationBuilderMember(self.handle, id, name.as_ref().as_ptr() as _, value);
}
self
}
pub fn remove(&self, id: usize) -> &Self {
unsafe {
BNRemoveEnumerationBuilderMember(self.handle, id);
}
self
}
pub fn members(&self) -> Vec<EnumerationMember> {
unsafe {
let mut count: usize = mem::zeroed();
let members_raw = BNGetEnumerationBuilderMembers(self.handle, &mut count);
let members: &[BNEnumerationMember] = slice::from_raw_parts(members_raw, count);
let result = (0..count)
.map(|i| EnumerationMember::from_raw(members[i]))
.collect();
BNFreeEnumerationMemberList(members_raw, count);
result
}
}
}
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) };
}
}
/////////////////
// Enumeration
#[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<Self> {
debug_assert!(!handle.is_null());
Ref::new(Self { handle })
}
pub fn builder() -> EnumerationBuilder {
EnumerationBuilder::new()
}
pub fn members(&self) -> Vec<EnumerationMember> {
unsafe {
let mut count: usize = mem::zeroed();
let members_raw = BNGetEnumerationMembers(self.handle, &mut count);
let members: &[BNEnumerationMember] = slice::from_raw_parts(members_raw, count);
let result = (0..count)
.map(|i| EnumerationMember::from_raw(members[i]))
.collect();
BNFreeEnumerationMemberList(members_raw, count);
result
}
}
}
unsafe impl RefCountable for Enumeration {
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
Self::ref_from_raw(BNNewEnumerationReference(handle.handle))
}
unsafe fn dec_ref(handle: &Self) {
BNFreeEnumeration(handle.handle);
}
}
impl ToOwned for Enumeration {
type Owned = Ref<Self>;
fn to_owned(&self) -> Self::Owned {
unsafe { RefCountable::inc_ref(self) }
}
}
//////////////////////
// StructureBuilder
pub type StructureType = BNStructureVariant;
#[derive(PartialEq, Eq, Hash)]
pub struct StructureBuilder {
pub(crate) handle: *mut BNStructureBuilder,
}
/// ```rust
/// // Includes
/// use binaryninja::types::{Structure, Type};
///
/// // Define struct, set size (in bytes)
/// let mut my_custom_struct = StructureBuilder::new();
/// let field_1 = Type::named_int(5, false, "my_weird_int_type");
/// let field_2 = Type::int(4, false);
/// let field_3 = Type::int(8, false);
///
/// // Assign those fields
/// my_custom_struct.append(&field_1, "field_4");
/// my_custom_struct.insert(&field_1, "field_1", 0);
/// my_custom_struct.insert(&field_2, "field_2", 5);
/// my_custom_struct.insert(&field_3, "field_3", 9);
///
/// // Convert structure to type
/// let my_custom_structure_type = Self::structure_type(&mut my_custom_struct);
///
/// // Add the struct to the binary view to use in analysis
/// let bv = unsafe { BinaryView::from_raw(view) };
/// 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 }
}
// Chainable terminal
pub fn finalize(&self) -> Ref<Structure> {
unsafe { Structure::ref_from_raw(BNFinalizeStructureBuilder(self.handle)) }
}
// Chainable builders/setters
pub fn set_width(&self, width: u64) -> &Self {
unsafe {
BNSetStructureBuilderWidth(self.handle, width);
}
self
}
pub fn set_alignment(&self, alignment: usize) -> &Self {
unsafe {
BNSetStructureBuilderAlignment(self.handle, alignment);
}
self
}
pub fn set_packed(&self, packed: bool) -> &Self {
unsafe {
BNSetStructureBuilderPacked(self.handle, packed);
}
self
}
pub fn set_structure_type(&self, t: StructureType) -> &Self {
unsafe { BNSetStructureBuilderType(self.handle, t) };
self
}
pub fn set_pointer_offset(&self, offset: i64) -> &Self {
unsafe { BNSetStructureBuilderPointerOffset(self.handle, offset) };
self
}
pub fn set_propagates_data_var_refs(&self, does: bool) -> &Self {
unsafe { BNSetStructureBuilderPropagatesDataVariableReferences(self.handle, does) };
self
}
pub fn set_base_structures(&self, bases: Vec<BaseStructure>) -> &Self {
let mut bases_api = vec![];
for base in bases {
bases_api.push(BNBaseStructure {
type_: base.ty.handle,
offset: base.offset,
width: base.width,
});
}
unsafe {
BNSetBaseStructuresForStructureBuilder(
self.handle,
bases_api.as_mut_ptr(),
bases_api.len(),
)
};
self
}
pub fn append<'a, S: BnStrCompatible, T: Into<Conf<&'a Type>>>(
&self,
t: T,
name: S,
access: MemberAccess,
scope: MemberScope,
) -> &Self {
let name = name.into_bytes_with_nul();
unsafe {
BNAddStructureBuilderMember(
self.handle,
&t.into().into(),
name.as_ref().as_ptr() as _,
access,
scope,
);
}
self
}
pub fn insert_member(&self, member: &StructureMember, overwrite_existing: bool) -> &Self {
let ty = member.ty.clone();
self.insert(
ty.as_ref(),
member.name.clone(),
member.offset,
overwrite_existing,
member.access,
member.scope,
);
self
}
pub fn insert<'a, S: BnStrCompatible, T: Into<Conf<&'a Type>>>(
&self,
t: T,
name: S,
offset: u64,
overwrite_existing: bool,
access: MemberAccess,
scope: MemberScope,
) -> &Self {
let name = name.into_bytes_with_nul();
unsafe {
BNAddStructureBuilderMemberAtOffset(
self.handle,
&t.into().into(),
name.as_ref().as_ptr() as _,
offset,
overwrite_existing,
access,
scope,
);
}
self
}
pub fn with_members<'a, S: BnStrCompatible, T: Into<Conf<&'a Type>>>(
&self,
members: impl IntoIterator<Item = (T, S)>,
) -> &Self {
for (t, name) in members.into_iter() {
self.append(t, name, MemberAccess::NoAccess, MemberScope::NoScope);
}
self
}
// Getters
pub fn width(&self) -> u64 {
unsafe { BNGetStructureBuilderWidth(self.handle) }
}
pub fn alignment(&self) -> usize {
unsafe { BNGetStructureBuilderAlignment(self.handle) }
}
pub fn packed(&self) -> bool {
unsafe { BNIsStructureBuilderPacked(self.handle) }
}
pub fn structure_type(&self) -> StructureType {
unsafe { BNGetStructureBuilderType(self.handle) }
}
pub fn pointer_offset(&self) -> i64 {
unsafe { BNGetStructureBuilderPointerOffset(self.handle) }
}
pub fn propagates_data_var_refs(&self) -> bool {
unsafe { BNStructureBuilderPropagatesDataVariableReferences(self.handle) }
}
pub fn base_structures(&self) -> Result<Vec<BaseStructure>> {
let mut count = 0usize;
let bases = unsafe { BNGetBaseStructuresForStructureBuilder(self.handle, &mut count) };
if bases.is_null() {
Err(())
} else {
let bases_slice = unsafe { slice::from_raw_parts_mut(bases, count) };
let result = bases_slice
.iter()
.map(|base| unsafe { BaseStructure::from_raw(*base) })
.collect::<Vec<_>>();
unsafe {
BNFreeBaseStructureList(bases, count);
}
Ok(result)
}
}
// TODO : The other methods in the python version (type, members, remove, replace, etc)
}
impl From<&Structure> for StructureBuilder {
fn from(structure: &Structure) -> StructureBuilder {
unsafe { Self::from_raw(BNCreateStructureBuilderFromStructure(structure.handle)) }
}
}
impl From<Vec<StructureMember>> for StructureBuilder {
fn from(members: Vec<StructureMember>) -> StructureBuilder {
let builder = StructureBuilder::new();
for m in members {
builder.insert_member(&m, false);
}
builder
}
}
impl Debug for StructureBuilder {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "StructureBuilder {{ ... }}")
}
}
impl Drop for StructureBuilder {
fn drop(&mut self) {
unsafe { BNFreeStructureBuilder(self.handle) };
}
}
impl Default for StructureBuilder {
fn default() -> Self {
Self::new()
}
}
///////////////
// Structure
#[derive(PartialEq, Eq, Hash)]
pub struct Structure {
pub(crate) handle: *mut BNStructure,
}
impl Structure {
unsafe fn from_raw(handle: *mut BNStructure) -> Self {
debug_assert!(!handle.is_null());
Self { handle }
}
pub(crate) unsafe fn ref_from_raw(handle: *mut BNStructure) -> Ref<Self> {
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) }
}
pub fn members(&self) -> Result<Vec<StructureMember>> {
unsafe {
let mut count: usize = mem::zeroed();
let members_raw: *mut BNStructureMember =
BNGetStructureMembers(self.handle, &mut count);
if members_raw.is_null() {
return Err(());
}
let members = slice::from_raw_parts(members_raw, count);
let result = (0..count)
.map(|i| StructureMember::from_raw(members[i]))
.collect();
BNFreeStructureMemberList(members_raw, count);
Ok(result)
}
}
pub fn base_structures(&self) -> Result<Vec<BaseStructure>> {
let mut count = 0usize;
let bases = unsafe { BNGetBaseStructuresForStructure(self.handle, &mut count) };
if bases.is_null() {
Err(())
} else {
let bases_slice = unsafe { slice::from_raw_parts_mut(bases, count) };
let result = bases_slice
.iter()
.map(|base| unsafe { BaseStructure::from_raw(*base) })
.collect::<Vec<_>>();
unsafe {
BNFreeBaseStructureList(bases, count);
}
Ok(result)
}
}
// TODO : The other methods in the python version (alignment, packed, type, members, remove, replace, etc)
}
impl Debug for Structure {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "Structure {{")?;
if let Ok(members) = self.members() {
for member in members {
write!(f, " {:?}", member)?;
}
}
write!(f, "}}")
}
}
unsafe impl RefCountable for Structure {
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
Ref::new(Self::from_raw(BNNewStructureReference(handle.handle)))
}
unsafe fn dec_ref(handle: &Self) {
BNFreeStructure(handle.handle);
}
}
impl ToOwned for Structure {
type Owned = Ref<Self>;
fn to_owned(&self) -> Self::Owned {
unsafe { RefCountable::inc_ref(self) }
}
}
#[derive(Debug, Clone)]
pub struct StructureMember {
pub ty: Conf<Ref<Type>>,
pub name: BnString,
pub offset: u64,
pub access: MemberAccess,
pub scope: MemberScope,
}
impl StructureMember {
pub fn new<T: BnStrCompatible>(
ty: Conf<Ref<Type>>,
name: T,
offset: u64,
access: MemberAccess,
scope: MemberScope,
) -> Self {
Self {
ty,
name: BnString::new(name),
offset,
access,
scope,
}
}
pub(crate) unsafe fn from_raw(handle: BNStructureMember) -> Self {
Self {
ty: Conf::new(
RefCountable::inc_ref(&Type::from_raw(handle.type_)),
handle.typeConfidence,
),
name: BnString::new(BnStr::from_raw(handle.name)),
offset: handle.offset,
access: handle.access,
scope: handle.scope,
}
}
}
#[derive(Debug, Clone)]
pub struct InheritedStructureMember {
pub base: Ref<NamedTypeReference>,
pub base_offset: u64,
pub member: StructureMember,
pub member_index: usize,
}
impl InheritedStructureMember {
pub fn new(
base: Ref<NamedTypeReference>,
base_offset: u64,
member: StructureMember,
member_index: usize,
) -> Self {
Self {
base,
base_offset,
member,
member_index,
}
}
// pub(crate) unsafe fn from_raw(handle: BNInheritedStructureMember) -> Self {
// Self {
// base: RefCountable::inc_ref(&NamedTypeReference::from_raw(handle.base)),
// base_offset: handle.baseOffset,
// member: StructureMember::from_raw(handle.member),
// member_index: handle.memberIndex,
// }
// }
}
#[derive(Debug, Clone)]
pub struct BaseStructure {
pub ty: Ref<NamedTypeReference>,
pub offset: u64,
pub width: u64,
}
impl BaseStructure {
pub fn new(ty: Ref<NamedTypeReference>, offset: u64, width: u64) -> Self {
Self { ty, offset, width }
}
pub(crate) unsafe fn from_raw(handle: BNBaseStructure) -> Self {
Self {
ty: RefCountable::inc_ref(&NamedTypeReference::from_raw(handle.type_)),
offset: handle.offset,
width: handle.width,
}
}
}
////////////////////////
// NamedTypeReference
#[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<Self> {
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, mut name: QualifiedName) -> Ref<Self> {
unsafe {
RefCountable::inc_ref(&Self {
handle: BNCreateNamedType(type_class, ptr::null() as *const _, &mut name.0),
})
}
}
/// 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<S: BnStrCompatible>(
type_class: NamedTypeReferenceClass,
type_id: S,
mut name: QualifiedName,
) -> Ref<Self> {
let type_id = type_id.into_bytes_with_nul();
unsafe {
RefCountable::inc_ref(&Self {
handle: BNCreateNamedType(type_class, type_id.as_ref().as_ptr() as _, &mut name.0),
})
}
}
pub fn name(&self) -> QualifiedName {
let named_ref: BNQualifiedName = unsafe { BNGetTypeReferenceName(self.handle) };
QualifiedName(named_ref)
}
pub fn id(&self) -> BnString {
unsafe { BnString::from_raw(BNGetTypeReferenceId(self.handle)) }
}
pub fn class(&self) -> NamedTypeReferenceClass {
unsafe { BNGetTypeReferenceClass(self.handle) }
}
fn target_helper(&self, bv: &BinaryView, visited: &mut HashSet<BnString>) -> Option<Ref<Type>> {
// TODO : This is a clippy bug (#10088, I think); remove after we upgrade past 2022-12-12
#[allow(clippy::manual_filter)]
if let Some(t) = bv.get_type_by_id(self.id()) {
if t.type_class() != TypeClass::NamedTypeReferenceClass {
Some(t)
} else {
let t = t.get_named_type_reference().unwrap();
if visited.contains(&t.id()) {
error!("Can't get target for recursively defined type!");
None
} else {
visited.insert(t.id());
t.target_helper(bv, visited)
}
}
} else {
None
}
}
pub fn target(&self, bv: &BinaryView) -> Option<Ref<Type>> {
//! Returns the type referenced by this named type reference
self.target_helper(bv, &mut HashSet::new())
}
}
impl ToOwned for NamedTypeReference {
type Owned = Ref<Self>;
fn to_owned(&self) -> Self::Owned {
unsafe { RefCountable::inc_ref(self) }
}
}
unsafe impl RefCountable for NamedTypeReference {
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
Self::ref_from_raw(BNNewNamedTypeReference(handle.handle))
}
unsafe fn dec_ref(handle: &Self) {
BNFreeNamedTypeReference(handle.handle)
}
}
impl Debug for NamedTypeReference {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{} (id: {})", self.name(), self.id())
}
}
///////////////////
// QualifiedName
#[repr(transparent)]
pub struct QualifiedName(pub(crate) BNQualifiedName);
impl QualifiedName {
// TODO : I think this is bad
pub fn string(&self) -> String {
unsafe {
slice::from_raw_parts(self.0.name, self.0.nameCount)
.iter()
.map(|c| CStr::from_ptr(*c).to_string_lossy())
.collect::<Vec<_>>()
.join("::")
}
}
pub fn join(&self) -> Cow<str> {
let join: *mut c_char = self.0.join;
unsafe { CStr::from_ptr(join) }.to_string_lossy()
}
pub fn strings(&self) -> Vec<Cow<str>> {
let names: *mut *mut c_char = self.0.name;
unsafe {
slice::from_raw_parts(names, self.0.nameCount)
.iter()
.map(|name| CStr::from_ptr(*name).to_string_lossy())
.collect::<Vec<_>>()
}
}
pub fn len(&self) -> usize {
self.0.nameCount
}
pub fn is_empty(&self) -> bool {
self.0.nameCount == 0
}
}
impl<S: BnStrCompatible> From<S> for QualifiedName {
fn from(name: S) -> Self {
let join = BnString::new("::");
let name = name.into_bytes_with_nul();
let mut list = vec![name.as_ref().as_ptr() as *const _];
QualifiedName(BNQualifiedName {
name: unsafe { BNAllocStringList(list.as_mut_ptr(), 1) },
join: join.into_raw(),
nameCount: 1,
})
}
}
impl<S: BnStrCompatible> From<Vec<S>> for QualifiedName {
fn from(names: Vec<S>) -> Self {
let join = BnString::new("::");
let names = names
.into_iter()
.map(|n| n.into_bytes_with_nul())
.collect::<Vec<_>>();
let mut list = names
.iter()
.map(|n| n.as_ref().as_ptr() as *const _)
.collect::<Vec<_>>();
QualifiedName(BNQualifiedName {
name: unsafe { BNAllocStringList(list.as_mut_ptr(), list.len()) },
join: join.into_raw(),
nameCount: list.len(),
})
}
}
impl Clone for QualifiedName {
fn clone(&self) -> Self {
let strings = self.strings();
let name = Self::from(strings.iter().collect::<Vec<&Cow<str>>>());
name
}
}
impl Hash for QualifiedName {
fn hash<H: Hasher>(&self, state: &mut H) {
self.join().hash(state);
self.strings().hash(state);
}
}
impl Debug for QualifiedName {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.string())
}
}
impl Display for QualifiedName {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.string())
}
}
impl PartialEq for QualifiedName {
fn eq(&self, other: &Self) -> bool {
self.strings() == other.strings()
}
}
impl Eq for QualifiedName {}
impl Drop for QualifiedName {
fn drop(&mut self) {
unsafe {
BNFreeQualifiedName(&mut self.0);
}
}
}
impl CoreArrayProvider for QualifiedName {
type Raw = BNQualifiedName;
type Context = ();
}
unsafe impl CoreOwnedArrayProvider for QualifiedName {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeTypeNameList(raw, count);
}
}
unsafe impl<'a> CoreArrayWrapper<'a> for QualifiedName {
type Wrapped = &'a QualifiedName;
unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
mem::transmute(raw)
}
}
//////////////////////////
// QualifiedNameAndType
#[repr(transparent)]
pub struct QualifiedNameAndType(pub(crate) BNQualifiedNameAndType);
impl QualifiedNameAndType {
pub fn name(&self) -> &QualifiedName {
unsafe { mem::transmute(&self.0.name) }
}
pub fn type_object(&self) -> Guard<Type> {
unsafe { Guard::new(Type::from_raw(self.0.type_), self) }
}
}
impl Drop for QualifiedNameAndType {
fn drop(&mut self) {
unsafe {
BNFreeQualifiedNameAndType(&mut self.0);
}
}
}
impl CoreArrayProvider for QualifiedNameAndType {
type Raw = BNQualifiedNameAndType;
type Context = ();
}
unsafe impl CoreOwnedArrayProvider for QualifiedNameAndType {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeTypeAndNameList(raw, count);
}
}
unsafe impl<'a> CoreArrayWrapper<'a> for QualifiedNameAndType {
type Wrapped = &'a QualifiedNameAndType;
unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
mem::transmute(raw)
}
}
//////////////////////////
// QualifiedNameTypeAndId
#[repr(transparent)]
pub struct QualifiedNameTypeAndId(pub(crate) BNQualifiedNameTypeAndId);
impl QualifiedNameTypeAndId {
pub fn name(&self) -> &QualifiedName {
unsafe { mem::transmute(&self.0.name) }
}
pub fn id(&self) -> &BnStr {
unsafe { BnStr::from_raw(self.0.id) }
}
pub fn type_object(&self) -> Guard<Type> {
unsafe { Guard::new(Type::from_raw(self.0.type_), self) }
}
}
impl Drop for QualifiedNameTypeAndId {
fn drop(&mut self) {
unsafe {
BNFreeQualifiedNameTypeAndId(&mut self.0);
}
}
}
impl CoreArrayProvider for QualifiedNameTypeAndId {
type Raw = BNQualifiedNameTypeAndId;
type Context = ();
}
unsafe impl CoreOwnedArrayProvider for QualifiedNameTypeAndId {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeTypeIdList(raw, count);
}
}
unsafe impl<'a> CoreArrayWrapper<'a> for QualifiedNameTypeAndId {
type Wrapped = &'a QualifiedNameTypeAndId;
unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
mem::transmute(raw)
}
}
//////////////////////////
// NameAndType
pub struct NameAndType<S: BnStrCompatible> {
pub name: S,
pub t: Conf<Ref<Type>>,
}
impl NameAndType<String> {
pub(crate) fn from_raw(raw: &BNNameAndType) -> Self {
Self::new(
raw_to_string(raw.name).unwrap(),
unsafe { &Type::ref_from_raw(raw.type_) },
raw.typeConfidence,
)
}
}
impl<S: BnStrCompatible> NameAndType<S> {
pub fn new(name: S, t: &Ref<Type>, confidence: u8) -> Self {
Self {
name,
t: Conf::new(t.clone(), confidence),
}
}
pub(crate) fn into_raw(self) -> BNNameAndType {
let t = self.t.clone();
let res = BNNameAndType {
name: BnString::new(self.name).into_raw(),
type_: t.contents.handle,
typeConfidence: self.t.confidence,
};
mem::forget(t);
res
}
pub fn type_with_confidence(&self) -> Conf<Ref<Type>> {
self.t.clone()
}
}
impl<S: BnStrCompatible> CoreArrayProvider for NameAndType<S> {
type Raw = BNNameAndType;
type Context = ();
}
unsafe impl<S: BnStrCompatible> CoreOwnedArrayProvider for NameAndType<S> {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeNameAndTypeList(raw, count);
}
}
unsafe impl<'a, S: 'a + BnStrCompatible> CoreArrayWrapper<'a> for NameAndType<S> {
type Wrapped = &'a NameAndType<S>;
unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
mem::transmute(raw)
}
}
//////////////////
// DataVariable
pub struct DataVariable {
pub address: u64,
pub t: Conf<Ref<Type>>,
pub auto_discovered: bool,
}
// impl DataVariable {
// pub(crate) fn from_raw(var: &BNDataVariable) -> Self {
// Self {
// address: var.address,
// t: Conf::new(unsafe { Type::ref_from_raw(var.type_) }, var.typeConfidence),
// auto_discovered: var.autoDiscovered,
// }
// }
// }
impl DataVariable {
pub fn type_with_confidence(&self) -> Conf<Ref<Type>> {
Conf::new(self.t.contents.clone(), self.t.confidence)
}
pub fn symbol(&self, bv: &BinaryView) -> Option<Ref<Symbol>> {
bv.symbol_by_address(self.address).ok()
}
}
impl CoreArrayProvider for DataVariable {
type Raw = BNDataVariable;
type Context = ();
}
unsafe impl CoreOwnedArrayProvider for DataVariable {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeDataVariables(raw, count);
}
}
unsafe impl<'a> CoreArrayWrapper<'a> for DataVariable {
type Wrapped = &'a DataVariable;
unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
mem::transmute(raw)
}
}
/////////////////////////
// DataVariableAndName
pub struct DataVariableAndName<S: BnStrCompatible> {
pub address: u64,
pub t: Conf<Ref<Type>>,
pub auto_discovered: bool,
pub name: S,
}
impl DataVariableAndName<String> {
pub(crate) fn from_raw(var: &BNDataVariableAndName) -> Self {
Self {
address: var.address,
t: Conf::new(unsafe { Type::ref_from_raw(var.type_) }, var.typeConfidence),
auto_discovered: var.autoDiscovered,
name: raw_to_string(var.name).unwrap(),
}
}
}
impl<S: BnStrCompatible> DataVariableAndName<S> {
pub fn new(address: u64, t: Conf<Ref<Type>>, auto_discovered: bool, name: S) -> Self {
Self {
address,
t,
auto_discovered,
name,
}
}
pub fn type_with_confidence(&self) -> Conf<Ref<Type>> {
Conf::new(self.t.contents.clone(), self.t.confidence)
}
}
// unsafe impl<S: BnStrCompatible> CoreArrayProvider for DataVariableAndName<S> {
// type Raw = BNDataVariableAndName;
// type Context = ();
// }
// unsafe impl<S: BnStrCompatible> CoreOwnedArrayProvider for DataVariableAndName<S> {
// unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
// BNFreeDataVariablesAndName(raw, count);
// }
// }
// unsafe impl<'a, S: 'a + BnStrCompatible> CoreArrayWrapper<'a> for DataVariableAndName<S> {
// type Wrapped = &'a DataVariableAndName<S>;
// }
// unsafe impl<'a, S: 'a + BnStrCompatible> CoreArrayWrapper<'a> for DataVariableAndName<S> {
// unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
// mem::transmute(raw)
// }
// }