update rust crate
This commit is contained in:
@@ -23,7 +23,7 @@ use std::{
|
||||
collections::HashMap,
|
||||
ffi::{c_char, c_int, CStr, CString},
|
||||
hash::Hash,
|
||||
mem::zeroed,
|
||||
mem::{zeroed, MaybeUninit},
|
||||
ops, ptr, slice,
|
||||
};
|
||||
|
||||
@@ -97,11 +97,11 @@ impl<'a> Iterator for BranchIter<'a> {
|
||||
#[repr(C)]
|
||||
pub struct InstructionInfo(BNInstructionInfo);
|
||||
impl InstructionInfo {
|
||||
pub fn new(len: usize, branch_delay: bool) -> Self {
|
||||
pub fn new(len: usize, delay_slots: u8) -> Self {
|
||||
InstructionInfo(BNInstructionInfo {
|
||||
length: len,
|
||||
archTransitionByTargetAddr: false,
|
||||
branchDelay: branch_delay,
|
||||
delaySlots: delay_slots,
|
||||
branchCount: 0usize,
|
||||
branchType: [BranchType::UnresolvedBranch; 3],
|
||||
branchTarget: [0u64; 3],
|
||||
@@ -121,8 +121,8 @@ impl InstructionInfo {
|
||||
self.0.branchCount
|
||||
}
|
||||
|
||||
pub fn branch_delay(&self) -> bool {
|
||||
self.0.branchDelay
|
||||
pub fn delay_slots(&self) -> u8 {
|
||||
self.0.delaySlots
|
||||
}
|
||||
|
||||
pub fn branches(&self) -> BranchIter {
|
||||
@@ -296,7 +296,7 @@ pub trait FlagGroup: Sized + Clone + Copy {
|
||||
/// Types to represent the different comparisons, so for `cr1_lt` we
|
||||
/// would return a mapping along the lines of:
|
||||
///
|
||||
/// ```
|
||||
/// ```text
|
||||
/// cr1_signed -> LLFC_SLT,
|
||||
/// cr1_unsigned -> LLFC_ULT,
|
||||
/// ```
|
||||
@@ -313,7 +313,7 @@ pub trait Intrinsic: Sized + Clone + Copy {
|
||||
fn id(&self) -> u32;
|
||||
|
||||
/// Reeturns the list of the input names and types for this intrinsic.
|
||||
fn inputs(&self) -> Vec<NameAndType<String>>;
|
||||
fn inputs(&self) -> Vec<Ref<NameAndType>>;
|
||||
|
||||
/// Returns the list of the output types for this intrinsic.
|
||||
fn outputs(&self) -> Vec<Conf<Ref<Type>>>;
|
||||
@@ -650,7 +650,7 @@ impl Intrinsic for UnusedIntrinsic {
|
||||
fn id(&self) -> u32 {
|
||||
unreachable!()
|
||||
}
|
||||
fn inputs(&self) -> Vec<NameAndType<String>> {
|
||||
fn inputs(&self) -> Vec<Ref<NameAndType>> {
|
||||
unreachable!()
|
||||
}
|
||||
fn outputs(&self) -> Vec<Conf<Ref<Type>>> {
|
||||
@@ -715,6 +715,21 @@ impl Register for CoreRegister {
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for CoreRegister {
|
||||
type Raw = u32;
|
||||
type Context = CoreArchitecture;
|
||||
type Wrapped<'a> = Self;
|
||||
}
|
||||
|
||||
unsafe impl CoreArrayProviderInner for CoreRegister {
|
||||
unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
|
||||
BNFreeRegisterList(raw)
|
||||
}
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
Self(context.0, *raw)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CoreRegisterStackInfo(*mut BNArchitecture, BNRegisterStackInfo);
|
||||
|
||||
impl RegisterStackInfo for CoreRegisterStackInfo {
|
||||
@@ -968,8 +983,8 @@ impl FlagGroup for CoreFlagGroup {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub struct CoreIntrinsic(*mut BNArchitecture, u32);
|
||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||
pub struct CoreIntrinsic(pub(crate) *mut BNArchitecture, pub(crate) u32);
|
||||
|
||||
impl Intrinsic for crate::architecture::CoreIntrinsic {
|
||||
fn name(&self) -> Cow<str> {
|
||||
@@ -992,7 +1007,7 @@ impl Intrinsic for crate::architecture::CoreIntrinsic {
|
||||
self.1
|
||||
}
|
||||
|
||||
fn inputs(&self) -> Vec<NameAndType<String>> {
|
||||
fn inputs(&self) -> Vec<Ref<NameAndType>> {
|
||||
let mut count: usize = 0;
|
||||
|
||||
unsafe {
|
||||
@@ -1000,7 +1015,7 @@ impl Intrinsic for crate::architecture::CoreIntrinsic {
|
||||
|
||||
let ret = slice::from_raw_parts_mut(inputs, count)
|
||||
.iter()
|
||||
.map(NameAndType::from_raw)
|
||||
.map(|x| NameAndType::from_raw(x).to_owned())
|
||||
.collect();
|
||||
|
||||
BNFreeNameAndTypeList(inputs, count);
|
||||
@@ -1162,17 +1177,18 @@ impl Architecture for CoreArchitecture {
|
||||
&mut result as *mut _,
|
||||
&mut count as *mut _,
|
||||
) {
|
||||
let vec = Vec::<BNInstructionTextToken>::from_raw_parts(result, count, count)
|
||||
let vec = slice::from_raw_parts(result, count)
|
||||
.iter()
|
||||
.map(|x| InstructionTextToken::from_raw(x))
|
||||
.map(|x| InstructionTextToken::from_raw(x).to_owned())
|
||||
.collect();
|
||||
BNFreeInstructionText(result, count);
|
||||
Some((consumed, vec))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fn instruction_llil(
|
||||
&self,
|
||||
data: &[u8],
|
||||
@@ -1689,8 +1705,8 @@ where
|
||||
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync,
|
||||
F: FnOnce(CustomArchitectureHandle<A>, CoreArchitecture) -> A,
|
||||
{
|
||||
arch: A,
|
||||
func: F,
|
||||
arch: MaybeUninit<A>,
|
||||
func: Option<F>,
|
||||
}
|
||||
|
||||
extern "C" fn cb_init<A, F>(ctxt: *mut c_void, obj: *mut BNArchitecture)
|
||||
@@ -1704,11 +1720,10 @@ where
|
||||
handle: ctxt as *mut A,
|
||||
};
|
||||
|
||||
let create = ptr::read(&custom_arch.func);
|
||||
ptr::write(
|
||||
&mut custom_arch.arch,
|
||||
create(custom_arch_handle, CoreArchitecture(obj)),
|
||||
);
|
||||
let create = custom_arch.func.take().unwrap();
|
||||
custom_arch
|
||||
.arch
|
||||
.write(create(custom_arch_handle, CoreArchitecture(obj)));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1811,27 +1826,25 @@ where
|
||||
let data = unsafe { slice::from_raw_parts(data, *len) };
|
||||
let result = unsafe { &mut *result };
|
||||
|
||||
match custom_arch.instruction_text(data, addr) {
|
||||
Some((res_size, mut res_tokens)) => {
|
||||
unsafe {
|
||||
// TODO: Can't use into_raw_parts as it's unstable so we do this instead...
|
||||
let r_ptr = res_tokens.as_mut_ptr();
|
||||
let r_count = res_tokens.len();
|
||||
mem::forget(res_tokens);
|
||||
let Some((res_size, res_tokens)) = custom_arch.instruction_text(data, addr) else {
|
||||
return false;
|
||||
};
|
||||
|
||||
*result = &mut (*r_ptr).0;
|
||||
*count = r_count;
|
||||
*len = res_size;
|
||||
}
|
||||
true
|
||||
}
|
||||
None => false,
|
||||
let res_tokens: Box<[_]> = res_tokens.into_boxed_slice();
|
||||
unsafe {
|
||||
let res_tokens = Box::leak(res_tokens);
|
||||
let r_ptr = res_tokens.as_mut_ptr();
|
||||
let r_count = res_tokens.len();
|
||||
|
||||
*result = &mut (*r_ptr).0;
|
||||
*count = r_count;
|
||||
*len = res_size;
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
extern "C" fn cb_free_instruction_text(tokens: *mut BNInstructionTextToken, count: usize) {
|
||||
let _tokens =
|
||||
unsafe { Vec::from_raw_parts(tokens as *mut InstructionTextToken, count, count) };
|
||||
let _tokens = unsafe { Box::from_raw(ptr::slice_from_raw_parts_mut(tokens, count)) };
|
||||
}
|
||||
|
||||
extern "C" fn cb_instruction_llil<A>(
|
||||
@@ -1931,15 +1944,7 @@ where
|
||||
if len == 0 {
|
||||
ptr::null_mut()
|
||||
} else {
|
||||
let mut res = Vec::with_capacity(len + 1);
|
||||
|
||||
res.push(len as u32);
|
||||
|
||||
for i in items {
|
||||
res.push(i);
|
||||
}
|
||||
|
||||
assert!(res.len() == len + 1);
|
||||
let mut res: Box<[_]> = [len as u32].into_iter().chain(items).collect();
|
||||
|
||||
let raw = res.as_mut_ptr();
|
||||
mem::forget(res);
|
||||
@@ -2280,7 +2285,8 @@ where
|
||||
unsafe {
|
||||
let actual_start = regs.offset(-1);
|
||||
let len = *actual_start + 1;
|
||||
let _regs = Vec::from_raw_parts(actual_start, len as usize, len as usize);
|
||||
let regs_ptr = ptr::slice_from_raw_parts_mut(actual_start, len.try_into().unwrap());
|
||||
let _regs = Box::from_raw(regs_ptr);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2420,28 +2426,28 @@ where
|
||||
{
|
||||
let custom_arch = unsafe { &*(ctxt as *mut A) };
|
||||
|
||||
if let Some(intrinsic) = custom_arch.intrinsic_from_id(intrinsic) {
|
||||
let inputs = intrinsic.inputs();
|
||||
let mut res = Vec::with_capacity(inputs.len());
|
||||
for input in inputs {
|
||||
res.push(input.into_raw());
|
||||
}
|
||||
|
||||
unsafe {
|
||||
*count = res.len();
|
||||
if res.is_empty() {
|
||||
ptr::null_mut()
|
||||
} else {
|
||||
let raw = res.as_mut_ptr();
|
||||
mem::forget(res);
|
||||
raw
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let Some(intrinsic) = custom_arch.intrinsic_from_id(intrinsic) else {
|
||||
unsafe {
|
||||
*count = 0;
|
||||
}
|
||||
ptr::null_mut()
|
||||
return ptr::null_mut();
|
||||
};
|
||||
|
||||
let inputs = intrinsic.inputs();
|
||||
let mut res: Box<[_]> = inputs
|
||||
.into_iter()
|
||||
.map(|input| unsafe { Ref::into_raw(input) }.0)
|
||||
.collect();
|
||||
|
||||
unsafe {
|
||||
*count = res.len();
|
||||
if res.is_empty() {
|
||||
ptr::null_mut()
|
||||
} else {
|
||||
let raw = res.as_mut_ptr();
|
||||
mem::forget(res);
|
||||
raw
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2453,9 +2459,9 @@ where
|
||||
|
||||
if !nt.is_null() {
|
||||
unsafe {
|
||||
let list = Vec::from_raw_parts(nt, count, count);
|
||||
for nt in list {
|
||||
BnString::from_raw(nt.name);
|
||||
let name_and_types = Box::from_raw(ptr::slice_from_raw_parts_mut(nt, count));
|
||||
for nt in name_and_types.iter() {
|
||||
Ref::new(NameAndType::from_raw(nt));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2473,10 +2479,7 @@ where
|
||||
|
||||
if let Some(intrinsic) = custom_arch.intrinsic_from_id(intrinsic) {
|
||||
let inputs = intrinsic.outputs();
|
||||
let mut res = Vec::with_capacity(inputs.len());
|
||||
for input in inputs {
|
||||
res.push(input.into());
|
||||
}
|
||||
let mut res: Box<[_]> = inputs.iter().map(|input| input.as_ref().into()).collect();
|
||||
|
||||
unsafe {
|
||||
*count = res.len();
|
||||
@@ -2505,9 +2508,7 @@ where
|
||||
{
|
||||
let _custom_arch = unsafe { &*(ctxt as *mut A) };
|
||||
if !tl.is_null() {
|
||||
unsafe {
|
||||
let _list = Vec::from_raw_parts(tl, count, count);
|
||||
}
|
||||
let _type_list = unsafe { Box::from_raw(ptr::slice_from_raw_parts_mut(tl, count)) };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2685,8 +2686,8 @@ where
|
||||
let name = name.into_bytes_with_nul();
|
||||
|
||||
let uninit_arch = ArchitectureBuilder {
|
||||
arch: unsafe { zeroed() },
|
||||
func,
|
||||
arch: MaybeUninit::zeroed(),
|
||||
func: Some(func),
|
||||
};
|
||||
|
||||
let raw = Box::into_raw(Box::new(uninit_arch));
|
||||
@@ -2776,7 +2777,7 @@ where
|
||||
|
||||
assert!(!res.is_null());
|
||||
|
||||
&(*raw).arch
|
||||
(*raw).arch.assume_init_mut()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -104,21 +104,14 @@ unsafe impl RefCountable for BackgroundTask {
|
||||
impl CoreArrayProvider for BackgroundTask {
|
||||
type Raw = *mut BNBackgroundTask;
|
||||
type Context = ();
|
||||
type Wrapped<'a> = Guard<'a, BackgroundTask>;
|
||||
}
|
||||
|
||||
unsafe impl CoreOwnedArrayProvider for BackgroundTask {
|
||||
unsafe impl CoreArrayProviderInner for BackgroundTask {
|
||||
unsafe fn free(raw: *mut *mut BNBackgroundTask, count: usize, _context: &()) {
|
||||
BNFreeBackgroundTaskList(raw, count);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> CoreArrayWrapper<'a> for BackgroundTask {
|
||||
type Wrapped = Guard<'a, BackgroundTask>;
|
||||
|
||||
unsafe fn wrap_raw(
|
||||
raw: &'a *mut BNBackgroundTask,
|
||||
context: &'a (),
|
||||
) -> Guard<'a, BackgroundTask> {
|
||||
unsafe fn wrap_raw<'a>(raw: &'a *mut BNBackgroundTask, context: &'a ()) -> Self::Wrapped<'a> {
|
||||
Guard::new(BackgroundTask::from_raw(*raw), context)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,18 +68,14 @@ pub struct EdgeContext<'a, C: 'a + BlockContext> {
|
||||
impl<'a, C: 'a + BlockContext> CoreArrayProvider for Edge<'a, C> {
|
||||
type Raw = BNBasicBlockEdge;
|
||||
type Context = EdgeContext<'a, C>;
|
||||
type Wrapped<'b> = Edge<'b, C> where 'a: 'b;
|
||||
}
|
||||
|
||||
unsafe impl<'a, C: 'a + BlockContext> CoreOwnedArrayProvider for Edge<'a, C> {
|
||||
unsafe impl<'a, C: 'a + BlockContext> CoreArrayProviderInner for Edge<'a, C> {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeBasicBlockEdgeList(raw, count);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a, C: 'a + BlockContext> CoreArrayWrapper<'a> for Edge<'a, C> {
|
||||
type Wrapped = Edge<'a, C>;
|
||||
|
||||
unsafe fn wrap_raw(raw: &'a Self::Raw, context: &'a Self::Context) -> Edge<'a, C> {
|
||||
unsafe fn wrap_raw<'b>(raw: &'b Self::Raw, context: &'b Self::Context) -> Self::Wrapped<'b> {
|
||||
let edge_target = Guard::new(
|
||||
BasicBlock::from_raw(raw.target, context.orig_block.context.clone()),
|
||||
raw,
|
||||
@@ -301,18 +297,14 @@ unsafe impl<C: BlockContext> RefCountable for BasicBlock<C> {
|
||||
impl<C: BlockContext> CoreArrayProvider for BasicBlock<C> {
|
||||
type Raw = *mut BNBasicBlock;
|
||||
type Context = C;
|
||||
type Wrapped<'a> = Guard<'a, BasicBlock<C>> where C: 'a;
|
||||
}
|
||||
|
||||
unsafe impl<C: BlockContext> CoreOwnedArrayProvider for BasicBlock<C> {
|
||||
unsafe impl<C: BlockContext> CoreArrayProviderInner for BasicBlock<C> {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeBasicBlockList(raw, count);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a, C: 'a + BlockContext> CoreArrayWrapper<'a> for BasicBlock<C> {
|
||||
type Wrapped = Guard<'a, BasicBlock<C>>;
|
||||
|
||||
unsafe fn wrap_raw(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped {
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
Guard::new(BasicBlock::from_raw(*raw, context.clone()), context)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,24 +23,20 @@ pub use binaryninjacore_sys::BNAnalysisState as AnalysisState;
|
||||
pub use binaryninjacore_sys::BNModificationStatus as ModificationStatus;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::c_void;
|
||||
use std::ffi::{c_char, c_void};
|
||||
use std::ops::Range;
|
||||
use std::os::raw::c_char;
|
||||
use std::ptr;
|
||||
use std::result;
|
||||
use std::{ops, slice};
|
||||
use std::{ops, ptr, result, slice};
|
||||
|
||||
use crate::architecture::Architecture;
|
||||
use crate::architecture::CoreArchitecture;
|
||||
use crate::architecture::{Architecture, CoreArchitecture};
|
||||
use crate::basicblock::BasicBlock;
|
||||
use crate::component::{Component, ComponentBuilder, IntoComponentGuid};
|
||||
use crate::databuffer::DataBuffer;
|
||||
use crate::debuginfo::DebugInfo;
|
||||
use crate::fileaccessor::FileAccessor;
|
||||
use crate::filemetadata::FileMetadata;
|
||||
use crate::flowgraph::FlowGraph;
|
||||
use crate::function::{Function, NativeBlock};
|
||||
use crate::linearview::LinearDisassemblyLine;
|
||||
use crate::linearview::LinearViewCursor;
|
||||
use crate::linearview::{LinearDisassemblyLine, LinearViewCursor};
|
||||
use crate::metadata::Metadata;
|
||||
use crate::platform::Platform;
|
||||
use crate::relocation::Relocation;
|
||||
@@ -49,7 +45,10 @@ use crate::segment::{Segment, SegmentBuilder};
|
||||
use crate::settings::Settings;
|
||||
use crate::symbol::{Symbol, SymbolType};
|
||||
use crate::tags::{Tag, TagType};
|
||||
use crate::types::{DataVariable, NamedTypeReference, QualifiedName, QualifiedNameAndType, Type};
|
||||
use crate::typelibrary::TypeLibrary;
|
||||
use crate::types::{
|
||||
Conf, DataVariable, NamedTypeReference, QualifiedName, QualifiedNameAndType, Type,
|
||||
};
|
||||
use crate::Endianness;
|
||||
|
||||
use crate::rc::*;
|
||||
@@ -225,18 +224,10 @@ pub trait BinaryViewExt: BinaryViewBase {
|
||||
|
||||
/// Reads up to `len` bytes from address `offset`
|
||||
fn read_vec(&self, offset: u64, len: usize) -> Vec<u8> {
|
||||
let mut ret = Vec::with_capacity(len);
|
||||
let mut ret = vec![0; len];
|
||||
|
||||
unsafe {
|
||||
let res;
|
||||
|
||||
{
|
||||
let dest_slice = ret.get_unchecked_mut(0..len);
|
||||
res = self.read(dest_slice, offset);
|
||||
}
|
||||
|
||||
ret.set_len(res);
|
||||
}
|
||||
let size = self.read(&mut ret, offset);
|
||||
ret.truncate(size);
|
||||
|
||||
ret
|
||||
}
|
||||
@@ -244,26 +235,10 @@ pub trait BinaryViewExt: BinaryViewBase {
|
||||
/// Appends up to `len` bytes from address `offset` into `dest`
|
||||
fn read_into_vec(&self, dest: &mut Vec<u8>, offset: u64, len: usize) -> usize {
|
||||
let starting_len = dest.len();
|
||||
let space = dest.capacity() - starting_len;
|
||||
|
||||
if space < len {
|
||||
dest.reserve(len - space);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
let res;
|
||||
|
||||
{
|
||||
let dest_slice = dest.get_unchecked_mut(starting_len..starting_len + len);
|
||||
res = self.read(dest_slice, offset);
|
||||
}
|
||||
|
||||
if res > 0 {
|
||||
dest.set_len(starting_len + res);
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
dest.resize(starting_len + len, 0);
|
||||
let read_size = self.read(&mut dest[starting_len..], offset);
|
||||
dest.truncate(starting_len + read_size);
|
||||
read_size
|
||||
}
|
||||
|
||||
fn notify_data_written(&self, offset: u64, len: usize) {
|
||||
@@ -292,6 +267,18 @@ pub trait BinaryViewExt: BinaryViewBase {
|
||||
unsafe { BNIsOffsetWritableSemantics(self.as_ref().handle, offset) }
|
||||
}
|
||||
|
||||
fn original_image_base(&self) -> u64 {
|
||||
unsafe {
|
||||
BNGetOriginalImageBase(self.as_ref().handle)
|
||||
}
|
||||
}
|
||||
|
||||
fn set_original_image_base(&self, image_base: u64) {
|
||||
unsafe {
|
||||
BNSetOriginalImageBase(self.as_ref().handle, image_base)
|
||||
}
|
||||
}
|
||||
|
||||
fn end(&self) -> u64 {
|
||||
unsafe { BNGetEndOffset(self.as_ref().handle) }
|
||||
}
|
||||
@@ -574,16 +561,27 @@ pub trait BinaryViewExt: BinaryViewBase {
|
||||
}
|
||||
}
|
||||
|
||||
fn define_auto_data_var(&self, dv: DataVariable) {
|
||||
fn data_variable_at_address(&self, addr: u64) -> Option<Ref<DataVariable>> {
|
||||
let dv = BNDataVariable::default();
|
||||
unsafe {
|
||||
BNDefineDataVariable(self.as_ref().handle, dv.address, &mut dv.t.into());
|
||||
if BNGetDataVariableAtAddress(self.as_ref().handle, addr, std::mem::transmute(&dv)) {
|
||||
Some(DataVariable(dv).to_owned())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn define_auto_data_var<'a, T: Into<Conf<&'a Type>>>(&self, addr: u64, ty: T) {
|
||||
unsafe {
|
||||
BNDefineDataVariable(self.as_ref().handle, addr, &mut ty.into().into());
|
||||
}
|
||||
}
|
||||
|
||||
/// You likely would also like to call [`Self::define_user_symbol`] to bind this data variable with a name
|
||||
fn define_user_data_var(&self, dv: DataVariable) {
|
||||
fn define_user_data_var<'a, T: Into<Conf<&'a Type>>>(&self, addr: u64, ty: T) {
|
||||
unsafe {
|
||||
BNDefineUserDataVariable(self.as_ref().handle, dv.address, &mut dv.t.into());
|
||||
BNDefineUserDataVariable(self.as_ref().handle, addr, &mut ty.into().into());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -674,7 +672,7 @@ pub trait BinaryViewExt: BinaryViewBase {
|
||||
let name_array = unsafe { Array::<QualifiedName>::new(result_names, result_count, ()) };
|
||||
|
||||
for (id, name) in id_array.iter().zip(name_array.iter()) {
|
||||
result.insert(id.as_str().to_owned(), name.clone());
|
||||
result.insert(id.to_owned(), name.clone());
|
||||
}
|
||||
|
||||
result
|
||||
@@ -964,6 +962,15 @@ pub trait BinaryViewExt: BinaryViewBase {
|
||||
}
|
||||
}
|
||||
|
||||
fn entry_point_functions(&self) -> Array<Function> {
|
||||
unsafe {
|
||||
let mut count = 0;
|
||||
let functions = BNGetAllEntryFunctions(self.as_ref().handle, &mut count);
|
||||
|
||||
Array::new(functions, count, ())
|
||||
}
|
||||
}
|
||||
|
||||
fn functions(&self) -> Array<Function> {
|
||||
unsafe {
|
||||
let mut count = 0;
|
||||
@@ -1056,7 +1063,7 @@ pub trait BinaryViewExt: BinaryViewBase {
|
||||
unsafe { BNApplyDebugInfo(self.as_ref().handle, debug_info.handle) }
|
||||
}
|
||||
|
||||
fn show_graph_report<S: BnStrCompatible>(&self, raw_name: S, graph: FlowGraph) {
|
||||
fn show_graph_report<S: BnStrCompatible>(&self, raw_name: S, graph: &FlowGraph) {
|
||||
let raw_name = raw_name.into_bytes_with_nul();
|
||||
unsafe {
|
||||
BNShowGraphReport(
|
||||
@@ -1363,6 +1370,261 @@ pub trait BinaryViewExt: BinaryViewBase {
|
||||
Array::new(handle, count, ())
|
||||
}
|
||||
}
|
||||
|
||||
fn component_by_guid<S: BnStrCompatible>(&self, guid: S) -> Option<Component> {
|
||||
let name = guid.into_bytes_with_nul();
|
||||
let result = unsafe {
|
||||
BNGetComponentByGuid(
|
||||
self.as_ref().handle,
|
||||
name.as_ref().as_ptr() as *const core::ffi::c_char,
|
||||
)
|
||||
};
|
||||
core::ptr::NonNull::new(result).map(|h| unsafe { Component::from_raw(h) })
|
||||
}
|
||||
|
||||
fn root_component(&self) -> Option<Component> {
|
||||
let result = unsafe { BNGetRootComponent(self.as_ref().handle) };
|
||||
core::ptr::NonNull::new(result).map(|h| unsafe { Component::from_raw(h) })
|
||||
}
|
||||
|
||||
fn component_builder(&self) -> ComponentBuilder {
|
||||
ComponentBuilder::new_from_raw(self.as_ref().handle)
|
||||
}
|
||||
|
||||
fn component_by_path<P: BnStrCompatible>(&self, path: P) -> Option<Component> {
|
||||
let path = path.into_bytes_with_nul();
|
||||
let result = unsafe {
|
||||
BNGetComponentByPath(
|
||||
self.as_ref().handle,
|
||||
path.as_ref().as_ptr() as *const core::ffi::c_char,
|
||||
)
|
||||
};
|
||||
core::ptr::NonNull::new(result).map(|h| unsafe { Component::from_raw(h) })
|
||||
}
|
||||
|
||||
fn remove_component(&self, component: &Component) -> bool {
|
||||
unsafe { BNRemoveComponent(self.as_ref().handle, component.as_raw()) }
|
||||
}
|
||||
|
||||
fn remove_component_by_guid<P: IntoComponentGuid>(&self, guid: P) -> bool {
|
||||
let path = guid.component_guid();
|
||||
unsafe { BNRemoveComponentByGuid(self.as_ref().handle, path.as_ptr()) }
|
||||
}
|
||||
|
||||
fn data_variable_parent_components(
|
||||
&self,
|
||||
data_variable: &DataVariable,
|
||||
) -> Array<Component> {
|
||||
let mut count = 0;
|
||||
let result = unsafe {
|
||||
BNGetDataVariableParentComponents(
|
||||
self.as_ref().handle,
|
||||
data_variable.address(),
|
||||
&mut count,
|
||||
)
|
||||
};
|
||||
unsafe { Array::new(result, count, ()) }
|
||||
}
|
||||
|
||||
/// Make the contents of a type library available for type/import resolution
|
||||
fn add_type_library(&self, library: &TypeLibrary) {
|
||||
unsafe { BNAddBinaryViewTypeLibrary(self.as_ref().handle, library.as_raw()) }
|
||||
}
|
||||
|
||||
fn type_library_by_name<S: BnStrCompatible>(&self, name: S) -> Option<TypeLibrary> {
|
||||
let name = name.into_bytes_with_nul();
|
||||
let result = unsafe {
|
||||
BNGetBinaryViewTypeLibrary(
|
||||
self.as_ref().handle,
|
||||
name.as_ref().as_ptr() as *const core::ffi::c_char,
|
||||
)
|
||||
};
|
||||
core::ptr::NonNull::new(result).map(|h| unsafe { TypeLibrary::from_raw(h) })
|
||||
}
|
||||
|
||||
/// Should be called by custom py:py:class:`BinaryView` implementations
|
||||
/// when they have successfully imported an object from a type library (eg a symbol's type).
|
||||
/// Values recorded with this function will then be queryable via [BinaryViewExt::lookup_imported_object_library].
|
||||
///
|
||||
/// * `lib` - Type Library containing the imported type
|
||||
/// * `name` - Name of the object in the type library
|
||||
/// * `addr` - address of symbol at import site
|
||||
/// * `platform` - Platform of symbol at import site
|
||||
fn record_imported_object_library(
|
||||
&self,
|
||||
lib: &TypeLibrary,
|
||||
name: &QualifiedName,
|
||||
addr: u64,
|
||||
platform: &Platform,
|
||||
) {
|
||||
unsafe {
|
||||
BNBinaryViewRecordImportedObjectLibrary(
|
||||
self.as_ref().handle,
|
||||
platform.handle,
|
||||
addr,
|
||||
lib.as_raw(),
|
||||
&name.0 as *const _ as *mut _,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Recursively imports a type from the specified type library, or, if
|
||||
/// no library was explicitly provided, the first type library associated with the current [BinaryView]
|
||||
/// that provides the name requested.
|
||||
///
|
||||
/// This may have the impact of loading other type libraries as dependencies on other type libraries are lazily resolved
|
||||
/// when references to types provided by them are first encountered.
|
||||
///
|
||||
/// Note that the name actually inserted into the view may not match the name as it exists in the type library in
|
||||
/// the event of a name conflict. To aid in this, the [Type] object returned is a `NamedTypeReference` to
|
||||
/// the deconflicted name used.
|
||||
fn import_type_library(
|
||||
&self,
|
||||
name: &QualifiedName,
|
||||
mut lib: Option<TypeLibrary>,
|
||||
) -> Option<Ref<Type>> {
|
||||
let mut lib_ref = lib
|
||||
.as_mut()
|
||||
.map(|l| unsafe { l.as_raw() } as *mut _)
|
||||
.unwrap_or(ptr::null_mut());
|
||||
let result = unsafe {
|
||||
BNBinaryViewImportTypeLibraryType(
|
||||
self.as_ref().handle,
|
||||
&mut lib_ref,
|
||||
&name.0 as *const _ as *mut _,
|
||||
)
|
||||
};
|
||||
(!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) })
|
||||
}
|
||||
|
||||
/// Recursively imports an object from the specified type library, or, if
|
||||
/// no library was explicitly provided, the first type library associated with the current [BinaryView]
|
||||
/// that provides the name requested.
|
||||
///
|
||||
/// This may have the impact of loading other type libraries as dependencies on other type libraries are lazily resolved
|
||||
/// when references to types provided by them are first encountered.
|
||||
///
|
||||
/// .. note:: If you are implementing a custom BinaryView and use this method to import object types,
|
||||
/// you should then call [BinaryViewExt::record_imported_object_library] with the details of where the object is located.
|
||||
fn import_type_object(
|
||||
&self,
|
||||
name: &QualifiedName,
|
||||
mut lib: Option<TypeLibrary>,
|
||||
) -> Option<Ref<Type>> {
|
||||
let mut lib_ref = lib
|
||||
.as_mut()
|
||||
.map(|l| unsafe { l.as_raw() } as *mut _)
|
||||
.unwrap_or(ptr::null_mut());
|
||||
let result = unsafe {
|
||||
BNBinaryViewImportTypeLibraryObject(
|
||||
self.as_ref().handle,
|
||||
&mut lib_ref,
|
||||
&name.0 as *const _ as *mut _,
|
||||
)
|
||||
};
|
||||
(!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) })
|
||||
}
|
||||
|
||||
/// Recursively imports a type interface given its GUID.
|
||||
///
|
||||
/// .. note:: To support this type of lookup a type library must have
|
||||
/// contain a metadata key called "type_guids" which is a map
|
||||
/// Dict[string_guid, string_type_name] or
|
||||
/// Dict[string_guid, Tuple[string_type_name, type_library_name]]
|
||||
fn import_type_by_guid<S: BnStrCompatible>(&self, guid: S) -> Option<Ref<Type>> {
|
||||
let guid = guid.into_bytes_with_nul();
|
||||
let result = unsafe {
|
||||
BNBinaryViewImportTypeLibraryTypeByGuid(
|
||||
self.as_ref().handle,
|
||||
guid.as_ref().as_ptr() as *const c_char,
|
||||
)
|
||||
};
|
||||
(!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) })
|
||||
}
|
||||
|
||||
/// Recursively exports `type_obj` into `lib` as a type with name `name`
|
||||
///
|
||||
/// As other referenced types are encountered, they are either copied into the destination type library or
|
||||
/// else the type library that provided the referenced type is added as a dependency for the destination library.
|
||||
fn export_type_to_library(&self, lib: &TypeLibrary, name: &QualifiedName, type_obj: &Type) {
|
||||
unsafe {
|
||||
BNBinaryViewExportTypeToTypeLibrary(
|
||||
self.as_ref().handle,
|
||||
lib.as_raw(),
|
||||
&name.0 as *const _ as *mut _,
|
||||
type_obj.handle,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Recursively exports `type_obj` into `lib` as a type with name `name`
|
||||
///
|
||||
/// As other referenced types are encountered, they are either copied into the destination type library or
|
||||
/// else the type library that provided the referenced type is added as a dependency for the destination library.
|
||||
fn export_object_to_library(&self, lib: &TypeLibrary, name: &QualifiedName, type_obj: &Type) {
|
||||
unsafe {
|
||||
BNBinaryViewExportObjectToTypeLibrary(
|
||||
self.as_ref().handle,
|
||||
lib.as_raw(),
|
||||
&name.0 as *const _ as *mut _,
|
||||
type_obj.handle,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Gives you details of which type library and name was used to determine
|
||||
/// the type of a symbol at a given address
|
||||
///
|
||||
/// * `addr` - address of symbol at import site
|
||||
/// * `platform` - Platform of symbol at import site
|
||||
fn lookup_imported_object_library(
|
||||
&self,
|
||||
addr: u64,
|
||||
platform: &Platform,
|
||||
) -> Option<(TypeLibrary, QualifiedName)> {
|
||||
let mut result_lib = ptr::null_mut();
|
||||
let mut result_name = Default::default();
|
||||
let success = unsafe {
|
||||
BNBinaryViewLookupImportedObjectLibrary(
|
||||
self.as_ref().handle,
|
||||
platform.handle,
|
||||
addr,
|
||||
&mut result_lib,
|
||||
&mut result_name,
|
||||
)
|
||||
};
|
||||
if !success {
|
||||
return None;
|
||||
}
|
||||
let lib = unsafe { TypeLibrary::from_raw(ptr::NonNull::new(result_lib)?) };
|
||||
let name = QualifiedName(result_name);
|
||||
Some((lib, name))
|
||||
}
|
||||
|
||||
/// Gives you details of from which type library and name a given type in the analysis was imported.
|
||||
///
|
||||
/// * `name` - Name of type in analysis
|
||||
fn lookup_imported_type_library(
|
||||
&self,
|
||||
name: &QualifiedNameAndType,
|
||||
) -> Option<(TypeLibrary, QualifiedName)> {
|
||||
let mut result_lib = ptr::null_mut();
|
||||
let mut result_name = Default::default();
|
||||
let success = unsafe {
|
||||
BNBinaryViewLookupImportedTypeLibrary(
|
||||
self.as_ref().handle,
|
||||
&name.0 as *const _ as *mut _,
|
||||
&mut result_lib,
|
||||
&mut result_name,
|
||||
)
|
||||
};
|
||||
if !success {
|
||||
return None;
|
||||
}
|
||||
let lib = unsafe { TypeLibrary::from_raw(ptr::NonNull::new(result_lib)?) };
|
||||
let name = QualifiedName(result_name);
|
||||
Some((lib, name))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: BinaryViewBase> BinaryViewExt for T {}
|
||||
@@ -1545,7 +1807,7 @@ pub type BinaryViewEventType = BNBinaryViewEventType;
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// ```no_run
|
||||
/// use binaryninja::binaryview::{BinaryView, BinaryViewEventHandler, BinaryViewEventType, register_binary_view_event};
|
||||
///
|
||||
/// struct EventHandlerContext {
|
||||
@@ -1553,7 +1815,7 @@ pub type BinaryViewEventType = BNBinaryViewEventType;
|
||||
/// }
|
||||
///
|
||||
/// impl BinaryViewEventHandler for EventHandlerContext {
|
||||
/// fn on_event(&mut self, binary_view: &BinaryView) {
|
||||
/// fn on_event(&self, binary_view: &BinaryView) {
|
||||
/// // handle event
|
||||
/// }
|
||||
/// }
|
||||
@@ -1576,11 +1838,9 @@ where
|
||||
ctx: *mut ::std::os::raw::c_void,
|
||||
view: *mut BNBinaryView,
|
||||
) {
|
||||
ffi_wrap!("EventHandler::on_event", unsafe {
|
||||
let mut context = &mut *(ctx as *mut Handler);
|
||||
|
||||
let handle = BinaryView::from_raw(BNNewViewReference(view));
|
||||
Handler::on_event(&mut context, handle.as_ref());
|
||||
ffi_wrap!("EventHandler::on_event", {
|
||||
let context = unsafe { &*(ctx as *const Handler) };
|
||||
context.on_event(&BinaryView::from_raw(BNNewViewReference(view)));
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ use binaryninjacore_sys::*;
|
||||
|
||||
use crate::architecture::{Architecture, ArchitectureExt, CoreArchitecture, Register};
|
||||
use crate::rc::{
|
||||
CoreArrayProvider, CoreArrayWrapper, CoreOwnedArrayProvider, Guard, Ref, RefCountable,
|
||||
CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable,
|
||||
};
|
||||
use crate::string::*;
|
||||
|
||||
@@ -89,23 +89,15 @@ where
|
||||
*count = len;
|
||||
|
||||
if len == 0 {
|
||||
ptr::null_mut()
|
||||
} else {
|
||||
let mut res = Vec::with_capacity(len + 1);
|
||||
|
||||
res.push(len as u32);
|
||||
|
||||
for i in items {
|
||||
res.push(i);
|
||||
}
|
||||
|
||||
assert!(res.len() == len + 1);
|
||||
|
||||
let raw = res.as_mut_ptr();
|
||||
mem::forget(res);
|
||||
|
||||
unsafe { raw.offset(1) }
|
||||
return ptr::null_mut();
|
||||
}
|
||||
|
||||
let res: Box<[_]> = [len as u32].into_iter().chain(items).collect();
|
||||
debug_assert!(res.len() == len + 1);
|
||||
|
||||
// it's free on the function below: `cb_free_register_list`
|
||||
let raw = Box::leak(res);
|
||||
&mut raw[1]
|
||||
}
|
||||
|
||||
extern "C" fn cb_free_register_list(_ctxt: *mut c_void, regs: *mut u32) {
|
||||
@@ -115,8 +107,8 @@ where
|
||||
}
|
||||
|
||||
let actual_start = regs.offset(-1);
|
||||
let len = *actual_start + 1;
|
||||
let _regs = Vec::from_raw_parts(actual_start, len as usize, len as usize);
|
||||
let len = (*actual_start) + 1;
|
||||
let _regs = Box::from_raw(ptr::slice_from_raw_parts_mut(actual_start, len as usize));
|
||||
})
|
||||
}
|
||||
|
||||
@@ -136,7 +128,7 @@ where
|
||||
where
|
||||
C: CallingConventionBase,
|
||||
{
|
||||
ffi_wrap!("CallingConvention::_callee_saved_registers", unsafe {
|
||||
ffi_wrap!("CallingConvention::callee_saved_registers", unsafe {
|
||||
let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
|
||||
let regs = ctxt.cc.callee_saved_registers();
|
||||
|
||||
@@ -448,24 +440,21 @@ impl<A: Architecture> CallingConvention<A> {
|
||||
unsafe { BnString::from_raw(BNGetCallingConventionName(self.handle)) }
|
||||
}
|
||||
|
||||
pub fn variables_for_parameters<S: Clone + BnStrCompatible>(
|
||||
pub fn variables_for_parameters(
|
||||
&self,
|
||||
params: &[FunctionParameter<S>],
|
||||
params: &[FunctionParameter],
|
||||
permitted_registers: Option<&[A::Register]>,
|
||||
) -> Vec<Variable> {
|
||||
let mut bn_params: Vec<BNFunctionParameter> = vec![];
|
||||
let mut name_strings = vec![];
|
||||
let name_strings = params.iter().map(|parameter| ¶meter.name);
|
||||
|
||||
for parameter in params.iter() {
|
||||
name_strings.push(parameter.name.clone().into_bytes_with_nul());
|
||||
}
|
||||
for (parameter, raw_name) in params.iter().zip(name_strings.iter_mut()) {
|
||||
for (parameter, raw_name) in params.iter().zip(name_strings) {
|
||||
let location = match ¶meter.location {
|
||||
Some(location) => location.raw(),
|
||||
None => unsafe { mem::zeroed() },
|
||||
};
|
||||
bn_params.push(BNFunctionParameter {
|
||||
name: raw_name.as_ref().as_ptr() as *mut _,
|
||||
name: BnString::new(raw_name).into_raw(),
|
||||
type_: parameter.t.contents.handle,
|
||||
typeConfidence: parameter.t.confidence,
|
||||
defaultLocation: parameter.location.is_none(),
|
||||
@@ -501,9 +490,6 @@ impl<A: Architecture> CallingConvention<A> {
|
||||
}
|
||||
};
|
||||
|
||||
// Gotta keep this around so the pointers are valid during the call
|
||||
drop(name_strings);
|
||||
|
||||
let vars_slice = unsafe { slice::from_raw_parts(vars, count) };
|
||||
let mut result = vec![];
|
||||
for var in vars_slice {
|
||||
@@ -575,11 +561,43 @@ impl<A: Architecture> CallingConventionBase for CallingConvention<A> {
|
||||
}
|
||||
|
||||
fn int_arg_registers(&self) -> Vec<A::Register> {
|
||||
Vec::new()
|
||||
unsafe {
|
||||
let mut count = 0;
|
||||
let regs = BNGetIntegerArgumentRegisters(self.handle, &mut count);
|
||||
let arch = self.arch_handle.borrow();
|
||||
|
||||
let res = slice::from_raw_parts(regs, count)
|
||||
.iter()
|
||||
.map(|&r| {
|
||||
arch.register_from_id(r)
|
||||
.expect("bad reg id from CallingConvention")
|
||||
})
|
||||
.collect();
|
||||
|
||||
BNFreeRegisterList(regs);
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
fn float_arg_registers(&self) -> Vec<A::Register> {
|
||||
Vec::new()
|
||||
unsafe {
|
||||
let mut count = 0;
|
||||
let regs = BNGetFloatArgumentRegisters(self.handle, &mut count);
|
||||
let arch = self.arch_handle.borrow();
|
||||
|
||||
let res = slice::from_raw_parts(regs, count)
|
||||
.iter()
|
||||
.map(|&r| {
|
||||
arch.register_from_id(r)
|
||||
.expect("bad reg id from CallingConvention")
|
||||
})
|
||||
.collect();
|
||||
|
||||
BNFreeRegisterList(regs);
|
||||
|
||||
res
|
||||
}
|
||||
}
|
||||
|
||||
fn arg_registers_shared_index(&self) -> bool {
|
||||
@@ -660,18 +678,14 @@ unsafe impl<A: Architecture> RefCountable for CallingConvention<A> {
|
||||
impl<A: Architecture> CoreArrayProvider for CallingConvention<A> {
|
||||
type Raw = *mut BNCallingConvention;
|
||||
type Context = A::Handle;
|
||||
type Wrapped<'a> = Guard<'a, CallingConvention<A>>;
|
||||
}
|
||||
|
||||
unsafe impl<A: Architecture> CoreOwnedArrayProvider for CallingConvention<A> {
|
||||
unsafe impl<A: Architecture> CoreArrayProviderInner for CallingConvention<A> {
|
||||
unsafe fn free(raw: *mut *mut BNCallingConvention, count: usize, _content: &Self::Context) {
|
||||
BNFreeCallingConventionList(raw, count);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a, A: Architecture> CoreArrayWrapper<'a> for CallingConvention<A> {
|
||||
type Wrapped = Guard<'a, CallingConvention<A>>;
|
||||
|
||||
unsafe fn wrap_raw(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped {
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
Guard::new(
|
||||
CallingConvention {
|
||||
handle: *raw,
|
||||
@@ -691,7 +705,7 @@ impl Debug for CallingConvention<CoreArchitecture> {
|
||||
|
||||
pub struct ConventionBuilder<A: Architecture> {
|
||||
caller_saved_registers: Vec<A::Register>,
|
||||
_callee_saved_registers: Vec<A::Register>,
|
||||
callee_saved_registers: Vec<A::Register>,
|
||||
int_arg_registers: Vec<A::Register>,
|
||||
float_arg_registers: Vec<A::Register>,
|
||||
|
||||
@@ -760,7 +774,7 @@ impl<A: Architecture> ConventionBuilder<A> {
|
||||
pub fn new(arch: &A) -> Self {
|
||||
Self {
|
||||
caller_saved_registers: Vec::new(),
|
||||
_callee_saved_registers: Vec::new(),
|
||||
callee_saved_registers: Vec::new(),
|
||||
int_arg_registers: Vec::new(),
|
||||
float_arg_registers: Vec::new(),
|
||||
|
||||
@@ -785,7 +799,7 @@ impl<A: Architecture> ConventionBuilder<A> {
|
||||
}
|
||||
|
||||
reg_list!(caller_saved_registers);
|
||||
reg_list!(_callee_saved_registers);
|
||||
reg_list!(callee_saved_registers);
|
||||
reg_list!(int_arg_registers);
|
||||
reg_list!(float_arg_registers);
|
||||
|
||||
@@ -819,7 +833,7 @@ impl<A: Architecture> CallingConventionBase for ConventionBuilder<A> {
|
||||
}
|
||||
|
||||
fn callee_saved_registers(&self) -> Vec<A::Register> {
|
||||
self.caller_saved_registers.clone()
|
||||
self.callee_saved_registers.clone()
|
||||
}
|
||||
|
||||
fn int_arg_registers(&self) -> Vec<A::Register> {
|
||||
|
||||
@@ -16,12 +16,16 @@
|
||||
//!
|
||||
//! All plugins need to provide one of the following functions for Binary Ninja to call:
|
||||
//!
|
||||
//! ```rust
|
||||
//! pub extern "C" fn CorePluginInit() -> bool {}
|
||||
//! ```no_run
|
||||
//! pub extern "C" fn CorePluginInit() -> bool {
|
||||
//! todo!();
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ```rust
|
||||
//! pub extern "C" fn UIPluginInit() -> bool {}
|
||||
//! ```no_run
|
||||
//! pub extern "C" fn UIPluginInit() -> bool {
|
||||
//! todo!();
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! Both of these functions can call any of the following registration functions, though `CorePluginInit` is called during Binary Ninja core initialization, and `UIPluginInit` is called during Binary Ninja UI initialization.
|
||||
@@ -62,7 +66,9 @@ where
|
||||
/// The function call required for generic commands; commands added in this way will be in the `Plugins` submenu of the menu bar.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// ```no_run
|
||||
/// # use binaryninja::command::Command;
|
||||
/// # use binaryninja::binaryview::BinaryView;
|
||||
/// struct MyCommand;
|
||||
///
|
||||
/// impl Command for MyCommand {
|
||||
@@ -76,6 +82,7 @@ where
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// # use binaryninja::command::register;
|
||||
/// #[no_mangle]
|
||||
/// pub extern "C" fn CorePluginInit() -> bool {
|
||||
/// register(
|
||||
@@ -160,7 +167,9 @@ where
|
||||
/// The function call required for generic commands; commands added in this way will be in the `Plugins` submenu of the menu bar.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// ```no_run
|
||||
/// # use binaryninja::command::AddressCommand;
|
||||
/// # use binaryninja::binaryview::BinaryView;
|
||||
/// struct MyCommand;
|
||||
///
|
||||
/// impl AddressCommand for MyCommand {
|
||||
@@ -174,6 +183,7 @@ where
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// # use binaryninja::command::register_for_address;
|
||||
/// #[no_mangle]
|
||||
/// pub extern "C" fn CorePluginInit() -> bool {
|
||||
/// register_for_address(
|
||||
@@ -258,10 +268,13 @@ where
|
||||
/// The function call required for generic commands; commands added in this way will be in the `Plugins` submenu of the menu bar.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// ```no_run
|
||||
/// # use std::ops::Range;
|
||||
/// # use binaryninja::command::RangeCommand;
|
||||
/// # use binaryninja::binaryview::BinaryView;
|
||||
/// struct MyCommand;
|
||||
///
|
||||
/// impl AddressCommand for MyCommand {
|
||||
/// impl RangeCommand for MyCommand {
|
||||
/// fn action(&self, view: &BinaryView, range: Range<u64>) {
|
||||
/// // Your code here
|
||||
/// }
|
||||
@@ -272,6 +285,7 @@ where
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// # use binaryninja::command::register_for_range;
|
||||
/// #[no_mangle]
|
||||
/// pub extern "C" fn CorePluginInit() -> bool {
|
||||
/// register_for_range(
|
||||
@@ -361,10 +375,14 @@ where
|
||||
/// The function call required for generic commands; commands added in this way will be in the `Plugins` submenu of the menu bar.
|
||||
///
|
||||
/// # Example
|
||||
/// ```rust
|
||||
/// ```no_run
|
||||
/// # use binaryninja::command::FunctionCommand;
|
||||
/// # use binaryninja::binaryview::BinaryView;
|
||||
/// # use binaryninja::function::Function;
|
||||
/// # use binaryninja::command::register_for_function;
|
||||
/// struct MyCommand;
|
||||
///
|
||||
/// impl AddressCommand for MyCommand {
|
||||
/// impl FunctionCommand for MyCommand {
|
||||
/// fn action(&self, view: &BinaryView, func: &Function) {
|
||||
/// // Your code here
|
||||
/// }
|
||||
|
||||
295
src/component.rs
Normal file
295
src/component.rs
Normal file
@@ -0,0 +1,295 @@
|
||||
use core::{ffi, mem, ptr};
|
||||
|
||||
use crate::binaryview::{BinaryView, BinaryViewBase, BinaryViewExt};
|
||||
use crate::function::Function;
|
||||
use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref};
|
||||
use crate::string::{BnStrCompatible, BnString};
|
||||
use crate::types::{ComponentReferencedTypes, DataVariable};
|
||||
|
||||
use binaryninjacore_sys::*;
|
||||
|
||||
pub struct ComponentBuilder {
|
||||
bv: *mut BNBinaryView,
|
||||
parent: Option<BnString>,
|
||||
name: Option<BnString>,
|
||||
}
|
||||
|
||||
impl ComponentBuilder {
|
||||
pub(crate) fn new_from_raw(bv: *mut BNBinaryView) -> Self {
|
||||
Self {
|
||||
bv,
|
||||
parent: None,
|
||||
name: None,
|
||||
}
|
||||
}
|
||||
pub fn new<I: BinaryViewBase>(bv: &I) -> Self {
|
||||
Self {
|
||||
bv: bv.as_ref().handle,
|
||||
parent: None,
|
||||
name: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parent<G: IntoComponentGuid>(mut self, parent: G) -> Self {
|
||||
self.parent = Some(parent.component_guid());
|
||||
self
|
||||
}
|
||||
|
||||
pub fn name<S: BnStrCompatible>(mut self, name: S) -> Self {
|
||||
self.name = Some(BnString::new(name));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn finalize(self) -> Component {
|
||||
let result = match (&self.parent, &self.name) {
|
||||
(None, None) => unsafe { BNCreateComponent(self.bv) },
|
||||
(None, Some(name)) => unsafe { BNCreateComponentWithName(self.bv, name.as_ptr()) },
|
||||
(Some(guid), None) => unsafe { BNCreateComponentWithParent(self.bv, guid.as_ptr()) },
|
||||
(Some(guid), Some(name)) => unsafe {
|
||||
BNCreateComponentWithParentAndName(self.bv, guid.as_ptr(), name.as_ptr())
|
||||
},
|
||||
};
|
||||
unsafe { Component::from_raw(ptr::NonNull::new(result).unwrap()) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Components are objects that can contain Functions, Data Variables, and other Components.
|
||||
///
|
||||
/// They can be queried for information about the items contained within them.
|
||||
///
|
||||
/// Components have a Guid, which persistent across saves and loads of the database, and should be
|
||||
/// used for retrieving components when such is required and a reference to the Component cannot be held.
|
||||
#[repr(transparent)]
|
||||
pub struct Component {
|
||||
handle: ptr::NonNull<BNComponent>,
|
||||
}
|
||||
|
||||
impl Component {
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
pub(crate) unsafe fn as_raw(&self) -> &mut BNComponent {
|
||||
&mut *self.handle.as_ptr()
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNComponent>) -> Self {
|
||||
Self { handle }
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn ref_from_raw(handle: &*mut BNComponent) -> &Self {
|
||||
assert!(!handle.is_null());
|
||||
mem::transmute(handle)
|
||||
}
|
||||
|
||||
pub fn guid(&self) -> BnString {
|
||||
let result = unsafe { BNComponentGetGuid(self.as_raw()) };
|
||||
assert!(!result.is_null());
|
||||
unsafe { BnString::from_raw(result) }
|
||||
}
|
||||
|
||||
/// Add function to this component.
|
||||
pub fn add_function(&self, func: &Function) -> bool {
|
||||
unsafe { BNComponentAddFunctionReference(self.as_raw(), func.handle) }
|
||||
}
|
||||
|
||||
/// Check whether this component contains a function.
|
||||
pub fn contains_function(&self, func: &Function) -> bool {
|
||||
unsafe { BNComponentContainsFunction(self.as_raw(), func.handle) }
|
||||
}
|
||||
|
||||
/// Remove function from this component.
|
||||
pub fn remove_function(&self, func: &Function) -> bool {
|
||||
unsafe { BNComponentRemoveFunctionReference(self.as_raw(), func.handle) }
|
||||
}
|
||||
|
||||
/// Move component to this component. This will remove it from the old parent.
|
||||
pub fn add_component(&self, component: &Component) -> bool {
|
||||
unsafe { BNComponentAddComponent(self.as_raw(), component.as_raw()) }
|
||||
}
|
||||
|
||||
/// Check whether this component contains a component.
|
||||
pub fn contains_component(&self, component: &Component) -> bool {
|
||||
unsafe { BNComponentContainsComponent(self.as_raw(), component.as_raw()) }
|
||||
}
|
||||
|
||||
/// Remove a component from the current component, moving it to the root.
|
||||
///
|
||||
/// This function has no effect when used from the root component.
|
||||
/// Use `BinaryView.remove_component` to Remove a component from the tree entirely.
|
||||
pub fn remove_component(&self, component: &Component) -> bool {
|
||||
self.view()
|
||||
.unwrap()
|
||||
.root_component()
|
||||
.unwrap()
|
||||
.add_component(component)
|
||||
}
|
||||
|
||||
/// Add data variable to this component.
|
||||
pub fn add_data_variable(&self, data_variable: &DataVariable) -> bool {
|
||||
unsafe { BNComponentAddDataVariable(self.as_raw(), data_variable.address()) }
|
||||
}
|
||||
|
||||
/// Check whether this component contains a data variable.
|
||||
pub fn contains_data_variable(&self, data_variable: &DataVariable) -> bool {
|
||||
unsafe { BNComponentContainsDataVariable(self.as_raw(), data_variable.address()) }
|
||||
}
|
||||
|
||||
/// Remove data variable from this component.
|
||||
pub fn remove_data_variable(&self, data_variable: &DataVariable) -> bool {
|
||||
unsafe { BNComponentRemoveDataVariable(self.as_raw(), data_variable.address()) }
|
||||
}
|
||||
|
||||
/// Original name of the component
|
||||
pub fn display_name(&self) -> BnString {
|
||||
let result = unsafe { BNComponentGetDisplayName(self.as_raw()) };
|
||||
assert!(!result.is_null());
|
||||
unsafe { BnString::from_raw(result) }
|
||||
}
|
||||
|
||||
/// Original name set for this component
|
||||
|
||||
/// :note: The `.display_name` property should be used for `bv.get_component_by_path()` lookups.
|
||||
|
||||
/// This can differ from the .display_name property if one of its sibling components has the same .original_name; In that
|
||||
/// case, .name will be an automatically generated unique name (e.g. "MyComponentName (1)") while .original_name will
|
||||
/// remain what was originally set (e.g. "MyComponentName")
|
||||
|
||||
/// If this component has a duplicate name and is moved to a component where none of its siblings share its name,
|
||||
/// .name will return the original "MyComponentName"
|
||||
pub fn name(&self) -> BnString {
|
||||
let result = unsafe { BNComponentGetOriginalName(self.as_raw()) };
|
||||
assert!(!result.is_null());
|
||||
unsafe { BnString::from_raw(result) }
|
||||
}
|
||||
|
||||
pub fn set_name<S: BnStrCompatible>(&self, name: S) {
|
||||
let name = name.into_bytes_with_nul();
|
||||
unsafe { BNComponentSetName(self.as_raw(), name.as_ref().as_ptr() as *const ffi::c_char) }
|
||||
}
|
||||
|
||||
/// The component that contains this component, if it exists.
|
||||
pub fn parent(&self) -> Option<Component> {
|
||||
let result = unsafe { BNComponentGetParent(self.as_raw()) };
|
||||
ptr::NonNull::new(result).map(|h| unsafe { Self::from_raw(h) })
|
||||
}
|
||||
|
||||
pub fn view(&self) -> Option<Ref<BinaryView>> {
|
||||
let result = unsafe { BNComponentGetView(self.as_raw()) };
|
||||
(!result.is_null()).then(|| unsafe { BinaryView::from_raw(result) })
|
||||
}
|
||||
|
||||
/// Is an iterator for all Components contained within this Component
|
||||
pub fn components(&self) -> Array<Component> {
|
||||
let mut count = 0;
|
||||
let result = unsafe { BNComponentGetContainedComponents(self.as_raw(), &mut count) };
|
||||
assert!(!result.is_null());
|
||||
unsafe { Array::new(result, count, ()) }
|
||||
}
|
||||
|
||||
/// List of all Functions contained within this Component
|
||||
pub fn functions(&self) -> Array<Function> {
|
||||
let mut count = 0;
|
||||
let result = unsafe { BNComponentGetContainedFunctions(self.as_raw(), &mut count) };
|
||||
assert!(!result.is_null());
|
||||
unsafe { Array::new(result, count, ()) }
|
||||
}
|
||||
|
||||
/// List of all Data Variables contained within this Component
|
||||
pub fn data_variables(&self) -> Array<DataVariable> {
|
||||
let mut count = 0;
|
||||
let result = unsafe { BNComponentGetContainedDataVariables(self.as_raw(), &mut count) };
|
||||
assert!(!result.is_null());
|
||||
unsafe { Array::new(result, count, ()) }
|
||||
}
|
||||
|
||||
/// Get data variables referenced by this component
|
||||
///
|
||||
/// * `recursive` - Get all DataVariables referenced by this component and subcomponents.
|
||||
pub fn get_referenced_data_variables(&self, recursive: bool) -> Array<DataVariable> {
|
||||
let mut count = 0;
|
||||
let result = if recursive {
|
||||
unsafe { BNComponentGetReferencedDataVariablesRecursive(self.as_raw(), &mut count) }
|
||||
} else {
|
||||
unsafe { BNComponentGetReferencedDataVariables(self.as_raw(), &mut count) }
|
||||
};
|
||||
unsafe { Array::new(result, count, ()) }
|
||||
}
|
||||
|
||||
/// Get Types referenced by this component
|
||||
///
|
||||
/// * `recursive` - Get all Types referenced by this component and subcomponents.
|
||||
pub fn get_referenced_types(&self, recursive: bool) -> Array<ComponentReferencedTypes> {
|
||||
let mut count = 0;
|
||||
let result = if recursive {
|
||||
unsafe { BNComponentGetReferencedTypesRecursive(self.as_raw(), &mut count) }
|
||||
} else {
|
||||
unsafe { BNComponentGetReferencedTypes(self.as_raw(), &mut count) }
|
||||
};
|
||||
unsafe { Array::new(result, count, ()) }
|
||||
}
|
||||
|
||||
pub fn remove_all_functions(&self) {
|
||||
unsafe { BNComponentRemoveAllFunctions(self.as_raw()) }
|
||||
}
|
||||
|
||||
pub fn add_all_members_from(&self, component: &Component) {
|
||||
unsafe { BNComponentAddAllMembersFromComponent(self.as_raw(), component.as_raw()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for Component {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
unsafe { BNComponentsEqual(self.as_raw(), other.as_raw()) }
|
||||
}
|
||||
|
||||
#[allow(clippy::partialeq_ne_impl)]
|
||||
fn ne(&self, other: &Self) -> bool {
|
||||
unsafe { BNComponentsNotEqual(self.as_raw(), other.as_raw()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Component {}
|
||||
|
||||
impl Drop for Component {
|
||||
fn drop(&mut self) {
|
||||
unsafe { BNFreeComponent(self.as_raw()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Component {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
Self::from_raw(ptr::NonNull::new(BNNewComponentReference(self.as_raw())).unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for Component {
|
||||
type Raw = *mut BNComponent;
|
||||
type Context = ();
|
||||
type Wrapped<'a> = &'a Self;
|
||||
}
|
||||
|
||||
unsafe impl CoreArrayProviderInner for Component {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeComponents(raw, count)
|
||||
}
|
||||
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
Self::ref_from_raw(raw)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IntoComponentGuid {
|
||||
fn component_guid(self) -> BnString;
|
||||
}
|
||||
|
||||
impl IntoComponentGuid for &Component {
|
||||
fn component_guid(self) -> BnString {
|
||||
self.guid()
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BnStrCompatible> IntoComponentGuid for S {
|
||||
fn component_guid(self) -> BnString {
|
||||
BnString::new(self)
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,7 @@ pub use binaryninjacore_sys::BNModificationStatus as ModificationStatus;
|
||||
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
@@ -122,11 +123,10 @@ where
|
||||
let long_name = long_name.into_bytes_with_nul();
|
||||
let long_name_ptr = long_name.as_ref().as_ptr() as *mut _;
|
||||
|
||||
let ctxt = Box::new(unsafe { mem::zeroed() });
|
||||
let ctxt = Box::into_raw(ctxt);
|
||||
let ctxt = Box::leak(Box::new(MaybeUninit::zeroed()));
|
||||
|
||||
let mut bn_obj = BNCustomBinaryViewType {
|
||||
context: ctxt as *mut _,
|
||||
context: ctxt.as_mut_ptr() as *mut _,
|
||||
create: Some(cb_create::<T>),
|
||||
parse: Some(cb_parse::<T>),
|
||||
isValidForData: Some(cb_valid::<T>),
|
||||
@@ -140,15 +140,16 @@ where
|
||||
if res.is_null() {
|
||||
// avoid leaking the space allocated for the type, but also
|
||||
// avoid running its Drop impl (if any -- not that there should
|
||||
// be one since view types live for the life of the process)
|
||||
mem::forget(*Box::from_raw(ctxt));
|
||||
// be one since view types live for the life of the process) as
|
||||
// MaybeUninit suppress the Drop implementation of it's inner type
|
||||
drop(Box::from_raw(ctxt));
|
||||
|
||||
panic!("bvt registration failed");
|
||||
}
|
||||
|
||||
ptr::write(ctxt, constructor(BinaryViewType(res)));
|
||||
ctxt.write(constructor(BinaryViewType(res)));
|
||||
|
||||
&*ctxt
|
||||
ctxt.assume_init_mut()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,19 +290,15 @@ impl BinaryViewTypeBase for BinaryViewType {
|
||||
impl CoreArrayProvider for BinaryViewType {
|
||||
type Raw = *mut BNBinaryViewType;
|
||||
type Context = ();
|
||||
type Wrapped<'a> = Guard<'a, BinaryViewType>;
|
||||
}
|
||||
|
||||
unsafe impl CoreOwnedArrayProvider for BinaryViewType {
|
||||
unsafe impl CoreArrayProviderInner for BinaryViewType {
|
||||
unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
|
||||
BNFreeBinaryViewTypeList(raw);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> CoreArrayWrapper<'a> for BinaryViewType {
|
||||
type Wrapped = BinaryViewType;
|
||||
|
||||
unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
|
||||
BinaryViewType(*raw)
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
Guard::new(BinaryViewType(*raw), &())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -388,7 +385,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
|
||||
|
||||
let view_name = view_type.name();
|
||||
|
||||
if let Ok(bv) = file.get_view_of_type(view_name.as_cstr()) {
|
||||
if let Ok(bv) = file.get_view_of_type(view_name.as_str()) {
|
||||
// while it seems to work most of the time, you can get really unlucky
|
||||
// if the a free of the existing view of the same type kicks off while
|
||||
// BNCreateBinaryViewOfType is still running. the freeObject callback
|
||||
@@ -772,7 +769,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
|
||||
|
||||
unsafe {
|
||||
let res = BNCreateCustomBinaryView(
|
||||
view_name.as_cstr().as_ptr(),
|
||||
view_name.as_ptr(),
|
||||
file.handle,
|
||||
parent.handle,
|
||||
&mut bn_obj,
|
||||
|
||||
654
src/database.rs
Normal file
654
src/database.rs
Normal file
@@ -0,0 +1,654 @@
|
||||
use std::collections::HashMap;
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
use std::{ffi, mem, ptr};
|
||||
|
||||
use binaryninjacore_sys::*;
|
||||
|
||||
use crate::binaryview::BinaryView;
|
||||
use crate::databuffer::DataBuffer;
|
||||
use crate::disassembly::InstructionTextToken;
|
||||
use crate::filemetadata::FileMetadata;
|
||||
use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref};
|
||||
use crate::string::{BnStrCompatible, BnString};
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct Database {
|
||||
handle: ptr::NonNull<BNDatabase>,
|
||||
}
|
||||
|
||||
impl Database {
|
||||
pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNDatabase>) -> Self {
|
||||
Self { handle }
|
||||
}
|
||||
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
pub(crate) unsafe fn as_raw(&self) -> &mut BNDatabase {
|
||||
&mut *self.handle.as_ptr()
|
||||
}
|
||||
|
||||
/// Get a snapshot by its id, or None if no snapshot with that id exists
|
||||
pub fn snapshot(&self, id: i64) -> Option<Snapshot> {
|
||||
let result = unsafe { BNGetDatabaseSnapshot(self.as_raw(), id) };
|
||||
ptr::NonNull::new(result).map(|handle| unsafe { Snapshot::from_raw(handle) })
|
||||
}
|
||||
|
||||
/// Get a list of all snapshots in the database
|
||||
pub fn snapshots(&self) -> Array<Snapshot> {
|
||||
let mut count = 0;
|
||||
let result = unsafe { BNGetDatabaseSnapshots(self.as_raw(), &mut count) };
|
||||
assert!(!result.is_null());
|
||||
unsafe { Array::new(result, count, ()) }
|
||||
}
|
||||
|
||||
/// Get the current snapshot
|
||||
pub fn current_snapshot(&self) -> Option<Snapshot> {
|
||||
let result = unsafe { BNGetDatabaseCurrentSnapshot(self.as_raw()) };
|
||||
ptr::NonNull::new(result).map(|handle| unsafe { Snapshot::from_raw(handle) })
|
||||
}
|
||||
|
||||
pub fn set_current_snapshot(&self, value: &Snapshot) {
|
||||
unsafe { BNSetDatabaseCurrentSnapshot(self.as_raw(), value.id()) }
|
||||
}
|
||||
|
||||
pub fn write_snapshot_data<N: BnStrCompatible>(
|
||||
&self,
|
||||
parents: &[i64],
|
||||
file: &BinaryView,
|
||||
name: N,
|
||||
data: &KeyValueStore,
|
||||
auto_save: bool,
|
||||
) -> i64 {
|
||||
let name_raw = name.into_bytes_with_nul();
|
||||
let name_ptr = name_raw.as_ref().as_ptr() as *const ffi::c_char;
|
||||
unsafe {
|
||||
BNWriteDatabaseSnapshotData(
|
||||
self.as_raw(),
|
||||
parents.as_ptr() as *mut _,
|
||||
parents.len(),
|
||||
file.handle,
|
||||
name_ptr,
|
||||
data.as_raw(),
|
||||
auto_save,
|
||||
ptr::null_mut(),
|
||||
Some(cb_progress_nop),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_snapshot_data_with_progress<N, F>(
|
||||
&self,
|
||||
parents: &[i64],
|
||||
file: &BinaryView,
|
||||
name: N,
|
||||
data: &KeyValueStore,
|
||||
auto_save: bool,
|
||||
mut progress: F,
|
||||
) -> i64
|
||||
where
|
||||
N: BnStrCompatible,
|
||||
F: FnMut(usize, usize) -> bool,
|
||||
{
|
||||
let name_raw = name.into_bytes_with_nul();
|
||||
let name_ptr = name_raw.as_ref().as_ptr() as *const ffi::c_char;
|
||||
let ctxt = &mut progress as *mut _ as *mut ffi::c_void;
|
||||
unsafe {
|
||||
BNWriteDatabaseSnapshotData(
|
||||
self.as_raw(),
|
||||
parents.as_ptr() as *mut _,
|
||||
parents.len(),
|
||||
file.handle,
|
||||
name_ptr,
|
||||
data.as_raw(),
|
||||
auto_save,
|
||||
ctxt,
|
||||
Some(cb_progress::<F>),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Trim a snapshot's contents in the database by id, but leave the parent/child
|
||||
/// hierarchy intact. Future references to this snapshot will return False for has_contents
|
||||
pub fn trim_snapshot(&self, id: i64) -> Result<(), ()> {
|
||||
if unsafe { BNTrimDatabaseSnapshot(self.as_raw(), id) } {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Remove a snapshot in the database by id, deleting its contents and references.
|
||||
/// Attempting to remove a snapshot with children will raise an exception.
|
||||
pub fn remove_snapshot(&self, id: i64) -> Result<(), ()> {
|
||||
if unsafe { BNRemoveDatabaseSnapshot(self.as_raw(), id) } {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
pub fn has_global<S: BnStrCompatible>(&self, key: S) -> bool {
|
||||
let key_raw = key.into_bytes_with_nul();
|
||||
let key_ptr = key_raw.as_ref().as_ptr() as *const ffi::c_char;
|
||||
unsafe { BNDatabaseHasGlobal(self.as_raw(), key_ptr) != 0 }
|
||||
}
|
||||
|
||||
/// Get a list of keys for all globals in the database
|
||||
pub fn global_keys(&self) -> Array<BnString> {
|
||||
let mut count = 0;
|
||||
let result = unsafe { BNGetDatabaseGlobalKeys(self.as_raw(), &mut count) };
|
||||
assert!(!result.is_null());
|
||||
unsafe { Array::new(result, count, ()) }
|
||||
}
|
||||
|
||||
/// Get a dictionary of all globals
|
||||
pub fn globals(&self) -> HashMap<String, String> {
|
||||
self.global_keys()
|
||||
.iter()
|
||||
.filter_map(|key| Some((key.to_string(), self.read_global(key)?.to_string())))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Get a specific global by key
|
||||
pub fn read_global<S: BnStrCompatible>(&self, key: S) -> Option<BnString> {
|
||||
let key_raw = key.into_bytes_with_nul();
|
||||
let key_ptr = key_raw.as_ref().as_ptr() as *const ffi::c_char;
|
||||
let result = unsafe { BNReadDatabaseGlobal(self.as_raw(), key_ptr) };
|
||||
unsafe { ptr::NonNull::new(result).map(|_| BnString::from_raw(result)) }
|
||||
}
|
||||
|
||||
/// Write a global into the database
|
||||
pub fn write_global<K: BnStrCompatible, V: BnStrCompatible>(&self, key: K, value: V) -> bool {
|
||||
let key_raw = key.into_bytes_with_nul();
|
||||
let key_ptr = key_raw.as_ref().as_ptr() as *const ffi::c_char;
|
||||
let value_raw = value.into_bytes_with_nul();
|
||||
let value_ptr = value_raw.as_ref().as_ptr() as *const ffi::c_char;
|
||||
unsafe { BNWriteDatabaseGlobal(self.as_raw(), key_ptr, value_ptr) }
|
||||
}
|
||||
|
||||
/// Get a specific global by key, as a binary buffer
|
||||
pub fn read_global_data<S: BnStrCompatible>(&self, key: S) -> Option<DataBuffer> {
|
||||
let key_raw = key.into_bytes_with_nul();
|
||||
let key_ptr = key_raw.as_ref().as_ptr() as *const ffi::c_char;
|
||||
let result = unsafe { BNReadDatabaseGlobalData(self.as_raw(), key_ptr) };
|
||||
ptr::NonNull::new(result).map(|_| DataBuffer::from_raw(result))
|
||||
}
|
||||
|
||||
/// Write a binary buffer into a global in the database
|
||||
pub fn write_global_data<K: BnStrCompatible>(&self, key: K, value: &DataBuffer) -> bool {
|
||||
let key_raw = key.into_bytes_with_nul();
|
||||
let key_ptr = key_raw.as_ref().as_ptr() as *const ffi::c_char;
|
||||
unsafe { BNWriteDatabaseGlobalData(self.as_raw(), key_ptr, value.as_raw()) }
|
||||
}
|
||||
|
||||
/// Get the owning FileMetadata
|
||||
pub fn file(&self) -> Ref<FileMetadata> {
|
||||
let result = unsafe { BNGetDatabaseFile(self.as_raw()) };
|
||||
assert!(!result.is_null());
|
||||
unsafe { Ref::new(FileMetadata::from_raw(result)) }
|
||||
}
|
||||
|
||||
/// Get the backing analysis cache kvs
|
||||
pub fn analysis_cache(&self) -> KeyValueStore {
|
||||
let result = unsafe { BNReadDatabaseAnalysisCache(self.as_raw()) };
|
||||
unsafe { KeyValueStore::from_raw(ptr::NonNull::new(result).unwrap()) }
|
||||
}
|
||||
|
||||
pub fn reload_connection(&self) {
|
||||
unsafe { BNDatabaseReloadConnection(self.as_raw()) }
|
||||
}
|
||||
|
||||
pub fn write_analysis_cache(&self, val: &KeyValueStore) -> Result<(), ()> {
|
||||
if unsafe { BNWriteDatabaseAnalysisCache(self.as_raw(), val.as_raw()) } {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn snapshot_has_data(&self, id: i64) -> bool {
|
||||
unsafe { BNSnapshotHasData(self.as_raw(), id) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Database {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe { Self::from_raw(ptr::NonNull::new(BNNewDatabaseReference(self.as_raw())).unwrap()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Database {
|
||||
fn drop(&mut self) {
|
||||
unsafe { BNFreeDatabase(self.as_raw()) }
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct Snapshot {
|
||||
handle: ptr::NonNull<BNSnapshot>,
|
||||
}
|
||||
|
||||
impl Snapshot {
|
||||
pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNSnapshot>) -> Self {
|
||||
Self { handle }
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn ref_from_raw(handle: &*mut BNSnapshot) -> &Self {
|
||||
mem::transmute(handle)
|
||||
}
|
||||
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
pub(crate) unsafe fn as_raw(&self) -> &mut BNSnapshot {
|
||||
&mut *self.handle.as_ptr()
|
||||
}
|
||||
|
||||
/// Get the owning database
|
||||
pub fn database(&self) -> Database {
|
||||
unsafe {
|
||||
Database::from_raw(ptr::NonNull::new(BNGetSnapshotDatabase(self.as_raw())).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the numerical id (read-only)
|
||||
pub fn id(&self) -> i64 {
|
||||
unsafe { BNGetSnapshotId(self.as_raw()) }
|
||||
}
|
||||
|
||||
/// Get the displayed snapshot name
|
||||
pub fn name(&self) -> BnString {
|
||||
unsafe { BnString::from_raw(BNGetSnapshotName(self.as_raw())) }
|
||||
}
|
||||
|
||||
/// Set the displayed snapshot name
|
||||
pub fn set_name<S: BnStrCompatible>(&self, value: S) {
|
||||
let value_raw = value.into_bytes_with_nul();
|
||||
let value_ptr = value_raw.as_ref().as_ptr() as *const ffi::c_char;
|
||||
unsafe { BNSetSnapshotName(self.as_raw(), value_ptr) }
|
||||
}
|
||||
|
||||
/// If the snapshot was the result of an auto-save
|
||||
pub fn is_auto_save(&self) -> bool {
|
||||
unsafe { BNIsSnapshotAutoSave(self.as_raw()) }
|
||||
}
|
||||
|
||||
/// If the snapshot has contents, and has not been trimmed
|
||||
pub fn has_contents(&self) -> bool {
|
||||
unsafe { BNSnapshotHasContents(self.as_raw()) }
|
||||
}
|
||||
|
||||
/// If the snapshot has undo data
|
||||
pub fn has_undo(&self) -> bool {
|
||||
unsafe { BNSnapshotHasUndo(self.as_raw()) }
|
||||
}
|
||||
|
||||
/// Get the first parent of the snapshot, or None if it has no parents
|
||||
pub fn first_parent(&self) -> Option<Snapshot> {
|
||||
let result = unsafe { BNGetSnapshotFirstParent(self.as_raw()) };
|
||||
ptr::NonNull::new(result).map(|s| unsafe { Snapshot::from_raw(s) })
|
||||
}
|
||||
|
||||
/// Get a list of all parent snapshots of the snapshot
|
||||
pub fn parents(&self) -> Array<Snapshot> {
|
||||
let mut count = 0;
|
||||
let result = unsafe { BNGetSnapshotParents(self.as_raw(), &mut count) };
|
||||
assert!(!result.is_null());
|
||||
unsafe { Array::new(result, count, ()) }
|
||||
}
|
||||
|
||||
/// Get a list of all child snapshots of the snapshot
|
||||
pub fn children(&self) -> Array<Snapshot> {
|
||||
let mut count = 0;
|
||||
let result = unsafe { BNGetSnapshotChildren(self.as_raw(), &mut count) };
|
||||
assert!(!result.is_null());
|
||||
unsafe { Array::new(result, count, ()) }
|
||||
}
|
||||
|
||||
/// Get a buffer of the raw data at the time of the snapshot
|
||||
pub fn file_contents(&self) -> Option<DataBuffer> {
|
||||
self.has_contents().then(|| unsafe {
|
||||
let result = BNGetSnapshotFileContents(self.as_raw());
|
||||
assert!(!result.is_null());
|
||||
DataBuffer::from_raw(result)
|
||||
})
|
||||
}
|
||||
|
||||
/// Get a hash of the data at the time of the snapshot
|
||||
pub fn file_contents_hash(&self) -> Option<DataBuffer> {
|
||||
self.has_contents().then(|| unsafe {
|
||||
let result = BNGetSnapshotFileContentsHash(self.as_raw());
|
||||
assert!(!result.is_null());
|
||||
DataBuffer::from_raw(result)
|
||||
})
|
||||
}
|
||||
|
||||
/// Get a list of undo entries at the time of the snapshot
|
||||
pub fn undo_entries(&self) -> Array<UndoEntry> {
|
||||
assert!(self.has_undo());
|
||||
let mut count = 0;
|
||||
let result = unsafe { BNGetSnapshotUndoEntries(self.as_raw(), &mut count) };
|
||||
assert!(!result.is_null());
|
||||
unsafe { Array::new(result, count, ()) }
|
||||
}
|
||||
|
||||
pub fn undo_entries_with_progress<F: FnMut(usize, usize) -> bool>(
|
||||
&self,
|
||||
mut progress: F,
|
||||
) -> Array<UndoEntry> {
|
||||
assert!(self.has_undo());
|
||||
let ctxt = &mut progress as *mut _ as *mut ffi::c_void;
|
||||
let mut count = 0;
|
||||
let result = unsafe {
|
||||
BNGetSnapshotUndoEntriesWithProgress(
|
||||
self.as_raw(),
|
||||
ctxt,
|
||||
Some(cb_progress::<F>),
|
||||
&mut count,
|
||||
)
|
||||
};
|
||||
assert!(!result.is_null());
|
||||
unsafe { Array::new(result, count, ()) }
|
||||
}
|
||||
|
||||
/// Get the backing kvs data with snapshot fields
|
||||
pub fn read_data(&self) -> KeyValueStore {
|
||||
let result = unsafe { BNReadSnapshotData(self.as_raw()) };
|
||||
unsafe { KeyValueStore::from_raw(ptr::NonNull::new(result).unwrap()) }
|
||||
}
|
||||
|
||||
pub fn read_data_with_progress<F: FnMut(usize, usize) -> bool>(
|
||||
&self,
|
||||
mut progress: F,
|
||||
) -> KeyValueStore {
|
||||
let ctxt = &mut progress as *mut _ as *mut ffi::c_void;
|
||||
let result =
|
||||
unsafe { BNReadSnapshotDataWithProgress(self.as_raw(), ctxt, Some(cb_progress::<F>)) };
|
||||
unsafe { KeyValueStore::from_raw(ptr::NonNull::new(result).unwrap()) }
|
||||
}
|
||||
|
||||
pub fn undo_data(&self) -> DataBuffer {
|
||||
let result = unsafe { BNGetSnapshotUndoData(self.as_raw()) };
|
||||
assert!(!result.is_null());
|
||||
DataBuffer::from_raw(result)
|
||||
}
|
||||
|
||||
pub fn store_data<F: FnMut(usize, usize) -> bool>(
|
||||
&self,
|
||||
data: KeyValueStore,
|
||||
mut progress: F,
|
||||
) -> bool {
|
||||
let ctxt = &mut progress as *mut _ as *mut ffi::c_void;
|
||||
unsafe { BNSnapshotStoreData(self.as_raw(), data.as_raw(), ctxt, Some(cb_progress::<F>)) }
|
||||
}
|
||||
|
||||
/// Determine if this snapshot has another as an ancestor
|
||||
pub fn has_ancestor(self, other: &Snapshot) -> bool {
|
||||
unsafe { BNSnapshotHasAncestor(self.as_raw(), other.as_raw()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for Snapshot {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe { Self::from_raw(ptr::NonNull::new(BNNewSnapshotReference(self.as_raw())).unwrap()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Snapshot {
|
||||
fn drop(&mut self) {
|
||||
unsafe { BNFreeSnapshot(self.as_raw()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for Snapshot {
|
||||
type Raw = *mut BNSnapshot;
|
||||
type Context = ();
|
||||
type Wrapped<'a> = &'a Self;
|
||||
}
|
||||
|
||||
unsafe impl CoreArrayProviderInner for Snapshot {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeSnapshotList(raw, count);
|
||||
}
|
||||
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
Self::ref_from_raw(raw)
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct KeyValueStore {
|
||||
handle: ptr::NonNull<BNKeyValueStore>,
|
||||
}
|
||||
|
||||
impl KeyValueStore {
|
||||
pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNKeyValueStore>) -> Self {
|
||||
Self { handle }
|
||||
}
|
||||
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
pub(crate) unsafe fn as_raw(&self) -> &mut BNKeyValueStore {
|
||||
&mut *self.handle.as_ptr()
|
||||
}
|
||||
|
||||
/// Get a list of all keys stored in the kvs
|
||||
pub fn keys(&self) -> Array<BnString> {
|
||||
let mut count = 0;
|
||||
let result = unsafe { BNGetKeyValueStoreKeys(self.as_raw(), &mut count) };
|
||||
assert!(!result.is_null());
|
||||
unsafe { Array::new(result, count, ()) }
|
||||
}
|
||||
|
||||
/// Get the value for a single key
|
||||
pub fn value<S: BnStrCompatible>(&self, key: S) -> Option<DataBuffer> {
|
||||
let key_raw = key.into_bytes_with_nul();
|
||||
let key_ptr = key_raw.as_ref().as_ptr() as *const ffi::c_char;
|
||||
let result = unsafe { BNGetKeyValueStoreBuffer(self.as_raw(), key_ptr) };
|
||||
ptr::NonNull::new(result).map(|_| DataBuffer::from_raw(result))
|
||||
}
|
||||
|
||||
/// Set the value for a single key
|
||||
pub fn set_value<S: BnStrCompatible>(&self, key: S, value: &DataBuffer) -> bool {
|
||||
let key_raw = key.into_bytes_with_nul();
|
||||
let key_ptr = key_raw.as_ref().as_ptr() as *const ffi::c_char;
|
||||
unsafe { BNSetKeyValueStoreBuffer(self.as_raw(), key_ptr, value.as_raw()) }
|
||||
}
|
||||
|
||||
/// Get the stored representation of the kvs
|
||||
pub fn serialized_data(&self) -> DataBuffer {
|
||||
let result = unsafe { BNGetKeyValueStoreSerializedData(self.as_raw()) };
|
||||
assert!(!result.is_null());
|
||||
DataBuffer::from_raw(result)
|
||||
}
|
||||
|
||||
/// Begin storing new keys into a namespace
|
||||
pub fn begin_namespace<S: BnStrCompatible>(&self, name: S) {
|
||||
let name_raw = name.into_bytes_with_nul();
|
||||
let name_ptr = name_raw.as_ref().as_ptr() as *const ffi::c_char;
|
||||
unsafe { BNBeginKeyValueStoreNamespace(self.as_raw(), name_ptr) }
|
||||
}
|
||||
|
||||
/// End storing new keys into a namespace
|
||||
pub fn end_namespace(&self) {
|
||||
unsafe { BNEndKeyValueStoreNamespace(self.as_raw()) }
|
||||
}
|
||||
|
||||
/// If the kvs is empty
|
||||
pub fn empty(&self) -> bool {
|
||||
unsafe { BNIsKeyValueStoreEmpty(self.as_raw()) }
|
||||
}
|
||||
|
||||
/// Number of values in the kvs
|
||||
pub fn value_size(&self) -> usize {
|
||||
unsafe { BNGetKeyValueStoreValueSize(self.as_raw()) }
|
||||
}
|
||||
|
||||
/// Length of serialized data
|
||||
pub fn data_size(&self) -> usize {
|
||||
unsafe { BNGetKeyValueStoreDataSize(self.as_raw()) }
|
||||
}
|
||||
|
||||
/// Size of all data in storage
|
||||
pub fn value_storage_size(self) -> usize {
|
||||
unsafe { BNGetKeyValueStoreValueStorageSize(self.as_raw()) }
|
||||
}
|
||||
|
||||
/// Number of namespaces pushed with begin_namespace
|
||||
pub fn namespace_size(self) -> usize {
|
||||
unsafe { BNGetKeyValueStoreNamespaceSize(self.as_raw()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for KeyValueStore {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
Self::from_raw(ptr::NonNull::new(BNNewKeyValueStoreReference(self.as_raw())).unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for KeyValueStore {
|
||||
fn drop(&mut self) {
|
||||
unsafe { BNFreeKeyValueStore(self.as_raw()) }
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct UndoEntry {
|
||||
handle: ptr::NonNull<BNUndoEntry>,
|
||||
}
|
||||
|
||||
impl UndoEntry {
|
||||
pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNUndoEntry>) -> Self {
|
||||
Self { handle }
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn ref_from_raw(handle: &*mut BNUndoEntry) -> &Self {
|
||||
mem::transmute(handle)
|
||||
}
|
||||
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
pub(crate) unsafe fn as_raw(&self) -> &mut BNUndoEntry {
|
||||
&mut *self.handle.as_ptr()
|
||||
}
|
||||
|
||||
pub fn id(&self) -> BnString {
|
||||
let result = unsafe { BNUndoEntryGetId(self.as_raw()) };
|
||||
assert!(!result.is_null());
|
||||
unsafe { BnString::from_raw(result) }
|
||||
}
|
||||
|
||||
pub fn actions(&self) -> Array<UndoAction> {
|
||||
let mut count = 0;
|
||||
let result = unsafe { BNUndoEntryGetActions(self.as_raw(), &mut count) };
|
||||
assert!(!result.is_null());
|
||||
unsafe { Array::new(result, count, ()) }
|
||||
}
|
||||
|
||||
pub fn time(&self) -> SystemTime {
|
||||
let m = Duration::from_secs(unsafe { BNUndoEntryGetTimestamp(self.as_raw()) });
|
||||
UNIX_EPOCH + m
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for UndoEntry {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
Self::from_raw(ptr::NonNull::new(BNNewUndoEntryReference(self.as_raw())).unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for UndoEntry {
|
||||
fn drop(&mut self) {
|
||||
unsafe { BNFreeUndoEntry(self.as_raw()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for UndoEntry {
|
||||
type Raw = *mut BNUndoEntry;
|
||||
type Context = ();
|
||||
type Wrapped<'a> = &'a Self;
|
||||
}
|
||||
|
||||
unsafe impl CoreArrayProviderInner for UndoEntry {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeUndoEntryList(raw, count);
|
||||
}
|
||||
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
Self::ref_from_raw(raw)
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct UndoAction {
|
||||
handle: ptr::NonNull<BNUndoAction>,
|
||||
}
|
||||
|
||||
impl UndoAction {
|
||||
pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNUndoAction>) -> Self {
|
||||
Self { handle }
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn ref_from_raw(handle: &*mut BNUndoAction) -> &Self {
|
||||
mem::transmute(handle)
|
||||
}
|
||||
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
pub(crate) unsafe fn as_raw(&self) -> &mut BNUndoAction {
|
||||
&mut *self.handle.as_ptr()
|
||||
}
|
||||
|
||||
pub fn summary_text(&self) -> BnString {
|
||||
let result = unsafe { BNUndoActionGetSummaryText(self.as_raw()) };
|
||||
assert!(!result.is_null());
|
||||
unsafe { BnString::from_raw(result) }
|
||||
}
|
||||
|
||||
pub fn summary(&self) -> Array<InstructionTextToken> {
|
||||
let mut count = 0;
|
||||
let result = unsafe { BNUndoActionGetSummary(self.as_raw(), &mut count) };
|
||||
assert!(!result.is_null());
|
||||
unsafe { Array::new(result, count, ()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for UndoAction {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
Self::from_raw(ptr::NonNull::new(BNNewUndoActionReference(self.as_raw())).unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for UndoAction {
|
||||
fn drop(&mut self) {
|
||||
unsafe { BNFreeUndoAction(self.as_raw()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for UndoAction {
|
||||
type Raw = *mut BNUndoAction;
|
||||
type Context = ();
|
||||
type Wrapped<'a> = &'a Self;
|
||||
}
|
||||
|
||||
unsafe impl CoreArrayProviderInner for UndoAction {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeUndoActionList(raw, count);
|
||||
}
|
||||
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
Self::ref_from_raw(raw)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn cb_progress<F: FnMut(usize, usize) -> bool>(
|
||||
ctxt: *mut ffi::c_void,
|
||||
arg1: usize,
|
||||
arg2: usize,
|
||||
) -> bool {
|
||||
let ctxt: &mut F = &mut *(ctxt as *mut F);
|
||||
ctxt(arg1, arg2)
|
||||
}
|
||||
|
||||
unsafe extern "C" fn cb_progress_nop(_ctxt: *mut ffi::c_void, _arg1: usize, _arg2: usize) -> bool {
|
||||
true
|
||||
}
|
||||
@@ -17,9 +17,10 @@
|
||||
use binaryninjacore_sys::*;
|
||||
|
||||
use std::ffi::c_void;
|
||||
use std::ptr;
|
||||
use std::slice;
|
||||
|
||||
use crate::string::BnString;
|
||||
|
||||
pub struct DataBuffer(*mut BNDataBuffer);
|
||||
|
||||
impl DataBuffer {
|
||||
@@ -31,10 +32,6 @@ impl DataBuffer {
|
||||
}
|
||||
|
||||
pub fn get_data(&self) -> &[u8] {
|
||||
if self.0.is_null() {
|
||||
// TODO : Change the default value and remove this
|
||||
return &[];
|
||||
}
|
||||
let buffer = unsafe { BNGetDataBufferContents(self.0) };
|
||||
if buffer.is_null() {
|
||||
&[]
|
||||
@@ -43,6 +40,65 @@ impl DataBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_data_at(&self, offset: usize) -> &[u8] {
|
||||
let len = self.len();
|
||||
if offset > len {
|
||||
panic!();
|
||||
}
|
||||
let slice_len = len - offset;
|
||||
let buffer = unsafe { BNGetDataBufferContentsAt(self.0, offset) };
|
||||
if buffer.is_null() {
|
||||
&[]
|
||||
} else {
|
||||
unsafe { slice::from_raw_parts(buffer as *const _, slice_len) }
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a copy of a especified part of the data
|
||||
pub fn get_slice(&self, start: usize, len: usize) -> Option<Self> {
|
||||
if start + len > self.len() {
|
||||
return None;
|
||||
}
|
||||
let ptr = unsafe { BNGetDataBufferSlice(self.0, start, len) };
|
||||
(!ptr.is_null()).then(|| Self(ptr))
|
||||
}
|
||||
|
||||
/// change the size of the allocated data, if new size is bigger data is
|
||||
/// need to be initialized
|
||||
pub unsafe fn set_len(&mut self, len: usize) {
|
||||
unsafe { BNSetDataBufferLength(self.0, len) }
|
||||
}
|
||||
|
||||
/// set the size to 0
|
||||
pub fn clear(&self) {
|
||||
unsafe { BNClearDataBuffer(self.0) }
|
||||
}
|
||||
|
||||
/// Copy the contents of `src` into `dst`
|
||||
pub fn assign(dst: &mut Self, src: &Self) {
|
||||
unsafe { BNAssignDataBuffer(dst.0, src.0) }
|
||||
}
|
||||
|
||||
/// Concat the contents of `src` into `dst`
|
||||
pub fn append(dst: &mut Self, src: &Self) {
|
||||
unsafe { BNAppendDataBuffer(dst.0, src.0) }
|
||||
}
|
||||
|
||||
/// concat the contents of `data` into self
|
||||
pub fn append_data(&self, data: &[u8]) {
|
||||
unsafe { BNAppendDataBufferContents(self.0, data.as_ptr() as *const c_void, data.len()) }
|
||||
}
|
||||
|
||||
/// Return the byte at `offset`
|
||||
pub unsafe fn byte_at(&self, offset: usize) -> u8 {
|
||||
unsafe { BNGetDataBufferByte(self.0, offset) }
|
||||
}
|
||||
|
||||
/// Set the value of the byte at `offset`
|
||||
pub unsafe fn set_byte_at(&mut self, offset: usize, byte: u8) {
|
||||
unsafe { BNSetDataBufferByte(self.0, offset, byte) }
|
||||
}
|
||||
|
||||
pub fn set_data(&mut self, data: &[u8]) {
|
||||
unsafe {
|
||||
BNSetDataBufferContents(
|
||||
@@ -53,12 +109,48 @@ impl DataBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_escaped_string(&self, null_terminates: bool) -> BnString {
|
||||
unsafe { BnString::from_raw(BNDataBufferToEscapedString(self.0, null_terminates)) }
|
||||
}
|
||||
|
||||
pub fn from_escaped_string(value: &BnString) -> Self {
|
||||
Self(unsafe { BNDecodeEscapedString(value.as_raw()) })
|
||||
}
|
||||
|
||||
pub fn to_base64(&self) -> BnString {
|
||||
unsafe { BnString::from_raw(BNDataBufferToBase64(self.0)) }
|
||||
}
|
||||
|
||||
pub fn from_base64(value: &BnString) -> Self {
|
||||
Self(unsafe { BNDecodeBase64(value.as_raw()) })
|
||||
}
|
||||
|
||||
pub fn zlib_compress(&self) -> Self {
|
||||
Self(unsafe { BNZlibCompress(self.0) })
|
||||
}
|
||||
|
||||
pub fn zlib_decompress(&self) -> Self {
|
||||
Self(unsafe { BNZlibDecompress(self.0) })
|
||||
}
|
||||
|
||||
pub fn lzma_decompress(&self) -> Self {
|
||||
Self(unsafe { BNLzmaDecompress(self.0) })
|
||||
}
|
||||
|
||||
pub fn lzma2_decompress(&self) -> Self {
|
||||
Self(unsafe { BNLzma2Decompress(self.0) })
|
||||
}
|
||||
|
||||
pub fn xz_decompress(&self) -> Self {
|
||||
Self(unsafe { BNXzDecompress(self.0) })
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
unsafe { BNGetDataBufferLength(self.0) }
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
unsafe { BNGetDataBufferLength(self.0) == 0 }
|
||||
self.len() == 0
|
||||
}
|
||||
|
||||
pub fn new(data: &[u8]) -> Result<Self, ()> {
|
||||
@@ -71,19 +163,16 @@ impl DataBuffer {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO : delete this
|
||||
impl Default for DataBuffer {
|
||||
fn default() -> Self {
|
||||
DataBuffer::from_raw(ptr::null_mut())
|
||||
Self(unsafe { BNCreateDataBuffer([].as_ptr() as *const c_void, 0) })
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DataBuffer {
|
||||
fn drop(&mut self) {
|
||||
if !self.0.is_null() {
|
||||
unsafe {
|
||||
BNFreeDataBuffer(self.0);
|
||||
}
|
||||
unsafe {
|
||||
BNFreeDataBuffer(self.0);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -93,3 +182,152 @@ impl Clone for DataBuffer {
|
||||
Self::from_raw(unsafe { BNDuplicateDataBuffer(self.0) })
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<&[u8]> for DataBuffer {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
|
||||
DataBuffer::new(value)
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for DataBuffer {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.get_data()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::borrow::Borrow<[u8]> for DataBuffer {
|
||||
fn borrow(&self) -> &[u8] {
|
||||
self.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! data_buffer_index {
|
||||
($range:ty, $output:ty) => {
|
||||
impl std::ops::Index<$range> for DataBuffer {
|
||||
type Output = $output;
|
||||
|
||||
fn index(&self, index: $range) -> &Self::Output {
|
||||
&self.get_data()[index]
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
data_buffer_index!(usize, u8);
|
||||
data_buffer_index!(std::ops::Range<usize>, [u8]);
|
||||
data_buffer_index!(std::ops::RangeInclusive<usize>, [u8]);
|
||||
data_buffer_index!(std::ops::RangeTo<usize>, [u8]);
|
||||
data_buffer_index!(std::ops::RangeFull, [u8]);
|
||||
|
||||
impl PartialEq for DataBuffer {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.as_ref() == other.as_ref()
|
||||
}
|
||||
}
|
||||
impl Eq for DataBuffer {}
|
||||
|
||||
impl PartialOrd for DataBuffer {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.as_ref().cmp(other.as_ref()))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for DataBuffer {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.as_ref().cmp(other.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::DataBuffer;
|
||||
|
||||
const DUMMY_DATA_0: &[u8] = b"0123456789\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x09\xFF";
|
||||
const DUMMY_DATA_1: &[u8] = b"qwertyuiopasdfghjkl\xE7zxcvbnm\x00\x01\x00";
|
||||
|
||||
#[test]
|
||||
fn get_slice() {
|
||||
let data = DataBuffer::new(DUMMY_DATA_0).unwrap();
|
||||
let slice = data.get_slice(9, 10).unwrap();
|
||||
assert_eq!(slice.get_data(), &DUMMY_DATA_0[9..19]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn set_len_write() {
|
||||
let mut data = DataBuffer::default();
|
||||
assert_eq!(data.get_data(), &[]);
|
||||
unsafe { data.set_len(DUMMY_DATA_0.len()) };
|
||||
assert_eq!(data.len(), DUMMY_DATA_0.len());
|
||||
let mut contents = DUMMY_DATA_0.to_vec();
|
||||
data.set_data(&contents);
|
||||
// modify the orinal contents, to make sure DataBuffer copied the data
|
||||
// and is not using the original pointer
|
||||
contents.as_mut_slice().fill(0x55);
|
||||
drop(contents);
|
||||
assert_eq!(data.get_data(), &DUMMY_DATA_0[..]);
|
||||
|
||||
// make sure the new len truncate the original data
|
||||
unsafe { data.set_len(13) };
|
||||
assert_eq!(data.get_data(), &DUMMY_DATA_0[..13]);
|
||||
|
||||
data.clear();
|
||||
assert_eq!(data.get_data(), &[]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn assign_append() {
|
||||
let mut dst = DataBuffer::new(DUMMY_DATA_0).unwrap();
|
||||
let mut src = DataBuffer::new(DUMMY_DATA_1).unwrap();
|
||||
DataBuffer::assign(&mut dst, &src);
|
||||
|
||||
assert_eq!(dst.get_data(), DUMMY_DATA_1);
|
||||
assert_eq!(src.get_data(), DUMMY_DATA_1);
|
||||
// overwrite the src, to make sure that src is copied to dst, and not
|
||||
// moved into it
|
||||
src.set_data(DUMMY_DATA_0);
|
||||
assert_eq!(dst.get_data(), DUMMY_DATA_1);
|
||||
assert_eq!(src.get_data(), DUMMY_DATA_0);
|
||||
|
||||
DataBuffer::append(&mut dst, &src);
|
||||
let result: Vec<_> = DUMMY_DATA_1.iter().chain(DUMMY_DATA_0).copied().collect();
|
||||
assert_eq!(dst.get_data(), &result);
|
||||
|
||||
assert_eq!(src.get_data(), DUMMY_DATA_0);
|
||||
src.set_data(DUMMY_DATA_1);
|
||||
assert_eq!(src.get_data(), DUMMY_DATA_1);
|
||||
assert_eq!(dst.get_data(), &result);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn to_from_formats() {
|
||||
let data = DataBuffer::new(DUMMY_DATA_0).unwrap();
|
||||
let escaped = data.to_escaped_string(false);
|
||||
let unescaped = DataBuffer::from_escaped_string(&escaped);
|
||||
drop(escaped);
|
||||
let escaped_part = data.to_escaped_string(true);
|
||||
let unescaped_part = DataBuffer::from_escaped_string(&escaped_part);
|
||||
drop(escaped_part);
|
||||
|
||||
let part = &DUMMY_DATA_0[0..DUMMY_DATA_0
|
||||
.iter()
|
||||
.position(|x| *x == 0)
|
||||
.unwrap_or(DUMMY_DATA_0.len())];
|
||||
assert_eq!(data.get_data(), DUMMY_DATA_0);
|
||||
assert_eq!(unescaped.get_data(), DUMMY_DATA_0);
|
||||
assert_eq!(unescaped_part.get_data(), part);
|
||||
|
||||
let escaped = data.to_base64();
|
||||
let unescaped = DataBuffer::from_base64(&escaped);
|
||||
drop(escaped);
|
||||
assert_eq!(data.get_data(), DUMMY_DATA_0);
|
||||
assert_eq!(unescaped.get_data(), DUMMY_DATA_0);
|
||||
|
||||
let compressed = data.zlib_compress();
|
||||
let decompressed = compressed.zlib_decompress();
|
||||
drop(compressed);
|
||||
assert_eq!(data.get_data(), DUMMY_DATA_0);
|
||||
assert_eq!(decompressed.get_data(), DUMMY_DATA_0);
|
||||
}
|
||||
}
|
||||
|
||||
117
src/debuginfo.rs
117
src/debuginfo.rs
@@ -27,7 +27,7 @@
|
||||
//! And finally calling `binaryninja::debuginfo::DebugInfoParser::register` to register it with the core.
|
||||
//!
|
||||
//! Here's a minimal, complete example boilerplate-plugin:
|
||||
//! ```
|
||||
//! ```no_run
|
||||
//! use binaryninja::{
|
||||
//! binaryview::BinaryView,
|
||||
//! debuginfo::{CustomDebugInfoParser, DebugInfo, DebugInfoParser},
|
||||
@@ -40,8 +40,9 @@
|
||||
//! true
|
||||
//! }
|
||||
//!
|
||||
//! fn parse_info(&self, _debug_info: &mut DebugInfo, _view: &BinaryView, _debug_file: &BinaryView, _progress: Box<dyn Fn(usize, usize) -> bool>) {
|
||||
//! fn parse_info(&self, _debug_info: &mut DebugInfo, _view: &BinaryView, _debug_file: &BinaryView, _progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>) -> bool {
|
||||
//! println!("Parsing info");
|
||||
//! true
|
||||
//! }
|
||||
//! }
|
||||
//!
|
||||
@@ -53,11 +54,14 @@
|
||||
//! ```
|
||||
//!
|
||||
//! `DebugInfo` will then be automatically applied to binary views that contain debug information (via the setting `analysis.debugInfo.internal`), binary views that provide valid external debug info files (`analysis.debugInfo.external`), or manually fetched/applied as below:
|
||||
//! ```
|
||||
//! let valid_parsers = DebugInfoParser::parsers_for_view(bv);
|
||||
//! let parser = valid_parsers[0];
|
||||
//! let debug_info = parser.parse_debug_info(bv);
|
||||
//! bv.apply_debug_info(debug_info);
|
||||
//! ```no_run
|
||||
//! # use binaryninja::debuginfo::DebugInfoParser;
|
||||
//! # use binaryninja::binaryview::BinaryViewExt;
|
||||
//! let bv = binaryninja::load("example").unwrap();
|
||||
//! let valid_parsers = DebugInfoParser::parsers_for_view(&bv);
|
||||
//! let parser = valid_parsers.get(0);
|
||||
//! let debug_info = parser.parse_debug_info(&bv, &bv, None, None).unwrap();
|
||||
//! bv.apply_debug_info(&debug_info);
|
||||
//! ```
|
||||
//!
|
||||
//! Multiple debug-info parsers can manually contribute debug info for a binary view by simply calling `parse_debug_info` with the
|
||||
@@ -71,10 +75,10 @@ use crate::{
|
||||
platform::Platform,
|
||||
rc::*,
|
||||
string::{raw_to_string, BnStrCompatible, BnString},
|
||||
types::{DataVariableAndName, NameAndType, Type},
|
||||
types::{DataVariableAndName, NameAndType, NamedTypedVariable, Type},
|
||||
};
|
||||
|
||||
use std::{hash::Hash, mem, os::raw::c_void, ptr, slice};
|
||||
use std::{hash::Hash, os::raw::c_void, ptr, slice};
|
||||
|
||||
struct ProgressContext(Option<Box<dyn Fn(usize, usize) -> Result<(), ()>>>);
|
||||
|
||||
@@ -109,14 +113,14 @@ impl DebugInfoParser {
|
||||
|
||||
/// List all debug-info parsers
|
||||
pub fn list() -> Array<DebugInfoParser> {
|
||||
let mut count: usize = unsafe { mem::zeroed() };
|
||||
let mut count = 0;
|
||||
let raw_parsers = unsafe { BNGetDebugInfoParsers(&mut count as *mut _) };
|
||||
unsafe { Array::new(raw_parsers, count, ()) }
|
||||
}
|
||||
|
||||
/// Returns a list of debug-info parsers that are valid for the provided binary view
|
||||
pub fn parsers_for_view(bv: &BinaryView) -> Array<DebugInfoParser> {
|
||||
let mut count: usize = unsafe { mem::zeroed() };
|
||||
let mut count = 0;
|
||||
let raw_parsers = unsafe { BNGetDebugInfoParsersForView(bv.handle, &mut count as *mut _) };
|
||||
unsafe { Array::new(raw_parsers, count, ()) }
|
||||
}
|
||||
@@ -269,12 +273,16 @@ impl ToOwned for DebugInfoParser {
|
||||
impl CoreArrayProvider for DebugInfoParser {
|
||||
type Raw = *mut BNDebugInfoParser;
|
||||
type Context = ();
|
||||
type Wrapped<'a> = Guard<'a, DebugInfoParser>;
|
||||
}
|
||||
|
||||
unsafe impl CoreOwnedArrayProvider for DebugInfoParser {
|
||||
unsafe impl CoreArrayProviderInner for DebugInfoParser {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _: &Self::Context) {
|
||||
BNFreeDebugInfoParserList(raw, count);
|
||||
}
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
Guard::new(Self { handle: *raw }, context)
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////
|
||||
@@ -293,6 +301,7 @@ pub struct DebugFunctionInfo {
|
||||
address: u64,
|
||||
platform: Option<Ref<Platform>>,
|
||||
components: Vec<String>,
|
||||
local_variables: Vec<NamedTypedVariable>,
|
||||
}
|
||||
|
||||
impl From<&BNDebugFunctionInfo> for DebugFunctionInfo {
|
||||
@@ -302,6 +311,15 @@ impl From<&BNDebugFunctionInfo> for DebugFunctionInfo {
|
||||
.map(|component| raw_to_string(*component as *const _).unwrap())
|
||||
.collect();
|
||||
|
||||
let local_variables: Vec<NamedTypedVariable> = unsafe { slice::from_raw_parts(raw.localVariables, raw.localVariableN) }
|
||||
.iter()
|
||||
.map(|local_variable| {
|
||||
unsafe {
|
||||
NamedTypedVariable::from_raw(local_variable)
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
Self {
|
||||
short_name: raw_to_string(raw.shortName),
|
||||
full_name: raw_to_string(raw.fullName),
|
||||
@@ -318,11 +336,13 @@ impl From<&BNDebugFunctionInfo> for DebugFunctionInfo {
|
||||
Some(unsafe { Platform::ref_from_raw(raw.platform) })
|
||||
},
|
||||
components,
|
||||
local_variables,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DebugFunctionInfo {
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn new(
|
||||
short_name: Option<String>,
|
||||
full_name: Option<String>,
|
||||
@@ -331,18 +351,17 @@ impl DebugFunctionInfo {
|
||||
address: Option<u64>,
|
||||
platform: Option<Ref<Platform>>,
|
||||
components: Vec<String>,
|
||||
local_variables: Vec<NamedTypedVariable>,
|
||||
) -> Self {
|
||||
Self {
|
||||
short_name,
|
||||
full_name,
|
||||
raw_name,
|
||||
type_,
|
||||
address: match address {
|
||||
Some(address) => address,
|
||||
_ => 0,
|
||||
},
|
||||
address: address.unwrap_or(0),
|
||||
platform,
|
||||
components,
|
||||
local_variables,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -376,7 +395,7 @@ impl DebugInfo {
|
||||
}
|
||||
|
||||
/// Returns a generator of all types provided by a named DebugInfoParser
|
||||
pub fn types_by_name<S: BnStrCompatible>(&self, parser_name: S) -> Vec<NameAndType<String>> {
|
||||
pub fn types_by_name<S: BnStrCompatible>(&self, parser_name: S) -> Vec<Ref<NameAndType>> {
|
||||
let parser_name = parser_name.into_bytes_with_nul();
|
||||
|
||||
let mut count: usize = 0;
|
||||
@@ -387,10 +406,10 @@ impl DebugInfo {
|
||||
&mut count,
|
||||
)
|
||||
};
|
||||
let result: Vec<NameAndType<String>> = unsafe {
|
||||
let result: Vec<Ref<NameAndType>> = unsafe {
|
||||
slice::from_raw_parts_mut(debug_types_ptr, count)
|
||||
.iter()
|
||||
.map(NameAndType::<String>::from_raw)
|
||||
.map(|x| NameAndType::from_raw(x).to_owned())
|
||||
.collect()
|
||||
};
|
||||
|
||||
@@ -399,13 +418,13 @@ impl DebugInfo {
|
||||
}
|
||||
|
||||
/// A generator of all types provided by DebugInfoParsers
|
||||
pub fn types(&self) -> Vec<NameAndType<String>> {
|
||||
pub fn types(&self) -> Vec<Ref<NameAndType>> {
|
||||
let mut count: usize = 0;
|
||||
let debug_types_ptr = unsafe { BNGetDebugTypes(self.handle, ptr::null_mut(), &mut count) };
|
||||
let result: Vec<NameAndType<String>> = unsafe {
|
||||
let result: Vec<Ref<NameAndType>> = unsafe {
|
||||
slice::from_raw_parts_mut(debug_types_ptr, count)
|
||||
.iter()
|
||||
.map(NameAndType::<String>::from_raw)
|
||||
.map(|x| NameAndType::from_raw(x).to_owned())
|
||||
.collect()
|
||||
};
|
||||
|
||||
@@ -416,7 +435,7 @@ impl DebugInfo {
|
||||
/// Returns a generator of all functions provided by a named DebugInfoParser
|
||||
pub fn functions_by_name<S: BnStrCompatible>(
|
||||
&self,
|
||||
parser_name: S,
|
||||
parser_name: S
|
||||
) -> Vec<DebugFunctionInfo> {
|
||||
let parser_name = parser_name.into_bytes_with_nul();
|
||||
|
||||
@@ -758,30 +777,41 @@ impl DebugInfo {
|
||||
let short_name_bytes = new_func.short_name.map(|name| name.into_bytes_with_nul());
|
||||
let short_name = short_name_bytes
|
||||
.as_ref()
|
||||
.map_or(ptr::null_mut() as *mut _, |name| {
|
||||
name.as_ptr() as _
|
||||
});
|
||||
.map_or(ptr::null_mut() as *mut _, |name| name.as_ptr() as _);
|
||||
let full_name_bytes = new_func.full_name.map(|name| name.into_bytes_with_nul());
|
||||
let full_name = full_name_bytes
|
||||
.as_ref()
|
||||
.map_or(ptr::null_mut() as *mut _, |name| {
|
||||
name.as_ptr() as _
|
||||
});
|
||||
.map_or(ptr::null_mut() as *mut _, |name| name.as_ptr() as _);
|
||||
let raw_name_bytes = new_func.raw_name.map(|name| name.into_bytes_with_nul());
|
||||
let raw_name = raw_name_bytes
|
||||
.as_ref()
|
||||
.map_or(ptr::null_mut() as *mut _, |name| {
|
||||
name.as_ptr() as _
|
||||
});
|
||||
.map_or(ptr::null_mut() as *mut _, |name| name.as_ptr() as _);
|
||||
|
||||
let mut components_array: Vec<*const ::std::os::raw::c_char> =
|
||||
let mut components_array: Vec<*mut ::std::os::raw::c_char> =
|
||||
Vec::with_capacity(new_func.components.len());
|
||||
for component in &new_func.components {
|
||||
components_array.push(component.as_ptr() as _);
|
||||
}
|
||||
|
||||
|
||||
let mut local_variables_array: Vec<BNVariableNameAndType> =
|
||||
Vec::with_capacity(new_func.local_variables.len());
|
||||
|
||||
unsafe {
|
||||
BNAddDebugFunction(
|
||||
for component in &new_func.components {
|
||||
components_array.push(BNAllocString(component.clone().into_bytes_with_nul().as_ptr() as _));
|
||||
}
|
||||
|
||||
for local_variable in &new_func.local_variables {
|
||||
local_variables_array.push(
|
||||
BNVariableNameAndType {
|
||||
var: local_variable.var.raw(),
|
||||
autoDefined: local_variable.auto_defined,
|
||||
typeConfidence: local_variable.ty.confidence,
|
||||
name: BNAllocString(local_variable.name.clone().into_bytes_with_nul().as_ptr() as _),
|
||||
type_: local_variable.ty.contents.handle,
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
let result = BNAddDebugFunction(
|
||||
self.handle,
|
||||
&mut BNDebugFunctionInfo {
|
||||
shortName: short_name,
|
||||
@@ -798,8 +828,19 @@ impl DebugInfo {
|
||||
},
|
||||
components: components_array.as_ptr() as _,
|
||||
componentN: new_func.components.len(),
|
||||
localVariables: local_variables_array.as_ptr() as _,
|
||||
localVariableN: local_variables_array.len(),
|
||||
},
|
||||
)
|
||||
);
|
||||
|
||||
for i in components_array {
|
||||
BNFreeString(i);
|
||||
}
|
||||
|
||||
for i in &local_variables_array {
|
||||
BNFreeString(i.name);
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -26,6 +26,49 @@ use crate::rc::*;
|
||||
|
||||
pub type Result<R> = result::Result<R, ()>;
|
||||
|
||||
pub fn demangle_llvm<S: BnStrCompatible>(
|
||||
mangled_name: S,
|
||||
simplify: bool,
|
||||
) -> Result<Vec<String>> {
|
||||
let mangled_name_bwn = mangled_name.into_bytes_with_nul();
|
||||
let mangled_name_ptr = mangled_name_bwn.as_ref();
|
||||
let mut out_name: *mut *mut std::os::raw::c_char = unsafe { std::mem::zeroed() };
|
||||
let mut out_size: usize = 0;
|
||||
let res = unsafe {
|
||||
BNDemangleLLVM(
|
||||
mangled_name_ptr.as_ptr() as *const c_char,
|
||||
&mut out_name,
|
||||
&mut out_size,
|
||||
simplify,
|
||||
)
|
||||
};
|
||||
|
||||
if !res || out_size == 0 {
|
||||
let cstr = match CStr::from_bytes_with_nul(mangled_name_ptr) {
|
||||
Ok(cstr) => cstr,
|
||||
Err(_) => {
|
||||
log::error!("demangle_llvm: failed to parse mangled name");
|
||||
return Err(());
|
||||
}
|
||||
};
|
||||
return Ok(vec![cstr.to_string_lossy().into_owned()]);
|
||||
}
|
||||
|
||||
if out_name.is_null() {
|
||||
log::error!("demangle_llvm: out_name is NULL");
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let names = unsafe { ArrayGuard::<BnString>::new(out_name, out_size, ()) }
|
||||
.iter()
|
||||
.map(str::to_string)
|
||||
.collect();
|
||||
|
||||
unsafe { BNFreeDemangledName(&mut out_name, out_size) };
|
||||
|
||||
Ok(names)
|
||||
}
|
||||
|
||||
pub fn demangle_gnu3<S: BnStrCompatible>(
|
||||
arch: &CoreArchitecture,
|
||||
mangled_name: S,
|
||||
@@ -33,8 +76,8 @@ pub fn demangle_gnu3<S: BnStrCompatible>(
|
||||
) -> Result<(Option<Ref<Type>>, Vec<String>)> {
|
||||
let mangled_name_bwn = mangled_name.into_bytes_with_nul();
|
||||
let mangled_name_ptr = mangled_name_bwn.as_ref();
|
||||
let mut out_type: *mut BNType = unsafe { std::mem::zeroed() };
|
||||
let mut out_name: *mut *mut std::os::raw::c_char = unsafe { std::mem::zeroed() };
|
||||
let mut out_type: *mut BNType = std::ptr::null_mut();
|
||||
let mut out_name: *mut *mut std::os::raw::c_char = std::ptr::null_mut();
|
||||
let mut out_size: usize = 0;
|
||||
let res = unsafe {
|
||||
BNDemangleGNU3(
|
||||
@@ -73,7 +116,7 @@ pub fn demangle_gnu3<S: BnStrCompatible>(
|
||||
|
||||
let names = unsafe { ArrayGuard::<BnString>::new(out_name, out_size, ()) }
|
||||
.iter()
|
||||
.map(|name| name.to_string())
|
||||
.map(str::to_string)
|
||||
.collect();
|
||||
|
||||
unsafe { BNFreeDemangledName(&mut out_name, out_size) };
|
||||
@@ -89,8 +132,8 @@ pub fn demangle_ms<S: BnStrCompatible>(
|
||||
let mangled_name_bwn = mangled_name.into_bytes_with_nul();
|
||||
let mangled_name_ptr = mangled_name_bwn.as_ref();
|
||||
|
||||
let mut out_type: *mut BNType = unsafe { std::mem::zeroed() };
|
||||
let mut out_name: *mut *mut std::os::raw::c_char = unsafe { std::mem::zeroed() };
|
||||
let mut out_type: *mut BNType = std::ptr::null_mut();
|
||||
let mut out_name: *mut *mut std::os::raw::c_char = std::ptr::null_mut();
|
||||
let mut out_size: usize = 0;
|
||||
let res = unsafe {
|
||||
BNDemangleMS(
|
||||
|
||||
@@ -16,12 +16,13 @@
|
||||
|
||||
use binaryninjacore_sys::*;
|
||||
|
||||
use crate::string::{BnStr, BnString};
|
||||
use crate::string::BnString;
|
||||
use crate::{BN_FULL_CONFIDENCE, BN_INVALID_EXPR};
|
||||
|
||||
use crate::rc::*;
|
||||
|
||||
use std::convert::From;
|
||||
use std::ffi::CStr;
|
||||
use std::mem;
|
||||
use std::ptr;
|
||||
|
||||
@@ -72,7 +73,7 @@ pub type InstructionTextTokenContext = BNInstructionTextTokenContext;
|
||||
// IndirectImportToken = 69,
|
||||
// ExternalSymbolToken = 70,
|
||||
|
||||
#[repr(C)]
|
||||
#[repr(transparent)]
|
||||
pub struct InstructionTextToken(pub(crate) BNInstructionTextToken);
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
|
||||
@@ -98,11 +99,15 @@ pub enum InstructionTextTokenContents {
|
||||
}
|
||||
|
||||
impl InstructionTextToken {
|
||||
pub(crate) unsafe fn from_raw(raw: &BNInstructionTextToken) -> Self {
|
||||
Self(*raw)
|
||||
pub(crate) unsafe fn from_raw(raw: &BNInstructionTextToken) -> &Self {
|
||||
mem::transmute(raw)
|
||||
}
|
||||
|
||||
pub fn new(text: BnString, contents: InstructionTextTokenContents) -> Self {
|
||||
pub(crate) fn into_raw(self) -> BNInstructionTextToken {
|
||||
mem::ManuallyDrop::new(self).0
|
||||
}
|
||||
|
||||
pub fn new(text: &str, contents: InstructionTextTokenContents) -> Self {
|
||||
let (value, address) = match contents {
|
||||
InstructionTextTokenContents::Integer(v) => (v, 0),
|
||||
InstructionTextTokenContents::PossibleAddress(v)
|
||||
@@ -149,7 +154,7 @@ impl InstructionTextToken {
|
||||
|
||||
InstructionTextToken(BNInstructionTextToken {
|
||||
type_,
|
||||
text: text.into_raw(),
|
||||
text: BnString::new(text).into_raw(),
|
||||
value,
|
||||
width,
|
||||
size: 0,
|
||||
@@ -159,7 +164,7 @@ impl InstructionTextToken {
|
||||
address,
|
||||
typeNames: ptr::null_mut(),
|
||||
namesCount: 0,
|
||||
exprIndex: BN_INVALID_EXPR
|
||||
exprIndex: BN_INVALID_EXPR,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -171,8 +176,8 @@ impl InstructionTextToken {
|
||||
self.0.context = context;
|
||||
}
|
||||
|
||||
pub fn text(&self) -> &BnStr {
|
||||
unsafe { BnStr::from_raw(self.0.text) }
|
||||
pub fn text(&self) -> &str {
|
||||
unsafe { CStr::from_ptr(self.0.text) }.to_str().unwrap()
|
||||
}
|
||||
|
||||
pub fn contents(&self) -> InstructionTextTokenContents {
|
||||
@@ -229,7 +234,7 @@ impl Default for InstructionTextToken {
|
||||
address: 0,
|
||||
typeNames: ptr::null_mut(),
|
||||
namesCount: 0,
|
||||
exprIndex: BN_INVALID_EXPR
|
||||
exprIndex: BN_INVALID_EXPR,
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -248,19 +253,51 @@ impl Clone for InstructionTextToken {
|
||||
confidence: 0xff,
|
||||
typeNames: ptr::null_mut(),
|
||||
namesCount: 0,
|
||||
exprIndex: self.0.exprIndex
|
||||
exprIndex: self.0.exprIndex,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TODO : There is almost certainly a memory leak here - in the case where
|
||||
// `impl CoreOwnedArrayProvider for InstructionTextToken` doesn't get triggered
|
||||
// impl Drop for InstructionTextToken {
|
||||
// fn drop(&mut self) {
|
||||
// let _owned = unsafe { BnString::from_raw(self.0.text) };
|
||||
// }
|
||||
// }
|
||||
impl Drop for InstructionTextToken {
|
||||
fn drop(&mut self) {
|
||||
if !self.0.text.is_null() {
|
||||
let _owned = unsafe { BnString::from_raw(self.0.text) };
|
||||
}
|
||||
if !self.0.typeNames.is_null() && self.0.namesCount != 0 {
|
||||
unsafe { BNFreeStringList(self.0.typeNames, self.0.namesCount) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for InstructionTextToken {
|
||||
type Raw = BNInstructionTextToken;
|
||||
type Context = ();
|
||||
type Wrapped<'a> = &'a Self;
|
||||
}
|
||||
unsafe impl CoreArrayProviderInner for InstructionTextToken {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeInstructionText(raw, count)
|
||||
}
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
core::mem::transmute(raw)
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for Array<InstructionTextToken> {
|
||||
type Raw = BNInstructionTextLine;
|
||||
type Context = ();
|
||||
type Wrapped<'a> = mem::ManuallyDrop<Self>;
|
||||
}
|
||||
unsafe impl CoreArrayProviderInner for Array<InstructionTextToken> {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeInstructionTextLines(raw, count)
|
||||
}
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
mem::ManuallyDrop::new(Self::new(raw.tokens, raw.count, ()))
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct DisassemblyTextLine(pub(crate) BNDisassemblyTextLine);
|
||||
|
||||
impl DisassemblyTextLine {
|
||||
@@ -289,7 +326,7 @@ impl DisassemblyTextLine {
|
||||
unsafe {
|
||||
std::slice::from_raw_parts::<BNInstructionTextToken>(self.0.tokens, self.0.count)
|
||||
.iter()
|
||||
.map(|&x| InstructionTextToken::from_raw(&x))
|
||||
.map(|x| InstructionTextToken::from_raw(x).clone())
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
@@ -306,10 +343,9 @@ impl std::fmt::Display for DisassemblyTextLine {
|
||||
}
|
||||
|
||||
impl From<Vec<InstructionTextToken>> for DisassemblyTextLine {
|
||||
fn from(mut tokens: Vec<InstructionTextToken>) -> Self {
|
||||
tokens.shrink_to_fit();
|
||||
fn from(tokens: Vec<InstructionTextToken>) -> Self {
|
||||
let mut tokens: Box<[_]> = tokens.into();
|
||||
|
||||
assert!(tokens.len() == tokens.capacity());
|
||||
// TODO: let (tokens_pointer, tokens_len, _) = unsafe { tokens.into_raw_parts() }; // Can't use for now...still a rust nightly feature
|
||||
let tokens_pointer = tokens.as_mut_ptr();
|
||||
let tokens_len = tokens.len();
|
||||
@@ -344,12 +380,11 @@ impl From<Vec<InstructionTextToken>> for DisassemblyTextLine {
|
||||
|
||||
impl From<&Vec<&str>> for DisassemblyTextLine {
|
||||
fn from(string_tokens: &Vec<&str>) -> Self {
|
||||
let mut tokens: Vec<BNInstructionTextToken> = Vec::with_capacity(string_tokens.len());
|
||||
tokens.extend(string_tokens.iter().map(|&token| {
|
||||
InstructionTextToken::new(BnString::new(token), InstructionTextTokenContents::Text).0
|
||||
}));
|
||||
let mut tokens: Box<[BNInstructionTextToken]> = string_tokens
|
||||
.iter()
|
||||
.map(|&token| InstructionTextToken::new(token, InstructionTextTokenContents::Text).into_raw())
|
||||
.collect();
|
||||
|
||||
assert!(tokens.len() == tokens.capacity());
|
||||
// let (tokens_pointer, tokens_len, _) = unsafe { tokens.into_raw_parts() }; // Can't use for now...still a rust nighly feature
|
||||
let tokens_pointer = tokens.as_mut_ptr();
|
||||
let tokens_len = tokens.len();
|
||||
@@ -413,12 +448,28 @@ impl Default for DisassemblyTextLine {
|
||||
|
||||
impl Drop for DisassemblyTextLine {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
Vec::from_raw_parts(self.0.tokens, self.0.count, self.0.count);
|
||||
if !self.0.tokens.is_null() {
|
||||
let ptr = core::ptr::slice_from_raw_parts_mut(self.0.tokens, self.0.count);
|
||||
let _ = unsafe { Box::from_raw(ptr) };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for DisassemblyTextLine {
|
||||
type Raw = BNDisassemblyTextLine;
|
||||
type Context = ();
|
||||
type Wrapped<'a> = &'a Self;
|
||||
}
|
||||
|
||||
unsafe impl CoreArrayProviderInner for DisassemblyTextLine {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeDisassemblyTextLines(raw, count)
|
||||
}
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
core::mem::transmute(raw)
|
||||
}
|
||||
}
|
||||
|
||||
pub type DisassemblyOption = BNDisassemblyOption;
|
||||
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
use crate::rc::{
|
||||
Array, CoreArrayProvider, CoreArrayWrapper, CoreOwnedArrayProvider, Ref, RefCountable,
|
||||
};
|
||||
use crate::rc::{Array, CoreArrayProvider, Guard, CoreArrayProviderInner, Ref, RefCountable};
|
||||
use crate::settings::Settings;
|
||||
use crate::string::{BnStr, BnStrCompatible, BnString};
|
||||
use crate::string::{BnStrCompatible, BnString};
|
||||
use binaryninjacore_sys::*;
|
||||
use std::collections::HashMap;
|
||||
use std::ffi::c_void;
|
||||
use std::ffi::{c_void, CStr};
|
||||
use std::os::raw::c_char;
|
||||
use std::ptr::null_mut;
|
||||
use std::slice;
|
||||
@@ -63,19 +61,15 @@ impl DownloadProvider {
|
||||
impl CoreArrayProvider for DownloadProvider {
|
||||
type Raw = *mut BNDownloadProvider;
|
||||
type Context = ();
|
||||
type Wrapped<'a> = Guard<'a, DownloadProvider>;
|
||||
}
|
||||
|
||||
unsafe impl CoreOwnedArrayProvider for DownloadProvider {
|
||||
unsafe impl CoreArrayProviderInner for DownloadProvider {
|
||||
unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
|
||||
BNFreeDownloadProviderList(raw);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> CoreArrayWrapper<'a> for DownloadProvider {
|
||||
type Wrapped = DownloadProvider;
|
||||
|
||||
unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
|
||||
DownloadProvider::from_raw(*raw)
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
Guard::new(DownloadProvider::from_raw(*raw), &())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,8 +264,8 @@ impl DownloadInstance {
|
||||
.zip(response_header_values.iter())
|
||||
{
|
||||
response_headers.insert(
|
||||
BnStr::from_raw(*key).to_string(),
|
||||
BnStr::from_raw(*value).to_string(),
|
||||
CStr::from_ptr(*key).to_str().unwrap().to_owned(),
|
||||
CStr::from_ptr(*value).to_str().unwrap().to_owned(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
196
src/enterprise.rs
Normal file
196
src/enterprise.rs
Normal file
@@ -0,0 +1,196 @@
|
||||
use std::marker::PhantomData;
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
|
||||
use crate::rc::Array;
|
||||
use crate::string::{BnStrCompatible, BnString};
|
||||
|
||||
pub fn server_username() -> BnString {
|
||||
unsafe { BnString::from_raw(binaryninjacore_sys::BNGetEnterpriseServerUsername()) }
|
||||
}
|
||||
|
||||
pub fn server_url() -> BnString {
|
||||
unsafe { BnString::from_raw(binaryninjacore_sys::BNGetEnterpriseServerUrl()) }
|
||||
}
|
||||
|
||||
pub fn set_server_url<S: BnStrCompatible>(url: S) -> Result<(), ()> {
|
||||
let url = url.into_bytes_with_nul();
|
||||
let result = unsafe {
|
||||
binaryninjacore_sys::BNSetEnterpriseServerUrl(url.as_ref().as_ptr() as *const std::os::raw::c_char)
|
||||
};
|
||||
if result {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn server_name() -> BnString {
|
||||
unsafe { BnString::from_raw(binaryninjacore_sys::BNGetEnterpriseServerName()) }
|
||||
}
|
||||
|
||||
pub fn server_id() -> BnString {
|
||||
unsafe { BnString::from_raw(binaryninjacore_sys::BNGetEnterpriseServerId()) }
|
||||
}
|
||||
|
||||
pub fn server_version() -> u64 {
|
||||
unsafe { binaryninjacore_sys::BNGetEnterpriseServerVersion() }
|
||||
}
|
||||
|
||||
pub fn server_build_id() -> BnString {
|
||||
unsafe { BnString::from_raw(binaryninjacore_sys::BNGetEnterpriseServerBuildId()) }
|
||||
}
|
||||
|
||||
pub fn server_token() -> BnString {
|
||||
unsafe { BnString::from_raw(binaryninjacore_sys::BNGetEnterpriseServerToken()) }
|
||||
}
|
||||
|
||||
pub fn license_duration() -> Duration {
|
||||
Duration::from_secs(unsafe { binaryninjacore_sys::BNGetEnterpriseServerLicenseDuration() })
|
||||
}
|
||||
|
||||
pub fn license_expiration_time() -> SystemTime {
|
||||
let m = Duration::from_secs(unsafe {
|
||||
binaryninjacore_sys::BNGetEnterpriseServerLicenseExpirationTime()
|
||||
});
|
||||
UNIX_EPOCH + m
|
||||
}
|
||||
|
||||
pub fn server_reservation_time_limit() -> Duration {
|
||||
Duration::from_secs(unsafe { binaryninjacore_sys::BNGetEnterpriseServerReservationTimeLimit() })
|
||||
}
|
||||
|
||||
pub fn is_server_floating_license() -> bool {
|
||||
unsafe { binaryninjacore_sys::BNIsEnterpriseServerFloatingLicense() }
|
||||
}
|
||||
|
||||
pub fn is_server_license_still_activated() -> bool {
|
||||
unsafe { binaryninjacore_sys::BNIsEnterpriseServerLicenseStillActivated() }
|
||||
}
|
||||
|
||||
pub fn authenticate_server_with_credentials<U, P>(username: U, password: P, remember: bool) -> bool
|
||||
where
|
||||
U: BnStrCompatible,
|
||||
P: BnStrCompatible,
|
||||
{
|
||||
let username = username.into_bytes_with_nul();
|
||||
let password = password.into_bytes_with_nul();
|
||||
unsafe {
|
||||
binaryninjacore_sys::BNAuthenticateEnterpriseServerWithCredentials(
|
||||
username.as_ref().as_ptr() as *const std::os::raw::c_char,
|
||||
password.as_ref().as_ptr() as *const std::os::raw::c_char,
|
||||
remember,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn authenticate_server_with_method<S: BnStrCompatible>(method: S, remember: bool) -> bool {
|
||||
let method = method.into_bytes_with_nul();
|
||||
unsafe {
|
||||
binaryninjacore_sys::BNAuthenticateEnterpriseServerWithMethod(
|
||||
method.as_ref().as_ptr() as *const std::os::raw::c_char,
|
||||
remember,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn connect_server() -> bool {
|
||||
unsafe { binaryninjacore_sys::BNConnectEnterpriseServer() }
|
||||
}
|
||||
|
||||
pub fn deauthenticate_server() -> bool {
|
||||
unsafe { binaryninjacore_sys::BNDeauthenticateEnterpriseServer() }
|
||||
}
|
||||
|
||||
pub fn cancel_server_authentication() {
|
||||
unsafe { binaryninjacore_sys::BNCancelEnterpriseServerAuthentication() }
|
||||
}
|
||||
|
||||
pub fn update_server_license(timeout: Duration) -> bool {
|
||||
unsafe { binaryninjacore_sys::BNUpdateEnterpriseServerLicense(timeout.as_secs()) }
|
||||
}
|
||||
|
||||
pub fn release_server_license() -> bool {
|
||||
unsafe { binaryninjacore_sys::BNReleaseEnterpriseServerLicense() }
|
||||
}
|
||||
|
||||
pub fn is_server_connected() -> bool {
|
||||
unsafe { binaryninjacore_sys::BNIsEnterpriseServerConnected() }
|
||||
}
|
||||
|
||||
pub fn is_server_authenticated() -> bool {
|
||||
unsafe { binaryninjacore_sys::BNIsEnterpriseServerAuthenticated() }
|
||||
}
|
||||
|
||||
pub fn is_server_initialized() -> bool {
|
||||
unsafe { binaryninjacore_sys::BNIsEnterpriseServerInitialized() }
|
||||
}
|
||||
|
||||
pub fn server_last_error() -> BnString {
|
||||
unsafe { BnString::from_raw(binaryninjacore_sys::BNGetEnterpriseServerLastError()) }
|
||||
}
|
||||
|
||||
pub fn server_authentication_methods() -> (Array<BnString>, Array<BnString>) {
|
||||
let mut methods = core::ptr::null_mut();
|
||||
let mut names = core::ptr::null_mut();
|
||||
let count = unsafe {
|
||||
binaryninjacore_sys::BNGetEnterpriseServerAuthenticationMethods(&mut methods, &mut names)
|
||||
};
|
||||
unsafe { (Array::new(methods, count, ()), Array::new(names, count, ())) }
|
||||
}
|
||||
|
||||
// NOTE don't implement Clone, Copy, so each callback can only be
|
||||
// register/unregistered only once
|
||||
#[repr(transparent)]
|
||||
#[derive(Debug)]
|
||||
pub struct EnterpriseServerCallback<'a> {
|
||||
handle: binaryninjacore_sys::BNEnterpriseServerCallbacks,
|
||||
lifetime: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
pub fn register_license_changed_callback<'a, F: FnMut(bool) + 'a>(
|
||||
callback: F,
|
||||
) -> EnterpriseServerCallback<'a> {
|
||||
unsafe extern "C" fn cb_license_status_changed<F: FnMut(bool)>(
|
||||
ctxt: *mut ::std::os::raw::c_void,
|
||||
still_valid: bool,
|
||||
) {
|
||||
let ctxt: &mut F = &mut *(ctxt as *mut F);
|
||||
ctxt(still_valid)
|
||||
}
|
||||
let mut handle = binaryninjacore_sys::BNEnterpriseServerCallbacks {
|
||||
context: Box::leak(Box::new(callback)) as *mut F as *mut core::ffi::c_void,
|
||||
licenseStatusChanged: Some(cb_license_status_changed::<F>),
|
||||
};
|
||||
unsafe { binaryninjacore_sys::BNRegisterEnterpriseServerNotification(&mut handle) }
|
||||
EnterpriseServerCallback {
|
||||
handle,
|
||||
lifetime: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unregister_license_changed_callback(mut callback_handle: EnterpriseServerCallback) {
|
||||
unsafe {
|
||||
binaryninjacore_sys::BNUnregisterEnterpriseServerNotification(&mut callback_handle.handle)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> EnterpriseServerCallback<'a> {
|
||||
/// register the license changed callback
|
||||
pub fn register<F: FnMut(bool) + 'a>(callback: F) -> Self {
|
||||
register_license_changed_callback(callback)
|
||||
}
|
||||
|
||||
/// deregister the license changed callback, equivalent to drop the struct
|
||||
pub fn deregister(self) {
|
||||
// Nothing, just drop self
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for EnterpriseServerCallback<'_> {
|
||||
fn drop(&mut self) {
|
||||
unregister_license_changed_callback(EnterpriseServerCallback {
|
||||
handle: self.handle,
|
||||
lifetime: PhantomData,
|
||||
})
|
||||
}
|
||||
}
|
||||
209
src/externallibrary.rs
Normal file
209
src/externallibrary.rs
Normal file
@@ -0,0 +1,209 @@
|
||||
use core::{ffi, mem, ptr};
|
||||
|
||||
use binaryninjacore_sys::*;
|
||||
|
||||
use crate::project::ProjectFile;
|
||||
use crate::rc::{CoreArrayProvider, CoreArrayProviderInner};
|
||||
use crate::string::{BnStrCompatible, BnString};
|
||||
use crate::symbol::Symbol;
|
||||
|
||||
/// An ExternalLibrary is an abstraction for a library that is optionally backed
|
||||
/// by a [ProjectFile].
|
||||
#[repr(transparent)]
|
||||
pub struct ExternalLibrary {
|
||||
handle: ptr::NonNull<BNExternalLibrary>,
|
||||
}
|
||||
|
||||
impl Drop for ExternalLibrary {
|
||||
fn drop(&mut self) {
|
||||
unsafe { BNFreeExternalLibrary(self.as_raw()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for ExternalLibrary {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
Self::from_raw(ptr::NonNull::new(BNNewExternalLibraryReference(self.as_raw())).unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExternalLibrary {
|
||||
pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNExternalLibrary>) -> Self {
|
||||
Self { handle }
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn ref_from_raw(handle: &*mut BNExternalLibrary) -> &Self {
|
||||
assert!(!handle.is_null());
|
||||
mem::transmute(handle)
|
||||
}
|
||||
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
pub(crate) unsafe fn as_raw(&self) -> &mut BNExternalLibrary {
|
||||
&mut *self.handle.as_ptr()
|
||||
}
|
||||
|
||||
/// Get the name of this external library
|
||||
pub fn name(&self) -> BnString {
|
||||
let result = unsafe { BNExternalLibraryGetName(self.as_raw()) };
|
||||
assert!(!result.is_null());
|
||||
unsafe { BnString::from_raw(result) }
|
||||
}
|
||||
|
||||
/// Get the file backing this external library
|
||||
pub fn backing_file(&self) -> Option<ProjectFile> {
|
||||
let result = unsafe { BNExternalLibraryGetBackingFile(self.as_raw()) };
|
||||
let handle = ptr::NonNull::new(result)?;
|
||||
Some(unsafe { ProjectFile::from_raw(handle) })
|
||||
}
|
||||
|
||||
/// Set the file backing this external library
|
||||
pub fn set_backing_file(&self, file: Option<&ProjectFile>) {
|
||||
let file_handle = file
|
||||
.map(|x| unsafe {x.as_raw() as *mut _})
|
||||
.unwrap_or(ptr::null_mut());
|
||||
unsafe { BNExternalLibrarySetBackingFile(self.as_raw(), file_handle) }
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for ExternalLibrary {
|
||||
type Raw = *mut BNExternalLibrary;
|
||||
type Context = ();
|
||||
type Wrapped<'a> = &'a Self;
|
||||
}
|
||||
|
||||
unsafe impl CoreArrayProviderInner for ExternalLibrary {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeExternalLibraryList(raw, count)
|
||||
}
|
||||
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
Self::ref_from_raw(raw)
|
||||
}
|
||||
}
|
||||
|
||||
/// An ExternalLocation is an association from a source symbol in a binary view
|
||||
/// to a target symbol and/or address in an [ExternalLibrary].
|
||||
#[repr(transparent)]
|
||||
pub struct ExternalLocation {
|
||||
handle: ptr::NonNull<BNExternalLocation>,
|
||||
}
|
||||
|
||||
impl Drop for ExternalLocation {
|
||||
fn drop(&mut self) {
|
||||
unsafe { BNFreeExternalLocation(self.as_raw()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for ExternalLocation {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
Self::from_raw(
|
||||
ptr::NonNull::new(BNNewExternalLocationReference(self.as_raw())).unwrap(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExternalLocation {
|
||||
pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNExternalLocation>) -> Self {
|
||||
Self { handle }
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn ref_from_raw(handle: &*mut BNExternalLocation) -> &Self {
|
||||
assert!(!handle.is_null());
|
||||
mem::transmute(handle)
|
||||
}
|
||||
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
pub(crate) unsafe fn as_raw(&self) -> &mut BNExternalLocation {
|
||||
&mut *self.handle.as_ptr()
|
||||
}
|
||||
|
||||
/// Get the source symbol for this ExternalLocation
|
||||
pub fn source_symbol(&self) -> Symbol {
|
||||
let result = unsafe { BNExternalLocationGetSourceSymbol(self.as_raw()) };
|
||||
assert!(!result.is_null());
|
||||
unsafe { Symbol::from_raw(result) }
|
||||
}
|
||||
|
||||
/// Get the ExternalLibrary that this ExternalLocation targets
|
||||
pub fn library(&self) -> Option<ExternalLibrary> {
|
||||
let result = unsafe { BNExternalLocationGetExternalLibrary(self.as_raw()) };
|
||||
let handle = ptr::NonNull::new(result)?;
|
||||
Some(unsafe { ExternalLibrary::from_raw(handle) })
|
||||
}
|
||||
|
||||
/// Set the ExternalLibrary that this ExternalLocation targets
|
||||
pub fn set_external_library(&self, lib: Option<&ExternalLibrary>) {
|
||||
let lib_handle = lib
|
||||
.map(|x| unsafe {x.as_raw() as *mut _})
|
||||
.unwrap_or(ptr::null_mut());
|
||||
unsafe { BNExternalLocationSetExternalLibrary(self.as_raw(), lib_handle) }
|
||||
}
|
||||
|
||||
/// Check if this ExternalLocation has a target address
|
||||
pub fn has_target_address(&self) -> bool {
|
||||
unsafe { BNExternalLocationHasTargetAddress(self.as_raw()) }
|
||||
}
|
||||
|
||||
/// Check if this ExternalLocation has a target symbol
|
||||
pub fn has_target_symbol(&self) -> bool {
|
||||
unsafe { BNExternalLocationHasTargetSymbol(self.as_raw()) }
|
||||
}
|
||||
|
||||
/// Get the address pointed to by this ExternalLocation, if any
|
||||
pub fn target_address(&self) -> Option<u64> {
|
||||
self.has_target_address()
|
||||
.then(|| unsafe { BNExternalLocationGetTargetAddress(self.as_raw()) })
|
||||
}
|
||||
|
||||
/// Set the address pointed to by this ExternalLocation.
|
||||
/// ExternalLocations must have a valid target address and/or symbol set.
|
||||
pub fn set_target_address(&self, mut address: Option<u64>) -> bool {
|
||||
let address_ptr = address
|
||||
.as_mut()
|
||||
.map(|x| x as *mut u64)
|
||||
.unwrap_or(ptr::null_mut());
|
||||
unsafe { BNExternalLocationSetTargetAddress(self.as_raw(), address_ptr) }
|
||||
}
|
||||
|
||||
/// Get the symbol pointed to by this ExternalLocation, if any
|
||||
pub fn target_symbol(&self) -> Option<BnString> {
|
||||
self.has_target_symbol().then(|| unsafe {
|
||||
let result = BNExternalLocationGetTargetSymbol(self.as_raw());
|
||||
assert!(!result.is_null());
|
||||
BnString::from_raw(result)
|
||||
})
|
||||
}
|
||||
|
||||
/// Set the symbol pointed to by this ExternalLocation.
|
||||
/// ExternalLocations must have a valid target address and/or symbol set.
|
||||
pub fn set_target_symbol<S: BnStrCompatible>(&self, symbol: Option<S>) -> bool {
|
||||
let symbol = symbol
|
||||
.map(|x| x.into_bytes_with_nul().as_ref().as_ptr() as *const ffi::c_char)
|
||||
.unwrap_or(ptr::null_mut());
|
||||
unsafe {
|
||||
BNExternalLocationSetTargetSymbol(
|
||||
self.as_raw(),
|
||||
symbol,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for ExternalLocation {
|
||||
type Raw = *mut BNExternalLocation;
|
||||
type Context = ();
|
||||
type Wrapped<'a> = &'a Self;
|
||||
}
|
||||
|
||||
unsafe impl CoreArrayProviderInner for ExternalLocation {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeExternalLocationList(raw, count)
|
||||
}
|
||||
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
Self::ref_from_raw(raw)
|
||||
}
|
||||
}
|
||||
@@ -27,6 +27,7 @@ use binaryninjacore_sys::{
|
||||
BNIsAnalysisChanged,
|
||||
BNIsBackedByDatabase,
|
||||
//BNSetFileMetadataNavigationHandler,
|
||||
BNGetFileMetadataDatabase,
|
||||
BNIsFileModified,
|
||||
BNMarkFileModified,
|
||||
BNMarkFileSaved,
|
||||
@@ -40,8 +41,10 @@ use binaryninjacore_sys::{
|
||||
BNSetFilename,
|
||||
BNUndo,
|
||||
};
|
||||
use binaryninjacore_sys::{BNCreateDatabaseWithProgress, BNOpenExistingDatabaseWithProgress};
|
||||
|
||||
use crate::binaryview::BinaryView;
|
||||
use crate::database::Database;
|
||||
|
||||
use crate::rc::*;
|
||||
use crate::string::*;
|
||||
@@ -204,16 +207,28 @@ impl FileMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_database<S: BnStrCompatible>(&self, filename: S) -> bool {
|
||||
pub fn create_database<S: BnStrCompatible>(
|
||||
&self,
|
||||
filename: S,
|
||||
progress_func: Option<fn(usize, usize) -> bool>,
|
||||
) -> bool {
|
||||
let filename = filename.into_bytes_with_nul();
|
||||
let filename_ptr = filename.as_ref().as_ptr() as *mut _;
|
||||
let raw = "Raw".into_bytes_with_nul();
|
||||
let raw_ptr = raw.as_ptr() as *mut _;
|
||||
|
||||
unsafe {
|
||||
BNCreateDatabase(
|
||||
BNGetFileViewOfType(self.handle, raw.as_ptr() as *mut _),
|
||||
filename.as_ref().as_ptr() as *mut _,
|
||||
ptr::null_mut() as *mut _,
|
||||
)
|
||||
let handle = unsafe { BNGetFileViewOfType(self.handle, raw_ptr) };
|
||||
match progress_func {
|
||||
None => unsafe { BNCreateDatabase(handle, filename_ptr, ptr::null_mut()) },
|
||||
Some(func) => unsafe {
|
||||
BNCreateDatabaseWithProgress(
|
||||
handle,
|
||||
filename_ptr,
|
||||
func as *mut libc::c_void,
|
||||
Some(cb_progress_func),
|
||||
ptr::null_mut(),
|
||||
)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -244,17 +259,25 @@ impl FileMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn open_database<S: BnStrCompatible>(&self, filename: S) -> Result<Ref<BinaryView>, ()> {
|
||||
pub fn open_database<S: BnStrCompatible>(
|
||||
&self,
|
||||
filename: S,
|
||||
progress_func: Option<fn(usize, usize) -> bool>,
|
||||
) -> Result<Ref<BinaryView>, ()> {
|
||||
let filename = filename.into_bytes_with_nul();
|
||||
let filename_ptr = filename.as_ref().as_ptr() as *mut _;
|
||||
|
||||
let view = unsafe { BNOpenExistingDatabase(self.handle, filename_ptr) };
|
||||
|
||||
// TODO : add optional progress function
|
||||
// let view = match progress_func {
|
||||
// None => BNOpenExistingDatabase(self.handle, filename_ptr),
|
||||
// _ => BNOpenExistingDatabaseWithProgress(self.handle, str(filename), None, ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_ulonglong, ctypes.c_ulonglong)(lambda ctxt, cur, total: progress_func(cur, total)))
|
||||
// };
|
||||
let view = match progress_func {
|
||||
None => unsafe { BNOpenExistingDatabase(self.handle, filename_ptr) },
|
||||
Some(func) => unsafe {
|
||||
BNOpenExistingDatabaseWithProgress(
|
||||
self.handle,
|
||||
filename_ptr,
|
||||
func as *mut libc::c_void,
|
||||
Some(cb_progress_func),
|
||||
)
|
||||
},
|
||||
};
|
||||
|
||||
if view.is_null() {
|
||||
Err(())
|
||||
@@ -262,6 +285,12 @@ impl FileMetadata {
|
||||
Ok(unsafe { BinaryView::from_raw(view) })
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the current database
|
||||
pub fn database(&self) -> Option<Database> {
|
||||
let result = unsafe { BNGetFileMetadataDatabase(self.handle) };
|
||||
ptr::NonNull::new(result).map(|handle| unsafe { Database::from_raw(handle) })
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOwned for FileMetadata {
|
||||
@@ -284,7 +313,11 @@ unsafe impl RefCountable for FileMetadata {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
BNCreateDatabase,
|
||||
BNCreateDatabaseWithProgress,
|
||||
*/
|
||||
unsafe extern "C" fn cb_progress_func(
|
||||
ctxt: *mut ::std::os::raw::c_void,
|
||||
progress: usize,
|
||||
total: usize,
|
||||
) -> bool {
|
||||
let func: fn(usize, usize) -> bool = core::mem::transmute(ctxt);
|
||||
func(progress, total)
|
||||
}
|
||||
|
||||
@@ -68,7 +68,7 @@ impl<'a> FlowGraphNode<'a> {
|
||||
unsafe { FlowGraphNode::from_raw(BNCreateFlowGraphNode(graph.handle)) }
|
||||
}
|
||||
|
||||
pub fn set_disassembly_lines(&self, lines: &'a Vec<DisassemblyTextLine>) {
|
||||
pub fn set_disassembly_lines(&self, lines: &'a [DisassemblyTextLine]) {
|
||||
unsafe {
|
||||
BNSetFlowGraphNodeLines(self.handle, lines.as_ptr() as *mut _, lines.len());
|
||||
// BNFreeDisassemblyTextLines(lines.as_ptr() as *mut _, lines.len()); // Shouldn't need...would be a double free?
|
||||
@@ -79,7 +79,7 @@ impl<'a> FlowGraphNode<'a> {
|
||||
let lines = lines
|
||||
.iter()
|
||||
.map(|&line| DisassemblyTextLine::from(&vec![line]))
|
||||
.collect();
|
||||
.collect::<Vec<_>>();
|
||||
self.set_disassembly_lines(&lines);
|
||||
}
|
||||
|
||||
@@ -114,8 +114,6 @@ impl<'a> ToOwned for FlowGraphNode<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO : FlowGraph are RefCounted objects, this needs to be changed to only return Refs to FlowGraph
|
||||
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct FlowGraph {
|
||||
pub(crate) handle: *mut BNFlowGraph,
|
||||
@@ -126,8 +124,8 @@ impl FlowGraph {
|
||||
Self { handle: raw }
|
||||
}
|
||||
|
||||
pub fn new() -> Self {
|
||||
unsafe { FlowGraph::from_raw(BNCreateFlowGraph()) }
|
||||
pub fn new() -> Ref<Self> {
|
||||
unsafe { Ref::new(FlowGraph::from_raw(BNCreateFlowGraph())) }
|
||||
}
|
||||
|
||||
pub fn append(&self, node: &FlowGraphNode) -> usize {
|
||||
|
||||
1899
src/function.rs
1899
src/function.rs
File diff suppressed because it is too large
Load Diff
@@ -14,9 +14,8 @@
|
||||
|
||||
use crate::{
|
||||
binaryview,
|
||||
metadata::Metadata,
|
||||
rc::{self, Ref},
|
||||
string::BnStrCompatible,
|
||||
rc,
|
||||
string::{BnStrCompatible, IntoJson},
|
||||
};
|
||||
|
||||
use std::env;
|
||||
@@ -59,6 +58,11 @@ fn binja_path() -> PathBuf {
|
||||
let path = CStr::from_ptr(info.dli_fname);
|
||||
let path = OsStr::from_bytes(path.to_bytes());
|
||||
let mut path = PathBuf::from(path);
|
||||
while path.is_symlink() {
|
||||
path = path
|
||||
.read_link()
|
||||
.expect("Failed to find libbinaryninjacore path!");
|
||||
}
|
||||
|
||||
path.pop();
|
||||
path
|
||||
@@ -95,13 +99,18 @@ pub fn shutdown() {
|
||||
unsafe { binaryninjacore_sys::BNShutdown() };
|
||||
}
|
||||
|
||||
pub fn is_shutdown_requested() -> bool {
|
||||
unsafe { binaryninjacore_sys::BNIsShutdownRequested() }
|
||||
}
|
||||
|
||||
/// Prelued-postlued helper function (calls [`init`] and [`shutdown`] for you)
|
||||
/// ```rust
|
||||
/// ```no_run
|
||||
/// # use binaryninja::binaryview::BinaryViewExt;
|
||||
/// binaryninja::headless::script_helper(|| {
|
||||
/// binaryninja::load("/bin/cat")
|
||||
/// .expect("Couldn't open `/bin/cat`")
|
||||
/// .iter()
|
||||
/// .for_each(|func| println!(" `{}`", func.symbol().full_name()));
|
||||
/// let cat = binaryninja::load("/bin/cat").expect("Couldn't open `/bin/cat`");
|
||||
/// for function in cat.functions().iter() {
|
||||
/// println!(" `{}`", function.symbol().full_name());
|
||||
/// }
|
||||
/// });
|
||||
/// ```
|
||||
pub fn script_helper(func: fn()) {
|
||||
@@ -119,7 +128,7 @@ impl Session {
|
||||
Self {}
|
||||
}
|
||||
|
||||
/// ```rust
|
||||
/// ```no_run
|
||||
/// let headless_session = binaryninja::headless::Session::new();
|
||||
///
|
||||
/// let bv = headless_session.load("/bin/cat").expect("Couldn't open `/bin/cat`");
|
||||
@@ -128,18 +137,23 @@ impl Session {
|
||||
crate::load(filename)
|
||||
}
|
||||
|
||||
/// ```rust
|
||||
/// let settings = [("analysis.linearSweep.autorun", false)].into();
|
||||
/// ```no_run
|
||||
/// use binaryninja::{metadata::Metadata, rc::Ref};
|
||||
/// use std::collections::HashMap;
|
||||
///
|
||||
/// let settings: Ref<Metadata> = HashMap::from([
|
||||
/// ("analysis.linearSweep.autorun", false.into()),
|
||||
/// ]).into();
|
||||
/// let headless_session = binaryninja::headless::Session::new();
|
||||
///
|
||||
/// let bv = headless_session.load_with_options("/bin/cat", true, Some(settings))
|
||||
/// .expect("Couldn't open `/bin/cat`");
|
||||
/// ```
|
||||
pub fn load_with_options(
|
||||
pub fn load_with_options<O: IntoJson>(
|
||||
&self,
|
||||
filename: &str,
|
||||
update_analysis_and_wait: bool,
|
||||
options: Option<Ref<Metadata>>,
|
||||
options: Option<O>,
|
||||
) -> Option<rc::Ref<binaryview::BinaryView>> {
|
||||
crate::load_with_options(filename, update_analysis_and_wait, options)
|
||||
}
|
||||
|
||||
@@ -2,8 +2,10 @@ use std::hash::{Hash, Hasher};
|
||||
|
||||
use binaryninjacore_sys::BNFreeHighLevelILFunction;
|
||||
use binaryninjacore_sys::BNGetHighLevelILBasicBlockList;
|
||||
use binaryninjacore_sys::BNGetHighLevelILIndexForInstruction;
|
||||
use binaryninjacore_sys::BNGetHighLevelILInstructionCount;
|
||||
use binaryninjacore_sys::BNGetHighLevelILOwnerFunction;
|
||||
use binaryninjacore_sys::BNGetHighLevelILRootExpr;
|
||||
use binaryninjacore_sys::BNGetHighLevelILSSAForm;
|
||||
use binaryninjacore_sys::BNHighLevelILFunction;
|
||||
use binaryninjacore_sys::BNNewHighLevelILFunctionReference;
|
||||
@@ -52,6 +54,29 @@ impl HighLevelILFunction {
|
||||
self.instruction_from_idx(expr_idx).lift()
|
||||
}
|
||||
|
||||
pub fn instruction_from_instruction_idx(&self, instr_idx: usize) -> HighLevelILInstruction {
|
||||
HighLevelILInstruction::new(self.as_non_ast(), unsafe {
|
||||
BNGetHighLevelILIndexForInstruction(self.handle, instr_idx)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn lifted_instruction_from_instruction_idx(
|
||||
&self,
|
||||
instr_idx: usize,
|
||||
) -> HighLevelILLiftedInstruction {
|
||||
self.instruction_from_instruction_idx(instr_idx).lift()
|
||||
}
|
||||
|
||||
pub fn root(&self) -> HighLevelILInstruction {
|
||||
HighLevelILInstruction::new(self.as_ast(), unsafe {
|
||||
BNGetHighLevelILRootExpr(self.handle)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn lifted_root(&self) -> HighLevelILLiftedInstruction {
|
||||
self.root().lift()
|
||||
}
|
||||
|
||||
pub fn instruction_count(&self) -> usize {
|
||||
unsafe { BNGetHighLevelILInstructionCount(self.handle) }
|
||||
}
|
||||
@@ -81,6 +106,22 @@ impl HighLevelILFunction {
|
||||
|
||||
unsafe { Array::new(blocks, count, context) }
|
||||
}
|
||||
|
||||
pub fn as_ast(&self) -> Ref<HighLevelILFunction> {
|
||||
Self {
|
||||
handle: self.handle,
|
||||
full_ast: true,
|
||||
}
|
||||
.to_owned()
|
||||
}
|
||||
|
||||
pub fn as_non_ast(&self) -> Ref<HighLevelILFunction> {
|
||||
Self {
|
||||
handle: self.handle,
|
||||
full_ast: false,
|
||||
}
|
||||
.to_owned()
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOwned for HighLevelILFunction {
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
use binaryninjacore_sys::BNFromVariableIdentifier;
|
||||
use binaryninjacore_sys::BNGetHighLevelILByIndex;
|
||||
use binaryninjacore_sys::BNHighLevelILOperation;
|
||||
|
||||
use crate::architecture::CoreIntrinsic;
|
||||
use crate::operand_iter::OperandIter;
|
||||
use crate::rc::Ref;
|
||||
use crate::types::{
|
||||
ConstantData, ILIntrinsic, RegisterValue, RegisterValueType, SSAVariable, Variable,
|
||||
};
|
||||
use crate::types::{ConstantData, RegisterValue, RegisterValueType, SSAVariable, Variable};
|
||||
|
||||
use super::operation::*;
|
||||
use super::{HighLevelILFunction, HighLevelILLiftedInstruction, HighLevelILLiftedInstructionKind};
|
||||
@@ -15,6 +13,8 @@ use super::{HighLevelILFunction, HighLevelILLiftedInstruction, HighLevelILLifted
|
||||
pub struct HighLevelILInstruction {
|
||||
pub function: Ref<HighLevelILFunction>,
|
||||
pub address: u64,
|
||||
pub index: usize,
|
||||
pub size: usize,
|
||||
pub kind: HighLevelILInstructionKind,
|
||||
}
|
||||
|
||||
@@ -144,8 +144,8 @@ pub enum HighLevelILInstructionKind {
|
||||
DoWhileSsa(WhileSsa),
|
||||
}
|
||||
impl HighLevelILInstruction {
|
||||
pub(crate) fn new(function: Ref<HighLevelILFunction>, idx: usize) -> Self {
|
||||
let op = unsafe { BNGetHighLevelILByIndex(function.handle, idx, function.full_ast) };
|
||||
pub(crate) fn new(function: Ref<HighLevelILFunction>, index: usize) -> Self {
|
||||
let op = unsafe { BNGetHighLevelILByIndex(function.handle, index, function.full_ast) };
|
||||
use BNHighLevelILOperation::*;
|
||||
use HighLevelILInstructionKind as Op;
|
||||
let kind = match op.operation {
|
||||
@@ -610,8 +610,8 @@ impl HighLevelILInstruction {
|
||||
body: op.operands[1] as usize,
|
||||
}),
|
||||
HLIL_DO_WHILE => Op::DoWhile(While {
|
||||
condition: op.operands[0] as usize,
|
||||
body: op.operands[1] as usize,
|
||||
body: op.operands[0] as usize,
|
||||
condition: op.operands[1] as usize,
|
||||
}),
|
||||
HLIL_WHILE_SSA => Op::WhileSsa(WhileSsa {
|
||||
condition_phi: op.operands[0] as usize,
|
||||
@@ -627,6 +627,8 @@ impl HighLevelILInstruction {
|
||||
Self {
|
||||
function,
|
||||
address: op.address,
|
||||
index,
|
||||
size: op.size,
|
||||
kind,
|
||||
}
|
||||
}
|
||||
@@ -809,11 +811,11 @@ impl HighLevelILInstruction {
|
||||
cond_false: self.lift_operand(op.cond_false),
|
||||
}),
|
||||
Intrinsic(op) => Lifted::Intrinsic(LiftedIntrinsic {
|
||||
intrinsic: ILIntrinsic::new(self.function.get_function().arch(), op.intrinsic),
|
||||
intrinsic: CoreIntrinsic(self.function.get_function().arch().0, op.intrinsic),
|
||||
params: self.lift_instruction_list(op.first_param, op.num_params),
|
||||
}),
|
||||
IntrinsicSsa(op) => Lifted::IntrinsicSsa(LiftedIntrinsicSsa {
|
||||
intrinsic: ILIntrinsic::new(self.function.get_function().arch(), op.intrinsic),
|
||||
intrinsic: CoreIntrinsic(self.function.get_function().arch().0, op.intrinsic),
|
||||
params: self.lift_instruction_list(op.first_param, op.num_params),
|
||||
dest_memory: op.dest_memory,
|
||||
src_memory: op.src_memory,
|
||||
@@ -875,6 +877,8 @@ impl HighLevelILInstruction {
|
||||
HighLevelILLiftedInstruction {
|
||||
function: self.function.clone(),
|
||||
address: self.address,
|
||||
index: self.index,
|
||||
size: self.size,
|
||||
kind,
|
||||
}
|
||||
}
|
||||
@@ -979,7 +983,7 @@ fn get_float(value: u64, size: usize) -> f64 {
|
||||
}
|
||||
|
||||
fn get_var(id: u64) -> Variable {
|
||||
unsafe { Variable::from_raw(BNFromVariableIdentifier(id)) }
|
||||
unsafe { Variable::from_identifier(id) }
|
||||
}
|
||||
|
||||
fn get_member_index(idx: u64) -> Option<usize> {
|
||||
|
||||
138
src/hlil/lift.rs
138
src/hlil/lift.rs
@@ -1,7 +1,9 @@
|
||||
use super::{operation::*, HighLevelILFunction};
|
||||
use super::operation::*;
|
||||
use super::HighLevelILFunction;
|
||||
|
||||
use crate::architecture::CoreIntrinsic;
|
||||
use crate::rc::Ref;
|
||||
use crate::types::{ConstantData, ILIntrinsic, SSAVariable, Variable};
|
||||
use crate::types::{ConstantData, SSAVariable, Variable};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum HighLevelILLiftedOperand {
|
||||
@@ -11,7 +13,7 @@ pub enum HighLevelILLiftedOperand {
|
||||
Float(f64),
|
||||
Int(u64),
|
||||
IntList(Vec<u64>),
|
||||
Intrinsic(ILIntrinsic),
|
||||
Intrinsic(CoreIntrinsic),
|
||||
Label(GotoLabel),
|
||||
MemberIndex(Option<usize>),
|
||||
Var(Variable),
|
||||
@@ -23,6 +25,8 @@ pub enum HighLevelILLiftedOperand {
|
||||
pub struct HighLevelILLiftedInstruction {
|
||||
pub function: Ref<HighLevelILFunction>,
|
||||
pub address: u64,
|
||||
pub index: usize,
|
||||
pub size: usize,
|
||||
pub kind: HighLevelILLiftedInstructionKind,
|
||||
}
|
||||
|
||||
@@ -153,6 +157,134 @@ pub enum HighLevelILLiftedInstructionKind {
|
||||
}
|
||||
|
||||
impl HighLevelILLiftedInstruction {
|
||||
pub fn name(&self) -> &'static str {
|
||||
use HighLevelILLiftedInstructionKind::*;
|
||||
match self.kind {
|
||||
Nop => "Nop",
|
||||
Break => "Break",
|
||||
Continue => "Continue",
|
||||
Noret => "Noret",
|
||||
Unreachable => "Unreachable",
|
||||
Bp => "Bp",
|
||||
Undef => "Undef",
|
||||
Unimpl => "Unimpl",
|
||||
Adc(_) => "Adc",
|
||||
Sbb(_) => "Sbb",
|
||||
Rlc(_) => "Rlc",
|
||||
Rrc(_) => "Rrc",
|
||||
Add(_) => "Add",
|
||||
Sub(_) => "Sub",
|
||||
And(_) => "And",
|
||||
Or(_) => "Or",
|
||||
Xor(_) => "Xor",
|
||||
Lsl(_) => "Lsl",
|
||||
Lsr(_) => "Lsr",
|
||||
Asr(_) => "Asr",
|
||||
Rol(_) => "Rol",
|
||||
Ror(_) => "Ror",
|
||||
Mul(_) => "Mul",
|
||||
MuluDp(_) => "MuluDp",
|
||||
MulsDp(_) => "MulsDp",
|
||||
Divu(_) => "Divu",
|
||||
DivuDp(_) => "DivuDp",
|
||||
Divs(_) => "Divs",
|
||||
DivsDp(_) => "DivsDp",
|
||||
Modu(_) => "Modu",
|
||||
ModuDp(_) => "ModuDp",
|
||||
Mods(_) => "Mods",
|
||||
ModsDp(_) => "ModsDp",
|
||||
CmpE(_) => "CmpE",
|
||||
CmpNe(_) => "CmpNe",
|
||||
CmpSlt(_) => "CmpSlt",
|
||||
CmpUlt(_) => "CmpUlt",
|
||||
CmpSle(_) => "CmpSle",
|
||||
CmpUle(_) => "CmpUle",
|
||||
CmpSge(_) => "CmpSge",
|
||||
CmpUge(_) => "CmpUge",
|
||||
CmpSgt(_) => "CmpSgt",
|
||||
CmpUgt(_) => "CmpUgt",
|
||||
TestBit(_) => "TestBit",
|
||||
AddOverflow(_) => "AddOverflow",
|
||||
Fadd(_) => "Fadd",
|
||||
Fsub(_) => "Fsub",
|
||||
Fmul(_) => "Fmul",
|
||||
Fdiv(_) => "Fdiv",
|
||||
FcmpE(_) => "FcmpE",
|
||||
FcmpNe(_) => "FcmpNe",
|
||||
FcmpLt(_) => "FcmpLt",
|
||||
FcmpLe(_) => "FcmpLe",
|
||||
FcmpGe(_) => "FcmpGe",
|
||||
FcmpGt(_) => "FcmpGt",
|
||||
FcmpO(_) => "FcmpO",
|
||||
FcmpUo(_) => "FcmpUo",
|
||||
ArrayIndex(_) => "ArrayIndex",
|
||||
ArrayIndexSsa(_) => "ArrayIndexSsa",
|
||||
Assign(_) => "Assign",
|
||||
AssignMemSsa(_) => "AssignMemSsa",
|
||||
AssignUnpack(_) => "AssignUnpack",
|
||||
AssignUnpackMemSsa(_) => "AssignUnpackMemSsa",
|
||||
Block(_) => "Block",
|
||||
Call(_) => "Call",
|
||||
Tailcall(_) => "Tailcall",
|
||||
CallSsa(_) => "CallSsa",
|
||||
Case(_) => "Case",
|
||||
Const(_) => "Const",
|
||||
ConstPtr(_) => "ConstPtr",
|
||||
Import(_) => "Import",
|
||||
ConstData(_) => "ConstData",
|
||||
Deref(_) => "Deref",
|
||||
AddressOf(_) => "AddressOf",
|
||||
Neg(_) => "Neg",
|
||||
Not(_) => "Not",
|
||||
Sx(_) => "Sx",
|
||||
Zx(_) => "Zx",
|
||||
LowPart(_) => "LowPart",
|
||||
BoolToInt(_) => "BoolToInt",
|
||||
UnimplMem(_) => "UnimplMem",
|
||||
Fsqrt(_) => "Fsqrt",
|
||||
Fneg(_) => "Fneg",
|
||||
Fabs(_) => "Fabs",
|
||||
FloatToInt(_) => "FloatToInt",
|
||||
IntToFloat(_) => "IntToFloat",
|
||||
FloatConv(_) => "FloatConv",
|
||||
RoundToInt(_) => "RoundToInt",
|
||||
Floor(_) => "Floor",
|
||||
Ceil(_) => "Ceil",
|
||||
Ftrunc(_) => "Ftrunc",
|
||||
DerefFieldSsa(_) => "DerefFieldSsa",
|
||||
DerefSsa(_) => "DerefSsa",
|
||||
ExternPtr(_) => "ExternPtr",
|
||||
FloatConst(_) => "FloatConst",
|
||||
For(_) => "For",
|
||||
ForSsa(_) => "ForSsa",
|
||||
Goto(_) => "Goto",
|
||||
Label(_) => "Label",
|
||||
If(_) => "If",
|
||||
Intrinsic(_) => "Intrinsic",
|
||||
IntrinsicSsa(_) => "IntrinsicSsa",
|
||||
Jump(_) => "Jump",
|
||||
MemPhi(_) => "MemPhi",
|
||||
Ret(_) => "Ret",
|
||||
Split(_) => "Split",
|
||||
StructField(_) => "StructField",
|
||||
DerefField(_) => "DerefField",
|
||||
Switch(_) => "Switch",
|
||||
Syscall(_) => "Syscall",
|
||||
SyscallSsa(_) => "SyscallSsa",
|
||||
Trap(_) => "Trap",
|
||||
VarDeclare(_) => "VarDeclare",
|
||||
Var(_) => "Var",
|
||||
VarInit(_) => "VarInit",
|
||||
VarInitSsa(_) => "VarInitSsa",
|
||||
VarPhi(_) => "VarPhi",
|
||||
VarSsa(_) => "VarSsa",
|
||||
While(_) => "While",
|
||||
DoWhile(_) => "DoWhile",
|
||||
WhileSsa(_) => "WhileSsa",
|
||||
DoWhileSsa(_) => "DoWhileSsa",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn operands(&self) -> Vec<(&'static str, HighLevelILLiftedOperand)> {
|
||||
use HighLevelILLiftedInstructionKind::*;
|
||||
use HighLevelILLiftedOperand as Operand;
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
use binaryninjacore_sys::BNGetGotoLabelName;
|
||||
|
||||
use crate::architecture::CoreIntrinsic;
|
||||
use crate::function::Function;
|
||||
use crate::rc::Ref;
|
||||
use crate::types::{ConstantData, ILIntrinsic, SSAVariable, Variable};
|
||||
use crate::string::BnString;
|
||||
use crate::types::{ConstantData, SSAVariable, Variable};
|
||||
|
||||
use super::HighLevelILLiftedInstruction;
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
pub struct GotoLabel {
|
||||
pub(crate) function: Ref<Function>,
|
||||
pub(crate) target: u64,
|
||||
pub target: u64,
|
||||
}
|
||||
|
||||
impl GotoLabel {
|
||||
pub fn name(&self) -> &str {
|
||||
let raw_str = unsafe { BNGetGotoLabelName(self.function.handle, self.target) };
|
||||
let c_str = unsafe { core::ffi::CStr::from_ptr(raw_str) };
|
||||
c_str.to_str().unwrap()
|
||||
pub fn name(&self) -> BnString {
|
||||
unsafe { BnString::from_raw(BNGetGotoLabelName(self.function.handle, self.target)) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,7 +320,7 @@ pub struct Intrinsic {
|
||||
}
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct LiftedIntrinsic {
|
||||
pub intrinsic: ILIntrinsic,
|
||||
pub intrinsic: CoreIntrinsic,
|
||||
pub params: Vec<HighLevelILLiftedInstruction>,
|
||||
}
|
||||
|
||||
@@ -335,7 +335,7 @@ pub struct IntrinsicSsa {
|
||||
}
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct LiftedIntrinsicSsa {
|
||||
pub intrinsic: ILIntrinsic,
|
||||
pub intrinsic: CoreIntrinsic,
|
||||
pub params: Vec<HighLevelILLiftedInstruction>,
|
||||
pub dest_memory: u64,
|
||||
pub src_memory: u64,
|
||||
|
||||
@@ -16,12 +16,13 @@
|
||||
|
||||
use binaryninjacore_sys::*;
|
||||
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::{c_char, c_void};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use crate::binaryview::BinaryView;
|
||||
use crate::rc::Ref;
|
||||
use crate::string::{BnStr, BnStrCompatible, BnString};
|
||||
use crate::string::{BnStrCompatible, BnString};
|
||||
|
||||
pub fn get_text_line_input(prompt: &str, title: &str) -> Option<String> {
|
||||
let mut value: *mut libc::c_char = std::ptr::null_mut();
|
||||
@@ -295,7 +296,9 @@ impl FormInputBuilder {
|
||||
result.type_ = BNFormInputFieldType::AddressFormField;
|
||||
result.prompt = prompt.as_ref().as_ptr() as *const c_char;
|
||||
if let Some(view) = view {
|
||||
result.view = view.handle;
|
||||
// the view is being moved into result, there is no need to clone
|
||||
// and drop is intentionally being avoided with `Ref::into_raw`
|
||||
result.view = unsafe { Ref::into_raw(view) }.handle;
|
||||
}
|
||||
result.currentAddress = current_address.unwrap_or(0);
|
||||
result.hasDefault = default.is_some();
|
||||
@@ -448,8 +451,10 @@ impl FormInputBuilder {
|
||||
///
|
||||
/// This API is flexible and works both in the UI via a pop-up dialog and on the command-line.
|
||||
///
|
||||
/// ```
|
||||
/// let responses = interaction::FormInputBuilder::new()
|
||||
/// ```no_run
|
||||
/// # use binaryninja::interaction::FormInputBuilder;
|
||||
/// # use binaryninja::interaction::FormResponses;
|
||||
/// let responses = FormInputBuilder::new()
|
||||
/// .text_field("First Name", None)
|
||||
/// .text_field("Last Name", None)
|
||||
/// .choice_field(
|
||||
@@ -466,15 +471,19 @@ impl FormInputBuilder {
|
||||
/// .get_form_input("Form Title");
|
||||
///
|
||||
/// let food = match responses[2] {
|
||||
/// Index(0) => "Pizza",
|
||||
/// Index(1) => "Also Pizza",
|
||||
/// Index(2) => "Also Pizza",
|
||||
/// Index(3) => "Wrong Answer",
|
||||
/// FormResponses::Index(0) => "Pizza",
|
||||
/// FormResponses::Index(1) => "Also Pizza",
|
||||
/// FormResponses::Index(2) => "Also Pizza",
|
||||
/// FormResponses::Index(3) => "Wrong Answer",
|
||||
/// _ => panic!("This person doesn't like pizza?!?"),
|
||||
/// };
|
||||
///
|
||||
/// let interaction::FormResponses::String(last_name) = responses[0];
|
||||
/// let interaction::FormResponses::String(first_name) = responses[1];
|
||||
/// let FormResponses::String(last_name) = &responses[0] else {
|
||||
/// unreachable!()
|
||||
/// };
|
||||
/// let FormResponses::String(first_name) = &responses[1] else {
|
||||
/// unreachable!()
|
||||
/// };
|
||||
///
|
||||
/// println!("{} {} likes {}", &first_name, &last_name, food);
|
||||
/// ```
|
||||
@@ -499,7 +508,10 @@ impl FormInputBuilder {
|
||||
| BNFormInputFieldType::SaveFileNameFormField
|
||||
| BNFormInputFieldType::DirectoryNameFormField => {
|
||||
FormResponses::String(unsafe {
|
||||
BnStr::from_raw(form_field.stringResult).to_string()
|
||||
CStr::from_ptr(form_field.stringResult)
|
||||
.to_str()
|
||||
.unwrap()
|
||||
.to_owned()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
169
src/lib.rs
169
src/lib.rs
@@ -49,7 +49,7 @@
|
||||
//!
|
||||
//! Create a new library (`cargo new --lib <plugin-name>`) and include the following in your `Cargo.toml`:
|
||||
//!
|
||||
//! ```
|
||||
//! ```toml
|
||||
//! [lib]
|
||||
//! crate-type = ["cdylib"]
|
||||
//!
|
||||
@@ -73,21 +73,19 @@
|
||||
//!
|
||||
//! ### `main.rs`
|
||||
//! Standalone binaries need to initialize Binary Ninja before they can work. You can do this through [`headless::Session`], [`headless::script_helper`], or [`headless::init()`] at start and [`headless::shutdown()`] at shutdown.
|
||||
//! ```rust
|
||||
//! fn main() {
|
||||
//! // This loads all the core architecture, platform, etc plugins
|
||||
//! // Standalone executables need to call this, but plugins do not
|
||||
//! let headless_session = binaryninja::headless::Session::new();
|
||||
//! ```no_run
|
||||
//! // This loads all the core architecture, platform, etc plugins
|
||||
//! // Standalone executables need to call this, but plugins do not
|
||||
//! let headless_session = binaryninja::headless::Session::new();
|
||||
//!
|
||||
//! println!("Loading binary...");
|
||||
//! let bv = headless_session.load("/bin/cat").expect("Couldn't open `/bin/cat`");
|
||||
//! println!("Loading binary...");
|
||||
//! let bv = headless_session.load("/bin/cat").expect("Couldn't open `/bin/cat`");
|
||||
//!
|
||||
//! // Your code here...
|
||||
//! }
|
||||
//! // Your code here...
|
||||
//! ```
|
||||
//!
|
||||
//! ### `Cargo.toml`
|
||||
//! ```
|
||||
//! ```toml
|
||||
//! [dependencies]
|
||||
//! binaryninja = { git = "https://github.com/Vector35/binaryninja-api.git", branch = "dev"}
|
||||
//! ```
|
||||
@@ -135,11 +133,15 @@ pub mod binarywriter;
|
||||
pub mod callingconvention;
|
||||
pub mod command;
|
||||
pub mod custombinaryview;
|
||||
pub mod database;
|
||||
pub mod databuffer;
|
||||
pub mod debuginfo;
|
||||
pub mod demangle;
|
||||
pub mod disassembly;
|
||||
pub mod enterprise;
|
||||
pub mod component;
|
||||
pub mod downloadprovider;
|
||||
pub mod externallibrary;
|
||||
pub mod fileaccessor;
|
||||
pub mod filemetadata;
|
||||
pub mod flowgraph;
|
||||
@@ -154,6 +156,7 @@ pub mod logger;
|
||||
pub mod metadata;
|
||||
pub mod mlil;
|
||||
pub mod platform;
|
||||
pub mod project;
|
||||
pub mod rc;
|
||||
pub mod references;
|
||||
pub mod relocation;
|
||||
@@ -164,7 +167,10 @@ pub mod string;
|
||||
pub mod symbol;
|
||||
pub mod tags;
|
||||
pub mod templatesimplifier;
|
||||
pub mod typelibrary;
|
||||
pub mod typearchive;
|
||||
pub mod types;
|
||||
pub mod update;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
@@ -173,8 +179,8 @@ pub use binaryninjacore_sys::BNEndianness as Endianness;
|
||||
use binaryview::BinaryView;
|
||||
use metadata::Metadata;
|
||||
use metadata::MetadataType;
|
||||
use rc::Ref;
|
||||
use string::BnStrCompatible;
|
||||
use string::IntoJson;
|
||||
|
||||
// Commented out to suppress unused warnings
|
||||
// const BN_MAX_INSTRUCTION_LENGTH: u64 = 256;
|
||||
@@ -199,14 +205,14 @@ const BN_INVALID_EXPR: usize = usize::MAX;
|
||||
/// The main way to open and load files into Binary Ninja. Make sure you've properly initialized the core before calling this function. See [`crate::headless::init()`]
|
||||
pub fn load<S: BnStrCompatible>(filename: S) -> Option<rc::Ref<binaryview::BinaryView>> {
|
||||
let filename = filename.into_bytes_with_nul();
|
||||
let metadata = Metadata::new_of_type(MetadataType::KeyValueDataType);
|
||||
let options = "\x00";
|
||||
|
||||
let handle = unsafe {
|
||||
binaryninjacore_sys::BNLoadFilename(
|
||||
filename.as_ref().as_ptr() as *mut _,
|
||||
true,
|
||||
options.as_ptr() as *mut core::ffi::c_char,
|
||||
None,
|
||||
metadata.handle,
|
||||
)
|
||||
};
|
||||
|
||||
@@ -219,31 +225,83 @@ pub fn load<S: BnStrCompatible>(filename: S) -> Option<rc::Ref<binaryview::Binar
|
||||
|
||||
/// The main way to open and load files (with options) into Binary Ninja. Make sure you've properly initialized the core before calling this function. See [`crate::headless::init()`]
|
||||
///
|
||||
/// ```rust
|
||||
/// let settings = [("analysis.linearSweep.autorun", false)].into();
|
||||
/// <div class="warning">Strict JSON doesn't support single quotes for strings, so you'll need to either use a raw strings (<code>f#"{"setting": "value"}"#</code>) or escape double quotes (<code>"{\"setting\": \"value\"}"</code>). Or use <code>serde_json::json</code>.</div>
|
||||
///
|
||||
/// let bv = binaryninja::load_with_options("/bin/cat", true, Some(settings))
|
||||
/// ```no_run
|
||||
/// # // Mock implementation of json! macro for documentation purposes
|
||||
/// # macro_rules! json {
|
||||
/// # ($($arg:tt)*) => {
|
||||
/// # stringify!($($arg)*)
|
||||
/// # };
|
||||
/// # }
|
||||
/// use binaryninja::{metadata::Metadata, rc::Ref};
|
||||
/// use std::collections::HashMap;
|
||||
///
|
||||
/// let bv = binaryninja::load_with_options("/bin/cat", true, Some(json!("analysis.linearSweep.autorun": false).to_string()))
|
||||
/// .expect("Couldn't open `/bin/cat`");
|
||||
/// ```
|
||||
pub fn load_with_options<S: BnStrCompatible>(
|
||||
pub fn load_with_options<S: BnStrCompatible, O: IntoJson>(
|
||||
filename: S,
|
||||
update_analysis_and_wait: bool,
|
||||
options: Option<Ref<Metadata>>,
|
||||
options: Option<O>,
|
||||
) -> Option<rc::Ref<binaryview::BinaryView>> {
|
||||
let filename = filename.into_bytes_with_nul();
|
||||
|
||||
let options_or_default = if let Some(opt) = options {
|
||||
opt
|
||||
opt.get_json_string()
|
||||
.ok()?
|
||||
.into_bytes_with_nul()
|
||||
.as_ref()
|
||||
.to_vec()
|
||||
} else {
|
||||
Metadata::new_of_type(MetadataType::KeyValueDataType)
|
||||
.get_json_string()
|
||||
.ok()?
|
||||
.as_ref()
|
||||
.to_vec()
|
||||
};
|
||||
|
||||
let handle = unsafe {
|
||||
binaryninjacore_sys::BNLoadFilename(
|
||||
filename.as_ref().as_ptr() as *mut _,
|
||||
update_analysis_and_wait,
|
||||
options_or_default.as_ptr() as *mut core::ffi::c_char,
|
||||
None,
|
||||
)
|
||||
};
|
||||
|
||||
if handle.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { BinaryView::from_raw(handle) })
|
||||
}
|
||||
}
|
||||
|
||||
pub fn load_view<O: IntoJson>(
|
||||
bv: &BinaryView,
|
||||
update_analysis_and_wait: bool,
|
||||
options: Option<O>,
|
||||
) -> Option<rc::Ref<binaryview::BinaryView>> {
|
||||
let options_or_default = if let Some(opt) = options {
|
||||
opt.get_json_string()
|
||||
.ok()?
|
||||
.into_bytes_with_nul()
|
||||
.as_ref()
|
||||
.to_vec()
|
||||
} else {
|
||||
Metadata::new_of_type(MetadataType::KeyValueDataType)
|
||||
.get_json_string()
|
||||
.ok()?
|
||||
.as_ref()
|
||||
.to_vec()
|
||||
};
|
||||
|
||||
let handle = unsafe {
|
||||
binaryninjacore_sys::BNLoadBinaryView(
|
||||
bv.handle as *mut _,
|
||||
update_analysis_and_wait,
|
||||
options_or_default.as_ptr() as *mut core::ffi::c_char,
|
||||
None,
|
||||
options_or_default.as_ref().handle,
|
||||
)
|
||||
};
|
||||
|
||||
@@ -377,6 +435,75 @@ pub fn version() -> string::BnString {
|
||||
unsafe { string::BnString::from_raw(binaryninjacore_sys::BNGetVersionString()) }
|
||||
}
|
||||
|
||||
pub fn build_id() -> u32 {
|
||||
unsafe { binaryninjacore_sys::BNGetBuildId() }
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Hash)]
|
||||
pub struct VersionInfo {
|
||||
pub major: u32,
|
||||
pub minor: u32,
|
||||
pub build: u32,
|
||||
pub channel: string::BnString,
|
||||
}
|
||||
|
||||
pub fn version_info() -> VersionInfo {
|
||||
let info_raw = unsafe { binaryninjacore_sys::BNGetVersionInfo() };
|
||||
VersionInfo {
|
||||
major: info_raw.major,
|
||||
minor: info_raw.minor,
|
||||
build: info_raw.build,
|
||||
channel: unsafe { string::BnString::from_raw(info_raw.channel) },
|
||||
}
|
||||
}
|
||||
|
||||
pub fn serial_number() -> string::BnString {
|
||||
unsafe { string::BnString::from_raw(binaryninjacore_sys::BNGetSerialNumber()) }
|
||||
}
|
||||
|
||||
pub fn is_license_validated() -> bool {
|
||||
unsafe { binaryninjacore_sys::BNIsLicenseValidated() }
|
||||
}
|
||||
|
||||
pub fn licensed_user_email() -> string::BnString {
|
||||
unsafe { string::BnString::from_raw(binaryninjacore_sys::BNGetLicensedUserEmail()) }
|
||||
}
|
||||
|
||||
pub fn license_count() -> i32 {
|
||||
unsafe { binaryninjacore_sys::BNGetLicenseCount() }
|
||||
}
|
||||
|
||||
pub fn set_license<S: string::BnStrCompatible>(license: S) {
|
||||
let license = license.into_bytes_with_nul();
|
||||
let license_slice = license.as_ref();
|
||||
unsafe { binaryninjacore_sys::BNSetLicense(license_slice.as_ptr() as *const std::os::raw::c_char) }
|
||||
}
|
||||
|
||||
pub fn product() -> string::BnString {
|
||||
unsafe { string::BnString::from_raw(binaryninjacore_sys::BNGetProduct()) }
|
||||
}
|
||||
|
||||
pub fn product_type() -> string::BnString {
|
||||
unsafe { string::BnString::from_raw(binaryninjacore_sys::BNGetProductType()) }
|
||||
}
|
||||
|
||||
pub fn license_expiration_time() -> std::time::SystemTime {
|
||||
let m = std::time::Duration::from_secs(unsafe {
|
||||
binaryninjacore_sys::BNGetLicenseExpirationTime()
|
||||
});
|
||||
std::time::UNIX_EPOCH + m
|
||||
}
|
||||
|
||||
pub fn is_ui_enabled() -> bool {
|
||||
unsafe { binaryninjacore_sys::BNIsUIEnabled() }
|
||||
}
|
||||
|
||||
pub fn is_database<S: string::BnStrCompatible>(filename: S) -> bool {
|
||||
let filename = filename.into_bytes_with_nul();
|
||||
let filename_slice = filename.as_ref();
|
||||
unsafe { binaryninjacore_sys::BNIsDatabase(filename_slice.as_ptr() as *const std::os::raw::c_char) }
|
||||
}
|
||||
|
||||
pub fn plugin_abi_version() -> u32 {
|
||||
binaryninjacore_sys::BN_CURRENT_CORE_ABI_VERSION
|
||||
}
|
||||
|
||||
@@ -415,18 +415,14 @@ impl std::fmt::Display for LinearDisassemblyLine {
|
||||
impl CoreArrayProvider for LinearDisassemblyLine {
|
||||
type Raw = BNLinearDisassemblyLine;
|
||||
type Context = ();
|
||||
type Wrapped<'a> = Guard<'a, LinearDisassemblyLine>;
|
||||
}
|
||||
|
||||
unsafe impl CoreOwnedArrayProvider for LinearDisassemblyLine {
|
||||
unsafe impl CoreArrayProviderInner for LinearDisassemblyLine {
|
||||
unsafe fn free(raw: *mut BNLinearDisassemblyLine, count: usize, _context: &()) {
|
||||
BNFreeLinearDisassemblyLines(raw, count);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> CoreArrayWrapper<'a> for LinearDisassemblyLine {
|
||||
type Wrapped = Guard<'a, LinearDisassemblyLine>;
|
||||
|
||||
unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
Guard::new(LinearDisassemblyLine::from_raw(raw), _context)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,6 +136,8 @@ where
|
||||
LLIL_ZX => ExprInfo::Zx(Operation::new(function, op)),
|
||||
LLIL_LOW_PART => ExprInfo::LowPart(Operation::new(function, op)),
|
||||
|
||||
LLIL_REG_SPLIT => ExprInfo::RegSplit(Operation::new(function, op)),
|
||||
|
||||
LLIL_CMP_E => ExprInfo::CmpE(Operation::new(function, op)),
|
||||
LLIL_CMP_NE => ExprInfo::CmpNe(Operation::new(function, op)),
|
||||
LLIL_CMP_SLT => ExprInfo::CmpSlt(Operation::new(function, op)),
|
||||
@@ -273,6 +275,7 @@ where
|
||||
LLIL_LOAD => ExprInfo::Load(Operation::new(self.function, op)),
|
||||
LLIL_POP => ExprInfo::Pop(Operation::new(self.function, op)),
|
||||
LLIL_REG => ExprInfo::Reg(Operation::new(self.function, op)),
|
||||
LLIL_REG_SPLIT => ExprInfo::RegSplit(Operation::new(self.function, op)),
|
||||
LLIL_FLAG => ExprInfo::Flag(Operation::new(self.function, op)),
|
||||
LLIL_FLAG_BIT => ExprInfo::FlagBit(Operation::new(self.function, op)),
|
||||
LLIL_FLAG_COND => ExprInfo::FlagCond(Operation::new(self.function, op)), // TODO lifted only
|
||||
@@ -327,6 +330,7 @@ where
|
||||
match op.operation {
|
||||
LLIL_LOAD_SSA => ExprInfo::Load(Operation::new(self.function, op)),
|
||||
LLIL_REG_SSA | LLIL_REG_SSA_PARTIAL => ExprInfo::Reg(Operation::new(self.function, op)),
|
||||
LLIL_REG_SPLIT_SSA => ExprInfo::RegSplit(Operation::new(self.function, op)),
|
||||
LLIL_FLAG_SSA => ExprInfo::Flag(Operation::new(self.function, op)),
|
||||
LLIL_FLAG_BIT_SSA => ExprInfo::FlagBit(Operation::new(self.function, op)),
|
||||
_ => common_info(self.function, op),
|
||||
@@ -383,6 +387,7 @@ where
|
||||
Load(Operation<'func, A, M, F, operation::Load>),
|
||||
Pop(Operation<'func, A, M, F, operation::Pop>),
|
||||
Reg(Operation<'func, A, M, F, operation::Reg>),
|
||||
RegSplit(Operation<'func, A, M, F, operation::RegSplit>),
|
||||
Const(Operation<'func, A, M, F, operation::Const>),
|
||||
ConstPtr(Operation<'func, A, M, F, operation::Const>),
|
||||
Flag(Operation<'func, A, M, F, operation::Flag>),
|
||||
@@ -595,6 +600,8 @@ where
|
||||
|
||||
Reg(ref op) => &op.op,
|
||||
|
||||
RegSplit(ref op) => &op.op,
|
||||
|
||||
Flag(ref op) => &op.op,
|
||||
|
||||
FlagBit(ref op) => &op.op,
|
||||
@@ -648,6 +655,8 @@ where
|
||||
|
||||
Reg(ref op) => op.flag_write(),
|
||||
|
||||
RegSplit(ref op) => op.flag_write(),
|
||||
|
||||
Flag(ref op) => op.flag_write(),
|
||||
|
||||
FlagBit(ref op) => op.flag_write(),
|
||||
@@ -735,6 +744,31 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
RegSplit(ref op) => {
|
||||
let low_reg = op.low_reg();
|
||||
let high_reg = op.high_reg();
|
||||
let size = op.size();
|
||||
|
||||
let low_size = match low_reg {
|
||||
Register::Temp(_) => Some(size),
|
||||
Register::ArchReg(ref r) if r.info().size() != size => Some(size),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
let high_size = match high_reg {
|
||||
Register::Temp(_) => Some(size),
|
||||
Register::ArchReg(ref r) if r.info().size() != size => Some(size),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
match (low_size, high_size) {
|
||||
(Some(ls), Some(hs)) => write!(f, "{:?}.{}:{:?}.{}", high_reg, hs, low_reg, ls),
|
||||
(Some(ls), None) => write!(f, "{:?}:{:?}.{}", high_reg, low_reg, ls),
|
||||
(None, Some(hs)) => write!(f, "{:?}.{}:{:?}", high_reg, hs, low_reg),
|
||||
_ => write!(f, "{:?}:{:?}", high_reg, low_reg),
|
||||
}
|
||||
}
|
||||
|
||||
Flag(ref _op) => write!(f, "flag"), // TODO
|
||||
|
||||
FlagBit(ref _op) => write!(f, "flag_bit"), // TODO
|
||||
|
||||
@@ -99,7 +99,7 @@ where
|
||||
let expr_idx =
|
||||
unsafe { BNGetLowLevelILIndexForInstruction(self.function.handle, self.instr_idx) };
|
||||
let op = unsafe { BNGetLowLevelILByIndex(self.function.handle, expr_idx) };
|
||||
return op.address;
|
||||
op.address
|
||||
}
|
||||
|
||||
pub fn info(&self) -> InstrInfo<'func, A, M, NonSSA<V>> {
|
||||
|
||||
@@ -607,6 +607,26 @@ where
|
||||
A: 'a + Architecture,
|
||||
R: ExpressionResultType,
|
||||
{
|
||||
pub fn from_expr(expr: Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R>) -> Self {
|
||||
use binaryninjacore_sys::BNGetLowLevelILByIndex;
|
||||
|
||||
let instr = unsafe {
|
||||
BNGetLowLevelILByIndex(expr.function.handle, expr.expr_idx)
|
||||
};
|
||||
|
||||
ExpressionBuilder {
|
||||
function: expr.function,
|
||||
op: instr.operation,
|
||||
size: instr.size,
|
||||
flags: instr.flags,
|
||||
op1: instr.operands[0],
|
||||
op2: instr.operands[1],
|
||||
op3: instr.operands[2],
|
||||
op4: instr.operands[3],
|
||||
_ty: PhantomData
|
||||
}
|
||||
}
|
||||
|
||||
pub fn with_flag_write(mut self, flag_write: A::FlagWrite) -> Self {
|
||||
// TODO verify valid id
|
||||
self.flags = flag_write.id();
|
||||
@@ -1016,6 +1036,43 @@ where
|
||||
Expression::new(self, expr_idx)
|
||||
}
|
||||
|
||||
pub fn reg_split<H: Into<Register<A::Register>>, L: Into<Register<A::Register>>>(
|
||||
&self,
|
||||
size: usize,
|
||||
hi_reg: H,
|
||||
lo_reg: L,
|
||||
) -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> {
|
||||
use binaryninjacore_sys::BNLowLevelILAddExpr;
|
||||
use binaryninjacore_sys::BNLowLevelILOperation::LLIL_REG_SPLIT;
|
||||
|
||||
// TODO verify valid id
|
||||
let hi_reg = match hi_reg.into() {
|
||||
Register::ArchReg(r) => r.id(),
|
||||
Register::Temp(r) => 0x8000_0000 | r,
|
||||
};
|
||||
|
||||
// TODO verify valid id
|
||||
let lo_reg = match lo_reg.into() {
|
||||
Register::ArchReg(r) => r.id(),
|
||||
Register::Temp(r) => 0x8000_0000 | r,
|
||||
};
|
||||
|
||||
let expr_idx = unsafe {
|
||||
BNLowLevelILAddExpr(
|
||||
self.handle,
|
||||
LLIL_REG_SPLIT,
|
||||
size,
|
||||
0,
|
||||
hi_reg as u64,
|
||||
lo_reg as u64,
|
||||
0,
|
||||
0,
|
||||
)
|
||||
};
|
||||
|
||||
Expression::new(self, expr_idx)
|
||||
}
|
||||
|
||||
pub fn set_reg<'a, R, E>(
|
||||
&'a self,
|
||||
size: usize,
|
||||
|
||||
@@ -12,8 +12,9 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
use binaryninjacore_sys::BNLowLevelILInstruction;
|
||||
use binaryninjacore_sys::{BNGetLowLevelILByIndex, BNLowLevelILInstruction};
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use std::marker::PhantomData;
|
||||
use std::mem;
|
||||
|
||||
@@ -89,10 +90,10 @@ pub struct Syscall;
|
||||
pub struct Intrinsic;
|
||||
|
||||
impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, Intrinsic>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
V: NonSSAVariant,
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
V: NonSSAVariant,
|
||||
{
|
||||
// TODO: Support register and expression lists
|
||||
pub fn intrinsic(&self) -> Option<A::Intrinsic> {
|
||||
@@ -289,6 +290,62 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
// LLIL_REG_SPLIT
|
||||
pub struct RegSplit;
|
||||
|
||||
impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, RegSplit>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
V: NonSSAVariant,
|
||||
{
|
||||
pub fn size(&self) -> usize {
|
||||
self.op.size
|
||||
}
|
||||
|
||||
pub fn low_reg(&self) -> Register<A::Register> {
|
||||
let raw_id = self.op.operands[0] as u32;
|
||||
|
||||
if raw_id >= 0x8000_0000 {
|
||||
Register::Temp(raw_id & 0x7fff_ffff)
|
||||
} else {
|
||||
self.function
|
||||
.arch()
|
||||
.register_from_id(raw_id)
|
||||
.map(Register::ArchReg)
|
||||
.unwrap_or_else(|| {
|
||||
error!(
|
||||
"got garbage register from LLIL_REG @ 0x{:x}",
|
||||
self.op.address
|
||||
);
|
||||
|
||||
Register::Temp(0)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn high_reg(&self) -> Register<A::Register> {
|
||||
let raw_id = self.op.operands[1] as u32;
|
||||
|
||||
if raw_id >= 0x8000_0000 {
|
||||
Register::Temp(raw_id & 0x7fff_ffff)
|
||||
} else {
|
||||
self.function
|
||||
.arch()
|
||||
.register_from_id(raw_id)
|
||||
.map(Register::ArchReg)
|
||||
.unwrap_or_else(|| {
|
||||
error!(
|
||||
"got garbage register from LLIL_REG @ 0x{:x}",
|
||||
self.op.address
|
||||
);
|
||||
|
||||
Register::Temp(0)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LLIL_FLAG, LLIL_FLAG_SSA
|
||||
pub struct Flag;
|
||||
|
||||
@@ -312,6 +369,36 @@ where
|
||||
// LLIL_JUMP_TO
|
||||
pub struct JumpTo;
|
||||
|
||||
struct TargetListIter<'func, A, M, F>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
{
|
||||
function: &'func Function<A, M, F>,
|
||||
cursor: BNLowLevelILInstruction,
|
||||
cursor_operand: usize,
|
||||
}
|
||||
|
||||
impl<'func, A, M, F> TargetListIter<'func, A, M, F>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
M: FunctionMutability,
|
||||
F: FunctionForm,
|
||||
{
|
||||
fn next(&mut self) -> u64 {
|
||||
if self.cursor_operand >= 3 {
|
||||
self.cursor = unsafe {
|
||||
BNGetLowLevelILByIndex(self.function.handle, self.cursor.operands[3] as usize)
|
||||
};
|
||||
self.cursor_operand = 0;
|
||||
}
|
||||
let result = self.cursor.operands[self.cursor_operand];
|
||||
self.cursor_operand += 1;
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl<'func, A, M, F> Operation<'func, A, M, F, JumpTo>
|
||||
where
|
||||
A: 'func + Architecture,
|
||||
@@ -321,7 +408,26 @@ where
|
||||
pub fn target(&self) -> Expression<'func, A, M, F, ValueExpr> {
|
||||
Expression::new(self.function, self.op.operands[0] as usize)
|
||||
}
|
||||
// TODO target list
|
||||
|
||||
pub fn target_list(&self) -> BTreeMap<u64, usize> {
|
||||
let mut result = BTreeMap::new();
|
||||
let count = self.op.operands[1] as usize / 2;
|
||||
let mut list = TargetListIter {
|
||||
function: self.function,
|
||||
cursor: unsafe {
|
||||
BNGetLowLevelILByIndex(self.function.handle, self.op.operands[2] as usize)
|
||||
},
|
||||
cursor_operand: 0,
|
||||
};
|
||||
|
||||
for _ in 0..count {
|
||||
let value = list.next();
|
||||
let target = list.next() as usize;
|
||||
result.insert(value, target);
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
// LLIL_CALL, LLIL_CALL_SSA
|
||||
@@ -382,12 +488,20 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
pub fn true_target_idx(&self) -> usize {
|
||||
self.op.operands[1] as usize
|
||||
}
|
||||
|
||||
pub fn false_target(&self) -> Instruction<'func, A, M, F> {
|
||||
Instruction {
|
||||
function: self.function,
|
||||
instr_idx: self.op.operands[2] as usize,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn false_target_idx(&self) -> usize {
|
||||
self.op.operands[2] as usize
|
||||
}
|
||||
}
|
||||
|
||||
// LLIL_GOTO
|
||||
@@ -405,6 +519,10 @@ where
|
||||
instr_idx: self.op.operands[0] as usize,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn target_idx(&self) -> usize {
|
||||
self.op.operands[0] as usize
|
||||
}
|
||||
}
|
||||
|
||||
// LLIL_FLAG_COND
|
||||
@@ -640,6 +758,7 @@ impl OperationArguments for SetFlag {}
|
||||
impl OperationArguments for Load {}
|
||||
impl OperationArguments for Store {}
|
||||
impl OperationArguments for Reg {}
|
||||
impl OperationArguments for RegSplit {}
|
||||
impl OperationArguments for Flag {}
|
||||
impl OperationArguments for FlagBit {}
|
||||
impl OperationArguments for Jump {}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
//! To use logging in your script, do something like:
|
||||
//!
|
||||
//! ```
|
||||
//! ```no-test
|
||||
//! use binaryninja::logger;
|
||||
//! use log::{info, LevelFilter};
|
||||
//!
|
||||
@@ -15,7 +15,7 @@
|
||||
//!
|
||||
//! or
|
||||
//!
|
||||
//!```
|
||||
//!```no-test
|
||||
//! use binaryninja::logger;
|
||||
//! use log::{info, LevelFilter};
|
||||
//!
|
||||
@@ -29,12 +29,11 @@
|
||||
//! ```
|
||||
//!
|
||||
|
||||
use crate::string::BnStr;
|
||||
|
||||
pub use binaryninjacore_sys::BNLogLevel as Level;
|
||||
use binaryninjacore_sys::{BNLogListener, BNUpdateLogListeners};
|
||||
|
||||
use log;
|
||||
use std::ffi::CStr;
|
||||
use std::os::raw::{c_char, c_void};
|
||||
|
||||
struct Logger;
|
||||
@@ -84,7 +83,7 @@ pub fn init(filter: log::LevelFilter) -> Result<(), log::SetLoggerError> {
|
||||
}
|
||||
|
||||
pub trait LogListener: 'static + Sync {
|
||||
fn log(&self, session: usize, level: Level, msg: &BnStr, logger_name: &BnStr, tid: usize);
|
||||
fn log(&self, session: usize, level: Level, msg: &CStr, logger_name: &CStr, tid: usize);
|
||||
fn level(&self) -> Level;
|
||||
fn close(&self) {}
|
||||
}
|
||||
@@ -147,8 +146,8 @@ extern "C" fn cb_log<L>(
|
||||
listener.log(
|
||||
session,
|
||||
level,
|
||||
BnStr::from_raw(msg),
|
||||
BnStr::from_raw(logger_name),
|
||||
CStr::from_ptr(msg),
|
||||
CStr::from_ptr(logger_name),
|
||||
tid,
|
||||
);
|
||||
})
|
||||
|
||||
122
src/metadata.rs
122
src/metadata.rs
@@ -1,7 +1,5 @@
|
||||
use crate::rc::{
|
||||
Array, CoreArrayProvider, CoreArrayWrapper, CoreOwnedArrayProvider, Guard, Ref, RefCountable,
|
||||
};
|
||||
use crate::string::{BnStrCompatible, BnString};
|
||||
use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
|
||||
use crate::string::{BnStrCompatible, BnString, IntoJson};
|
||||
use binaryninjacore_sys::*;
|
||||
use std::collections::HashMap;
|
||||
use std::os::raw::c_char;
|
||||
@@ -168,6 +166,19 @@ impl Metadata {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_json_string(&self) -> Result<BnString, ()> {
|
||||
match self.get_type() {
|
||||
MetadataType::StringDataType => {
|
||||
let ptr: *mut c_char = unsafe { BNMetadataGetJsonString(self.handle) };
|
||||
if ptr.is_null() {
|
||||
return Err(());
|
||||
}
|
||||
Ok(unsafe { BnString::from_raw(ptr) })
|
||||
}
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_raw(&self) -> Result<Vec<u8>, ()> {
|
||||
match self.get_type() {
|
||||
MetadataType::RawDataType => {
|
||||
@@ -335,18 +346,14 @@ unsafe impl RefCountable for Metadata {
|
||||
impl CoreArrayProvider for Metadata {
|
||||
type Raw = *mut BNMetadata;
|
||||
type Context = ();
|
||||
type Wrapped<'a> = Guard<'a, Metadata>;
|
||||
}
|
||||
|
||||
unsafe impl CoreOwnedArrayProvider for Metadata {
|
||||
unsafe impl CoreArrayProviderInner for Metadata {
|
||||
unsafe fn free(raw: *mut *mut BNMetadata, _count: usize, _context: &()) {
|
||||
BNFreeMetadataArray(raw);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> CoreArrayWrapper<'a> for Metadata {
|
||||
type Wrapped = Guard<'a, Metadata>;
|
||||
|
||||
unsafe fn wrap_raw(raw: &'a *mut BNMetadata, context: &'a ()) -> Guard<'a, Metadata> {
|
||||
unsafe fn wrap_raw<'a>(raw: &'a *mut BNMetadata, context: &'a ()) -> Self::Wrapped<'a> {
|
||||
Guard::new(Metadata::from_raw(*raw), context)
|
||||
}
|
||||
}
|
||||
@@ -403,12 +410,6 @@ impl From<&str> for Ref<Metadata> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Into<Ref<Metadata>>> From<&T> for Ref<Metadata> {
|
||||
fn from(value: &T) -> Self {
|
||||
value.into()
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Vec<u8>> for Ref<Metadata> {
|
||||
fn from(value: &Vec<u8>) -> Self {
|
||||
unsafe { Metadata::ref_from_raw(BNCreateMetadataRawData(value.as_ptr(), value.len())) }
|
||||
@@ -441,16 +442,15 @@ impl From<&Array<Metadata>> for Ref<Metadata> {
|
||||
|
||||
impl<S: BnStrCompatible> From<HashMap<S, Ref<Metadata>>> for Ref<Metadata> {
|
||||
fn from(value: HashMap<S, Ref<Metadata>>) -> Self {
|
||||
let mut key_refs: Vec<S::Result> = vec![];
|
||||
let mut keys: Vec<*const c_char> = vec![];
|
||||
let mut values: Vec<*mut BNMetadata> = vec![];
|
||||
for (k, v) in value.into_iter() {
|
||||
key_refs.push(k.into_bytes_with_nul());
|
||||
values.push(v.as_ref().handle);
|
||||
}
|
||||
for k in &key_refs {
|
||||
keys.push(k.as_ref().as_ptr() as *const c_char);
|
||||
}
|
||||
let data: Vec<(S::Result, Ref<Metadata>)> = value
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k.into_bytes_with_nul(), v))
|
||||
.collect();
|
||||
let mut keys: Vec<*const c_char> = data
|
||||
.iter()
|
||||
.map(|(k, _)| k.as_ref().as_ptr() as *const c_char)
|
||||
.collect();
|
||||
let mut values: Vec<*mut BNMetadata> = data.iter().map(|(_, v)| v.handle).collect();
|
||||
|
||||
unsafe {
|
||||
Metadata::ref_from_raw(BNCreateMetadataValueStore(
|
||||
@@ -462,19 +462,21 @@ impl<S: BnStrCompatible> From<HashMap<S, Ref<Metadata>>> for Ref<Metadata> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BnStrCompatible + Copy, T: Into<Ref<Metadata>>> From<&[(S, T)]> for Ref<Metadata> {
|
||||
impl<S, T> From<&[(S, T)]> for Ref<Metadata>
|
||||
where
|
||||
S: BnStrCompatible + Copy,
|
||||
for<'a> &'a T: Into<Ref<Metadata>>,
|
||||
{
|
||||
fn from(value: &[(S, T)]) -> Self {
|
||||
let mut key_refs: Vec<S::Result> = vec![];
|
||||
let mut keys: Vec<*const c_char> = vec![];
|
||||
let mut values: Vec<*mut BNMetadata> = vec![];
|
||||
for (k, v) in value.iter() {
|
||||
key_refs.push(k.into_bytes_with_nul());
|
||||
let value_metadata: Ref<Metadata> = v.into();
|
||||
values.push(value_metadata.handle);
|
||||
}
|
||||
for k in &key_refs {
|
||||
keys.push(k.as_ref().as_ptr() as *const c_char);
|
||||
}
|
||||
let data: Vec<(S::Result, Ref<Metadata>)> = value
|
||||
.iter()
|
||||
.map(|(k, v)| (k.into_bytes_with_nul(), v.into()))
|
||||
.collect();
|
||||
let mut keys: Vec<*const c_char> = data
|
||||
.iter()
|
||||
.map(|(k, _)| k.as_ref().as_ptr() as *const c_char)
|
||||
.collect();
|
||||
let mut values: Vec<*mut BNMetadata> = data.iter().map(|(_, v)| v.handle).collect();
|
||||
|
||||
unsafe {
|
||||
Metadata::ref_from_raw(BNCreateMetadataValueStore(
|
||||
@@ -486,29 +488,15 @@ impl<S: BnStrCompatible + Copy, T: Into<Ref<Metadata>>> From<&[(S, T)]> for Ref<
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: BnStrCompatible + Copy, T: Into<Ref<Metadata>>, const N: usize> From<[(S, T); N]>
|
||||
for Ref<Metadata>
|
||||
impl<S, T, const N: usize> From<[(S, T); N]> for Ref<Metadata>
|
||||
where
|
||||
S: BnStrCompatible + Copy,
|
||||
for<'a> &'a T: Into<Ref<Metadata>>,
|
||||
{
|
||||
fn from(value: [(S, T); N]) -> Self {
|
||||
let mut key_refs: Vec<S::Result> = vec![];
|
||||
let mut keys: Vec<*const c_char> = vec![];
|
||||
let mut values: Vec<*mut BNMetadata> = vec![];
|
||||
for (k, v) in value.into_iter() {
|
||||
key_refs.push(k.into_bytes_with_nul());
|
||||
let value_metadata: Ref<Metadata> = v.into();
|
||||
values.push(value_metadata.handle);
|
||||
}
|
||||
for k in &key_refs {
|
||||
keys.push(k.as_ref().as_ptr() as *const c_char);
|
||||
}
|
||||
|
||||
unsafe {
|
||||
Metadata::ref_from_raw(BNCreateMetadataValueStore(
|
||||
keys.as_mut_ptr(),
|
||||
values.as_mut_ptr(),
|
||||
keys.len(),
|
||||
))
|
||||
}
|
||||
let slice = &value[..];
|
||||
// use the `impl From<&[(S, T)]>`
|
||||
slice.into()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -714,3 +702,17 @@ impl TryFrom<&Metadata> for HashMap<String, Ref<Metadata>> {
|
||||
.map(|m| m.into_iter().map(|(k, v)| (k.to_string(), v)).collect())
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoJson for &Metadata {
|
||||
type Output = BnString;
|
||||
fn get_json_string(self) -> Result<BnString, ()> {
|
||||
Metadata::get_json_string(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoJson for Ref<Metadata> {
|
||||
type Output = BnString;
|
||||
fn get_json_string(self) -> Result<BnString, ()> {
|
||||
Metadata::get_json_string(&self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
use core::hash::{Hash, Hasher};
|
||||
use std::ffi::c_char;
|
||||
|
||||
use binaryninjacore_sys::BNFreeMediumLevelILFunction;
|
||||
use binaryninjacore_sys::BNGetMediumLevelILBasicBlockList;
|
||||
use binaryninjacore_sys::BNGetMediumLevelILInstructionCount;
|
||||
use binaryninjacore_sys::BNGetMediumLevelILOwnerFunction;
|
||||
use binaryninjacore_sys::BNGetMediumLevelILSSAForm;
|
||||
use binaryninjacore_sys::BNMediumLevelILFunction;
|
||||
use binaryninjacore_sys::BNMediumLevelILGetInstructionStart;
|
||||
use binaryninjacore_sys::BNNewMediumLevelILFunctionReference;
|
||||
use binaryninjacore_sys::*;
|
||||
|
||||
use crate::architecture::CoreArchitecture;
|
||||
use crate::basicblock::BasicBlock;
|
||||
use crate::function::Function;
|
||||
use crate::function::Location;
|
||||
use crate::rc::{Array, Ref, RefCountable};
|
||||
use crate::disassembly::DisassemblySettings;
|
||||
use crate::flowgraph::FlowGraph;
|
||||
use crate::function::{Function, Location};
|
||||
use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref, RefCountable};
|
||||
use crate::string::BnStrCompatible;
|
||||
use crate::types::{
|
||||
Conf, PossibleValueSet, RegisterValue, SSAVariable, Type, UserVariableValues, Variable,
|
||||
};
|
||||
|
||||
use super::{MediumLevelILBlock, MediumLevelILInstruction, MediumLevelILLiftedInstruction};
|
||||
|
||||
@@ -65,6 +65,19 @@ impl MediumLevelILFunction {
|
||||
self.instruction_from_idx(expr_idx).lift()
|
||||
}
|
||||
|
||||
pub fn instruction_from_instruction_idx(&self, instr_idx: usize) -> MediumLevelILInstruction {
|
||||
MediumLevelILInstruction::new(self.to_owned(), unsafe {
|
||||
BNGetMediumLevelILIndexForInstruction(self.handle, instr_idx)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn lifted_instruction_from_instruction_idx(
|
||||
&self,
|
||||
instr_idx: usize,
|
||||
) -> MediumLevelILLiftedInstruction {
|
||||
self.instruction_from_instruction_idx(instr_idx).lift()
|
||||
}
|
||||
|
||||
pub fn instruction_count(&self) -> usize {
|
||||
unsafe { BNGetMediumLevelILInstructionCount(self.handle) }
|
||||
}
|
||||
@@ -91,6 +104,478 @@ impl MediumLevelILFunction {
|
||||
|
||||
unsafe { Array::new(blocks, count, context) }
|
||||
}
|
||||
|
||||
pub fn get_var_definitions<'a>(&'a self, var: &Variable) -> MediumLevelILInstructionList<'a> {
|
||||
let mut count = 0;
|
||||
let raw_instrs =
|
||||
unsafe { BNGetMediumLevelILVariableDefinitions(self.handle, &var.raw(), &mut count) };
|
||||
assert!(!raw_instrs.is_null());
|
||||
let instrs = unsafe { core::slice::from_raw_parts(raw_instrs, count) };
|
||||
MediumLevelILInstructionList {
|
||||
mlil: self,
|
||||
ptr: raw_instrs,
|
||||
instr_idxs: instrs.iter(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_user_stack_var<'a, S: BnStrCompatible, C: Into<Conf<&'a Type>>>(
|
||||
self,
|
||||
offset: i64,
|
||||
var_type: C,
|
||||
name: S,
|
||||
) {
|
||||
let var_type = var_type.into();
|
||||
let mut raw_var_type: BNTypeWithConfidence = var_type.into();
|
||||
let name = name.into_bytes_with_nul();
|
||||
unsafe {
|
||||
BNCreateUserStackVariable(
|
||||
self.get_function().handle,
|
||||
offset,
|
||||
&mut raw_var_type,
|
||||
name.as_ref().as_ptr() as *const c_char,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn delete_user_stack_var(self, offset: i64) {
|
||||
unsafe { BNDeleteUserStackVariable(self.get_function().handle, offset) }
|
||||
}
|
||||
|
||||
pub fn create_user_var<'a, S: BnStrCompatible, C: Into<Conf<&'a Type>>>(
|
||||
&self,
|
||||
var: &Variable,
|
||||
var_type: C,
|
||||
name: S,
|
||||
ignore_disjoint_uses: bool,
|
||||
) {
|
||||
let var_type = var_type.into();
|
||||
let raw_var_type: BNTypeWithConfidence = var_type.into();
|
||||
let name = name.into_bytes_with_nul();
|
||||
unsafe {
|
||||
BNCreateUserVariable(
|
||||
self.get_function().handle,
|
||||
&var.raw(),
|
||||
&raw_var_type as *const _ as *mut _,
|
||||
name.as_ref().as_ptr() as *const _,
|
||||
ignore_disjoint_uses,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn delete_user_var(&self, var: &Variable) {
|
||||
unsafe { BNDeleteUserVariable(self.get_function().handle, &var.raw()) }
|
||||
}
|
||||
|
||||
pub fn is_var_user_defined(&self, var: &Variable) -> bool {
|
||||
unsafe { BNIsVariableUserDefined(self.get_function().handle, &var.raw()) }
|
||||
}
|
||||
|
||||
/// Allows the user to specify a PossibleValueSet value for an MLIL
|
||||
/// variable at its definition site.
|
||||
///
|
||||
/// .. warning:: Setting the variable value, triggers a reanalysis of the
|
||||
/// function and allows the dataflow to compute and propagate values which
|
||||
/// depend on the current variable. This implies that branch conditions
|
||||
/// whose values can be determined statically will be computed, leading to
|
||||
/// potential branch elimination at the HLIL layer.
|
||||
///
|
||||
/// * `var` - Variable for which the value is to be set
|
||||
/// * `addr` - Address of the definition site of the variable
|
||||
/// * `value` - Informed value of the variable
|
||||
///
|
||||
/// # Example
|
||||
/// ```no_run
|
||||
/// # use binaryninja::mlil::MediumLevelILFunction;
|
||||
/// # use binaryninja::types::PossibleValueSet;
|
||||
/// # let mlil_fun: MediumLevelILFunction = todo!();
|
||||
/// let (mlil_var, arch_addr, _val) = mlil_fun.user_var_values().all().next().unwrap();
|
||||
/// let def_address = arch_addr.address;
|
||||
/// let var_value = PossibleValueSet::ConstantValue{value: 5};
|
||||
/// mlil_fun.set_user_var_value(&mlil_var, def_address, var_value).unwrap();
|
||||
/// ```
|
||||
pub fn set_user_var_value(
|
||||
&self,
|
||||
var: &Variable,
|
||||
addr: u64,
|
||||
value: PossibleValueSet,
|
||||
) -> Result<(), ()> {
|
||||
let Some(_def_site) = self
|
||||
.get_var_definitions(var)
|
||||
.find(|def| def.address == addr)
|
||||
else {
|
||||
// Error "No definition for Variable found at given address"
|
||||
return Err(());
|
||||
};
|
||||
let function = self.get_function();
|
||||
let def_site = BNArchitectureAndAddress {
|
||||
arch: function.arch().0,
|
||||
address: addr,
|
||||
};
|
||||
let value = value.into_raw();
|
||||
|
||||
unsafe { BNSetUserVariableValue(function.handle, &var.raw(), &def_site, value.as_ffi()) }
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Clears a previously defined user variable value.
|
||||
///
|
||||
/// * `var` - Variable for which the value was informed
|
||||
/// * `def_addr` - Address of the definition site of the variable
|
||||
pub fn clear_user_var_value(&self, var: &Variable, addr: u64) -> Result<(), ()> {
|
||||
let Some(_var_def) = self
|
||||
.get_var_definitions(var)
|
||||
.find(|site| site.address == addr)
|
||||
else {
|
||||
//error "Could not get definition for Variable"
|
||||
return Err(());
|
||||
};
|
||||
|
||||
let function = self.get_function();
|
||||
let def_site = BNArchitectureAndAddress {
|
||||
arch: function.arch().0,
|
||||
address: addr,
|
||||
};
|
||||
|
||||
unsafe { BNClearUserVariableValue(function.handle, &var.raw(), &def_site) };
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Returns a map of current defined user variable values.
|
||||
/// Returns a Map of user current defined user variable values and their definition sites.
|
||||
pub fn user_var_values(&self) -> UserVariableValues {
|
||||
let mut count = 0;
|
||||
let function = self.get_function();
|
||||
let var_values = unsafe { BNGetAllUserVariableValues(function.handle, &mut count) };
|
||||
assert!(!var_values.is_null());
|
||||
UserVariableValues {
|
||||
vars: core::ptr::slice_from_raw_parts(var_values, count),
|
||||
}
|
||||
}
|
||||
|
||||
/// Clear all user defined variable values.
|
||||
pub fn clear_user_var_values(&self) -> Result<(), ()> {
|
||||
for (var, arch_and_addr, _value) in self.user_var_values().all() {
|
||||
self.clear_user_var_value(&var, arch_and_addr.address)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn create_auto_stack_var<'a, T: Into<Conf<&'a Type>>, S: BnStrCompatible>(
|
||||
&self,
|
||||
offset: i64,
|
||||
var_type: T,
|
||||
name: S,
|
||||
) {
|
||||
let var_type: Conf<&Type> = var_type.into();
|
||||
let mut var_type = var_type.into();
|
||||
let name = name.into_bytes_with_nul();
|
||||
let name_c_str = name.as_ref();
|
||||
unsafe {
|
||||
BNCreateAutoStackVariable(
|
||||
self.get_function().handle,
|
||||
offset,
|
||||
&mut var_type,
|
||||
name_c_str.as_ptr() as *const c_char,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn delete_auto_stack_var(&self, offset: i64) {
|
||||
unsafe { BNDeleteAutoStackVariable(self.get_function().handle, offset) }
|
||||
}
|
||||
|
||||
pub fn create_auto_var<'a, S: BnStrCompatible, C: Into<Conf<&'a Type>>>(
|
||||
&self,
|
||||
var: &Variable,
|
||||
var_type: C,
|
||||
name: S,
|
||||
ignore_disjoint_uses: bool,
|
||||
) {
|
||||
let var_type: Conf<&Type> = var_type.into();
|
||||
let mut var_type = var_type.into();
|
||||
let name = name.into_bytes_with_nul();
|
||||
let name_c_str = name.as_ref();
|
||||
unsafe {
|
||||
BNCreateAutoVariable(
|
||||
self.get_function().handle,
|
||||
&var.raw(),
|
||||
&mut var_type,
|
||||
name_c_str.as_ptr() as *const c_char,
|
||||
ignore_disjoint_uses,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a list of ILReferenceSource objects (IL xrefs or cross-references)
|
||||
/// that reference the given variable. The variable is a local variable that can be either on the stack,
|
||||
/// in a register, or in a flag.
|
||||
/// This function is related to get_hlil_var_refs(), which returns variable references collected
|
||||
/// from HLIL. The two can be different in several cases, e.g., multiple variables in MLIL can be merged
|
||||
/// into a single variable in HLIL.
|
||||
///
|
||||
/// * `var` - Variable for which to query the xref
|
||||
///
|
||||
/// # Example
|
||||
/// ```no_run
|
||||
/// # use binaryninja::mlil::MediumLevelILFunction;
|
||||
/// # use binaryninja::types::Variable;
|
||||
/// # let mlil_fun: MediumLevelILFunction = todo!();
|
||||
/// # let mlil_var: Variable = todo!();
|
||||
/// let instr = mlil_fun.var_refs(&mlil_var).get(0).expr();
|
||||
/// ```
|
||||
pub fn var_refs(&self, var: &Variable) -> Array<ILReferenceSource> {
|
||||
let mut count = 0;
|
||||
let refs = unsafe {
|
||||
BNGetMediumLevelILVariableReferences(
|
||||
self.get_function().handle,
|
||||
&mut var.raw(),
|
||||
&mut count,
|
||||
)
|
||||
};
|
||||
assert!(!refs.is_null());
|
||||
unsafe { Array::new(refs, count, self.to_owned()) }
|
||||
}
|
||||
|
||||
/// Returns a list of variables referenced by code in the function ``func``,
|
||||
/// of the architecture ``arch``, and at the address ``addr``. If no function is specified, references from
|
||||
/// all functions and containing the address will be returned. If no architecture is specified, the
|
||||
/// architecture of the function will be used.
|
||||
/// This function is related to get_hlil_var_refs_from(), which returns variable references collected
|
||||
/// from HLIL. The two can be different in several cases, e.g., multiple variables in MLIL can be merged
|
||||
/// into a single variable in HLIL.
|
||||
///
|
||||
/// * `addr` - virtual address to query for variable references
|
||||
/// * `length` - optional length of query
|
||||
/// * `arch` - optional architecture of query
|
||||
pub fn var_refs_from(
|
||||
&self,
|
||||
addr: u64,
|
||||
length: Option<u64>,
|
||||
arch: Option<CoreArchitecture>,
|
||||
) -> Array<VariableReferenceSource> {
|
||||
let function = self.get_function();
|
||||
let arch = arch.unwrap_or_else(|| function.arch());
|
||||
let mut count = 0;
|
||||
|
||||
let refs = if let Some(length) = length {
|
||||
unsafe {
|
||||
BNGetMediumLevelILVariableReferencesInRange(
|
||||
function.handle,
|
||||
arch.0,
|
||||
addr,
|
||||
length,
|
||||
&mut count,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
unsafe {
|
||||
BNGetMediumLevelILVariableReferencesFrom(function.handle, arch.0, addr, &mut count)
|
||||
}
|
||||
};
|
||||
assert!(!refs.is_null());
|
||||
unsafe { Array::new(refs, count, self.to_owned()) }
|
||||
}
|
||||
|
||||
/// Current IL Address
|
||||
pub fn current_address(&self) -> u64 {
|
||||
unsafe { BNMediumLevelILGetCurrentAddress(self.handle) }
|
||||
}
|
||||
|
||||
/// Set the current IL Address
|
||||
pub fn set_current_address(&self, value: u64, arch: Option<CoreArchitecture>) {
|
||||
let arch = arch
|
||||
.map(|x| x.0)
|
||||
.unwrap_or_else(|| self.get_function().arch().0);
|
||||
unsafe { BNMediumLevelILSetCurrentAddress(self.handle, arch, value) }
|
||||
}
|
||||
|
||||
/// Returns the BasicBlock at the given MLIL `instruction`.
|
||||
pub fn basic_block_containing(
|
||||
&self,
|
||||
instruction: &MediumLevelILInstruction,
|
||||
) -> Option<BasicBlock<MediumLevelILBlock>> {
|
||||
let index = instruction.index;
|
||||
let block = unsafe { BNGetMediumLevelILBasicBlockForInstruction(self.handle, index) };
|
||||
(!block.is_null()).then(|| unsafe {
|
||||
BasicBlock::from_raw(
|
||||
block,
|
||||
MediumLevelILBlock {
|
||||
function: self.to_owned(),
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
/// ends the function and computes the list of basic blocks.
|
||||
pub fn finalize(&self) {
|
||||
unsafe { BNFinalizeMediumLevelILFunction(self.handle) }
|
||||
}
|
||||
|
||||
/// Generate SSA form given the current MLIL
|
||||
///
|
||||
/// * `analyze_conditionals` - whether or not to analyze conditionals
|
||||
/// * `handle_aliases` - whether or not to handle aliases
|
||||
/// * `known_not_aliases` - optional list of variables known to be not aliased
|
||||
/// * `known_aliases` - optional list of variables known to be aliased
|
||||
pub fn generate_ssa_form(
|
||||
&self,
|
||||
analyze_conditionals: bool,
|
||||
handle_aliases: bool,
|
||||
known_not_aliases: impl IntoIterator<Item = Variable>,
|
||||
known_aliases: impl IntoIterator<Item = Variable>,
|
||||
) {
|
||||
let mut known_not_aliases: Box<[_]> =
|
||||
known_not_aliases.into_iter().map(|x| x.raw()).collect();
|
||||
let mut known_aliases: Box<[_]> = known_aliases.into_iter().map(|x| x.raw()).collect();
|
||||
let (known_not_aliases_ptr, known_not_aliases_len) = if known_not_aliases.is_empty() {
|
||||
(core::ptr::null_mut(), 0)
|
||||
} else {
|
||||
(known_not_aliases.as_mut_ptr(), known_not_aliases.len())
|
||||
};
|
||||
let (known_aliases_ptr, known_aliases_len) = if known_not_aliases.is_empty() {
|
||||
(core::ptr::null_mut(), 0)
|
||||
} else {
|
||||
(known_aliases.as_mut_ptr(), known_aliases.len())
|
||||
};
|
||||
unsafe {
|
||||
BNGenerateMediumLevelILSSAForm(
|
||||
self.handle,
|
||||
analyze_conditionals,
|
||||
handle_aliases,
|
||||
known_not_aliases_ptr,
|
||||
known_not_aliases_len,
|
||||
known_aliases_ptr,
|
||||
known_aliases_len,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the instruction that contains the given SSA variable's definition.
|
||||
///
|
||||
/// Since SSA variables can only be defined once, this will return the single instruction where that occurs.
|
||||
/// For SSA variable version 0s, which don't have definitions, this will return None instead.
|
||||
pub fn ssa_variable_definition(&self, var: SSAVariable) -> Option<MediumLevelILInstruction> {
|
||||
let result = unsafe {
|
||||
BNGetMediumLevelILSSAVarDefinition(self.handle, &var.variable.raw(), var.version)
|
||||
};
|
||||
(result < self.instruction_count())
|
||||
.then(|| MediumLevelILInstruction::new(self.to_owned(), result))
|
||||
}
|
||||
|
||||
pub fn ssa_memory_definition(&self, version: usize) -> Option<MediumLevelILInstruction> {
|
||||
let result = unsafe { BNGetMediumLevelILSSAMemoryDefinition(self.handle, version) };
|
||||
(result < self.instruction_count())
|
||||
.then(|| MediumLevelILInstruction::new(self.to_owned(), result))
|
||||
}
|
||||
|
||||
///Gets all the instructions that use the given SSA variable.
|
||||
pub fn ssa_variable_uses(&self, ssa_var: SSAVariable) -> Array<MediumLevelILInstruction> {
|
||||
let mut count = 0;
|
||||
let uses = unsafe {
|
||||
BNGetMediumLevelILSSAVarUses(
|
||||
self.handle,
|
||||
&ssa_var.variable.raw(),
|
||||
ssa_var.version,
|
||||
&mut count,
|
||||
)
|
||||
};
|
||||
assert!(!uses.is_null());
|
||||
unsafe { Array::new(uses, count, self.to_owned()) }
|
||||
}
|
||||
|
||||
pub fn ssa_memory_uses(&self, version: usize) -> Array<MediumLevelILInstruction> {
|
||||
let mut count = 0;
|
||||
let uses = unsafe { BNGetMediumLevelILSSAMemoryUses(self.handle, version, &mut count) };
|
||||
assert!(!uses.is_null());
|
||||
unsafe { Array::new(uses, count, self.to_owned()) }
|
||||
}
|
||||
|
||||
/// determines if `ssa_var` is live at any point in the function
|
||||
pub fn is_ssa_variable_live(&self, ssa_var: SSAVariable) -> bool {
|
||||
unsafe {
|
||||
BNIsMediumLevelILSSAVarLive(self.handle, &ssa_var.variable.raw(), ssa_var.version)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn variable_definitions(&self, variable: Variable) -> Array<MediumLevelILInstruction> {
|
||||
let mut count = 0;
|
||||
let defs = unsafe {
|
||||
BNGetMediumLevelILVariableDefinitions(self.handle, &variable.raw(), &mut count)
|
||||
};
|
||||
unsafe { Array::new(defs, count, self.to_owned()) }
|
||||
}
|
||||
|
||||
pub fn variable_uses(&self, variable: Variable) -> Array<MediumLevelILInstruction> {
|
||||
let mut count = 0;
|
||||
let uses =
|
||||
unsafe { BNGetMediumLevelILVariableUses(self.handle, &variable.raw(), &mut count) };
|
||||
unsafe { Array::new(uses, count, self.to_owned()) }
|
||||
}
|
||||
|
||||
/// Computes the list of instructions for which `var` is live.
|
||||
/// If `include_last_use` is false, the last use of the variable will not be included in the
|
||||
/// list (this allows for easier computation of overlaps in liveness between two variables).
|
||||
/// If the variable is never used, this function will return an empty list.
|
||||
///
|
||||
/// `var` - the variable to query
|
||||
/// `include_last_use` - whether to include the last use of the variable in the list of instructions
|
||||
pub fn live_instruction_for_variable(
|
||||
&self,
|
||||
variable: Variable,
|
||||
include_last_user: bool,
|
||||
) -> Array<MediumLevelILInstruction> {
|
||||
let mut count = 0;
|
||||
let uses = unsafe {
|
||||
BNGetMediumLevelILLiveInstructionsForVariable(
|
||||
self.handle,
|
||||
&variable.raw(),
|
||||
include_last_user,
|
||||
&mut count,
|
||||
)
|
||||
};
|
||||
unsafe { Array::new(uses, count, self.to_owned()) }
|
||||
}
|
||||
|
||||
pub fn ssa_variable_value(&self, ssa_var: SSAVariable) -> RegisterValue {
|
||||
unsafe {
|
||||
BNGetMediumLevelILSSAVarValue(self.handle, &ssa_var.variable.raw(), ssa_var.version)
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn create_graph(&self, settings: Option<DisassemblySettings>) -> FlowGraph {
|
||||
let settings = settings.map(|x| x.handle).unwrap_or(core::ptr::null_mut());
|
||||
let graph = unsafe { BNCreateMediumLevelILFunctionGraph(self.handle, settings) };
|
||||
unsafe { FlowGraph::from_raw(graph) }
|
||||
}
|
||||
|
||||
/// This gets just the MLIL variables - you may be interested in the union
|
||||
/// of [MediumLevelILFunction::aliased_variables] and
|
||||
/// [crate::function::Function::parameter_variables] for all the
|
||||
/// variables used in the function
|
||||
pub fn variables(&self) -> Array<Variable> {
|
||||
let mut count = 0;
|
||||
let uses = unsafe { BNGetMediumLevelILVariables(self.handle, &mut count) };
|
||||
unsafe { Array::new(uses, count, ()) }
|
||||
}
|
||||
|
||||
/// This returns a list of Variables that are taken reference to and used
|
||||
/// elsewhere. You may also wish to consider [MediumLevelILFunction::variables]
|
||||
/// and [crate::function::Function::parameter_variables]
|
||||
pub fn aliased_variables(&self) -> Array<Variable> {
|
||||
let mut count = 0;
|
||||
let uses = unsafe { BNGetMediumLevelILAliasedVariables(self.handle, &mut count) };
|
||||
unsafe { Array::new(uses, count, ()) }
|
||||
}
|
||||
|
||||
/// This gets just the MLIL SSA variables - you may be interested in the
|
||||
/// union of [MediumLevelILFunction::aliased_variables] and
|
||||
/// [crate::function::Function::parameter_variables] for all the
|
||||
/// variables used in the function.
|
||||
pub fn ssa_variables(&self) -> Array<Array<SSAVariable>> {
|
||||
let mut count = 0;
|
||||
let vars = unsafe { BNGetMediumLevelILVariables(self.handle, &mut count) };
|
||||
unsafe { Array::new(vars, count, self.to_owned()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl ToOwned for MediumLevelILFunction {
|
||||
@@ -118,3 +603,125 @@ impl core::fmt::Debug for MediumLevelILFunction {
|
||||
write!(f, "<mlil func handle {:p}>", self.handle)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MediumLevelILInstructionList<'a> {
|
||||
mlil: &'a MediumLevelILFunction,
|
||||
ptr: *mut usize,
|
||||
instr_idxs: core::slice::Iter<'a, usize>,
|
||||
}
|
||||
|
||||
impl Drop for MediumLevelILInstructionList<'_> {
|
||||
fn drop(&mut self) {
|
||||
unsafe { BNFreeILInstructionList(self.ptr) };
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for MediumLevelILInstructionList<'_> {
|
||||
type Item = MediumLevelILInstruction;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.instr_idxs
|
||||
.next()
|
||||
.map(|i| self.mlil.instruction_from_instruction_idx(*i))
|
||||
}
|
||||
}
|
||||
|
||||
impl DoubleEndedIterator for MediumLevelILInstructionList<'_> {
|
||||
fn next_back(&mut self) -> Option<Self::Item> {
|
||||
self.instr_idxs
|
||||
.next_back()
|
||||
.map(|i| self.mlil.instruction_from_instruction_idx(*i))
|
||||
}
|
||||
}
|
||||
|
||||
impl ExactSizeIterator for MediumLevelILInstructionList<'_> {}
|
||||
impl core::iter::FusedIterator for MediumLevelILInstructionList<'_> {}
|
||||
|
||||
/////////////////////////
|
||||
// FunctionGraphType
|
||||
|
||||
pub type FunctionGraphType = binaryninjacore_sys::BNFunctionGraphType;
|
||||
|
||||
/////////////////////////
|
||||
// ILReferenceSource
|
||||
|
||||
pub struct ILReferenceSource {
|
||||
mlil: Ref<MediumLevelILFunction>,
|
||||
_func: Ref<Function>,
|
||||
_arch: CoreArchitecture,
|
||||
addr: u64,
|
||||
type_: FunctionGraphType,
|
||||
expr_id: usize,
|
||||
}
|
||||
|
||||
impl ILReferenceSource {
|
||||
unsafe fn from_raw(value: BNILReferenceSource, mlil: Ref<MediumLevelILFunction>) -> Self {
|
||||
Self {
|
||||
mlil,
|
||||
_func: Function::from_raw(value.func),
|
||||
_arch: CoreArchitecture::from_raw(value.arch),
|
||||
addr: value.addr,
|
||||
type_: value.type_,
|
||||
expr_id: value.exprId,
|
||||
}
|
||||
}
|
||||
pub fn addr(&self) -> u64 {
|
||||
self.addr
|
||||
}
|
||||
pub fn graph_type(&self) -> FunctionGraphType {
|
||||
self.type_
|
||||
}
|
||||
pub fn expr(&self) -> MediumLevelILInstruction {
|
||||
self.mlil.instruction_from_idx(self.expr_id)
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for ILReferenceSource {
|
||||
type Raw = BNILReferenceSource;
|
||||
type Context = Ref<MediumLevelILFunction>;
|
||||
type Wrapped<'a> = Self;
|
||||
}
|
||||
unsafe impl CoreArrayProviderInner for ILReferenceSource {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeILReferences(raw, count)
|
||||
}
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
Self::from_raw(*raw, context.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////
|
||||
// VariableReferenceSource
|
||||
|
||||
pub struct VariableReferenceSource {
|
||||
var: Variable,
|
||||
source: ILReferenceSource,
|
||||
}
|
||||
|
||||
impl VariableReferenceSource {
|
||||
pub fn variable(&self) -> &Variable {
|
||||
&self.var
|
||||
}
|
||||
pub fn source(&self) -> &ILReferenceSource {
|
||||
&self.source
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for VariableReferenceSource {
|
||||
type Raw = BNVariableReferenceSource;
|
||||
type Context = Ref<MediumLevelILFunction>;
|
||||
type Wrapped<'a> = Self;
|
||||
}
|
||||
|
||||
unsafe impl CoreArrayProviderInner for VariableReferenceSource {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeVariableReferenceSourceList(raw, count)
|
||||
}
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
Self {
|
||||
var: Variable::from_raw(raw.var),
|
||||
source: ILReferenceSource::from_raw(raw.source, context.to_owned()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use binaryninjacore_sys::BNFromVariableIdentifier;
|
||||
use binaryninjacore_sys::BNGetMediumLevelILByIndex;
|
||||
use binaryninjacore_sys::BNMediumLevelILInstruction;
|
||||
use binaryninjacore_sys::BNMediumLevelILOperation;
|
||||
use binaryninjacore_sys::*;
|
||||
|
||||
use crate::architecture::CoreIntrinsic;
|
||||
use crate::disassembly::InstructionTextToken;
|
||||
use crate::operand_iter::OperandIter;
|
||||
use crate::rc::Ref;
|
||||
use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref};
|
||||
use crate::types::{
|
||||
ConstantData, ILIntrinsic, RegisterValue, RegisterValueType, SSAVariable, Variable,
|
||||
Conf, ConstantData, DataFlowQueryOption, ILBranchDependence, PossibleValueSet,
|
||||
RegisterValue, RegisterValueType, SSAVariable, Type, Variable,
|
||||
};
|
||||
|
||||
use super::lift::*;
|
||||
@@ -17,6 +17,8 @@ use super::MediumLevelILFunction;
|
||||
pub struct MediumLevelILInstruction {
|
||||
pub function: Ref<MediumLevelILFunction>,
|
||||
pub address: u64,
|
||||
pub index: usize,
|
||||
pub size: usize,
|
||||
pub kind: MediumLevelILInstructionKind,
|
||||
}
|
||||
|
||||
@@ -166,8 +168,8 @@ impl core::fmt::Debug for MediumLevelILInstruction {
|
||||
}
|
||||
|
||||
impl MediumLevelILInstruction {
|
||||
pub(crate) fn new(function: Ref<MediumLevelILFunction>, idx: usize) -> Self {
|
||||
let op = unsafe { BNGetMediumLevelILByIndex(function.handle, idx) };
|
||||
pub(crate) fn new(function: Ref<MediumLevelILFunction>, index: usize) -> Self {
|
||||
let op = unsafe { BNGetMediumLevelILByIndex(function.handle, index) };
|
||||
use BNMediumLevelILOperation::*;
|
||||
use MediumLevelILInstructionKind as Op;
|
||||
let kind = match op.operation {
|
||||
@@ -703,7 +705,12 @@ impl MediumLevelILInstruction {
|
||||
}),
|
||||
// translated directly into a list for Expression or Variables
|
||||
// TODO MLIL_MEMORY_INTRINSIC_SSA needs to be handled properly
|
||||
MLIL_CALL_OUTPUT | MLIL_CALL_PARAM | MLIL_CALL_PARAM_SSA | MLIL_CALL_OUTPUT_SSA | MLIL_MEMORY_INTRINSIC_OUTPUT_SSA | MLIL_MEMORY_INTRINSIC_SSA => {
|
||||
MLIL_CALL_OUTPUT
|
||||
| MLIL_CALL_PARAM
|
||||
| MLIL_CALL_PARAM_SSA
|
||||
| MLIL_CALL_OUTPUT_SSA
|
||||
| MLIL_MEMORY_INTRINSIC_OUTPUT_SSA
|
||||
| MLIL_MEMORY_INTRINSIC_SSA => {
|
||||
unreachable!()
|
||||
}
|
||||
};
|
||||
@@ -711,6 +718,8 @@ impl MediumLevelILInstruction {
|
||||
Self {
|
||||
function,
|
||||
address: op.address,
|
||||
index,
|
||||
size: op.size,
|
||||
kind,
|
||||
}
|
||||
}
|
||||
@@ -897,7 +906,7 @@ impl MediumLevelILInstruction {
|
||||
output: OperandIter::new(&*self.function, op.first_output, op.num_outputs)
|
||||
.vars()
|
||||
.collect(),
|
||||
intrinsic: ILIntrinsic::new(self.function.get_function().arch(), op.intrinsic),
|
||||
intrinsic: CoreIntrinsic(self.function.get_function().arch().0, op.intrinsic),
|
||||
params: OperandIter::new(&*self.function, op.first_param, op.num_params)
|
||||
.exprs()
|
||||
.map(|expr| expr.lift())
|
||||
@@ -916,7 +925,7 @@ impl MediumLevelILInstruction {
|
||||
output: OperandIter::new(&*self.function, op.first_output, op.num_outputs)
|
||||
.ssa_vars()
|
||||
.collect(),
|
||||
intrinsic: ILIntrinsic::new(self.function.get_function().arch(), op.intrinsic),
|
||||
intrinsic: CoreIntrinsic(self.function.get_function().arch().0, op.intrinsic),
|
||||
params: OperandIter::new(&*self.function, op.first_param, op.num_params)
|
||||
.exprs()
|
||||
.map(|expr| expr.lift())
|
||||
@@ -1019,10 +1028,394 @@ impl MediumLevelILInstruction {
|
||||
MediumLevelILLiftedInstruction {
|
||||
function: self.function.clone(),
|
||||
address: self.address,
|
||||
index: self.index,
|
||||
size: self.size,
|
||||
kind,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tokens(&self) -> Array<InstructionTextToken> {
|
||||
let mut count = 0;
|
||||
let mut tokens = core::ptr::null_mut();
|
||||
assert!(unsafe {
|
||||
BNGetMediumLevelILExprText(
|
||||
self.function.handle,
|
||||
self.function.get_function().arch().0,
|
||||
self.index,
|
||||
&mut tokens,
|
||||
&mut count,
|
||||
core::ptr::null_mut(),
|
||||
)
|
||||
});
|
||||
unsafe { Array::new(tokens, count, ()) }
|
||||
}
|
||||
|
||||
/// Value of expression if constant or a known value
|
||||
pub fn value(&self) -> RegisterValue {
|
||||
unsafe { BNGetMediumLevelILExprValue(self.function.handle, self.index) }.into()
|
||||
}
|
||||
|
||||
/// Possible values of expression using path-sensitive static data flow analysis
|
||||
pub fn possible_values(&self, options: Option<&[DataFlowQueryOption]>) -> PossibleValueSet {
|
||||
let options_ptr = options
|
||||
.map(|op| op.as_ptr() as *mut DataFlowQueryOption)
|
||||
.unwrap_or(core::ptr::null_mut());
|
||||
let options_len = options.map(|op| op.len()).unwrap_or(0);
|
||||
let mut value = unsafe {
|
||||
BNGetMediumLevelILPossibleExprValues(
|
||||
self.function.handle,
|
||||
self.index,
|
||||
options_ptr,
|
||||
options_len,
|
||||
)
|
||||
};
|
||||
let result = unsafe { PossibleValueSet::from_raw(value) };
|
||||
unsafe { BNFreePossibleValueSet(&mut value) }
|
||||
result
|
||||
}
|
||||
|
||||
pub fn possible_ssa_variable_values(
|
||||
&self,
|
||||
ssa_var: SSAVariable,
|
||||
options: Option<&[DataFlowQueryOption]>,
|
||||
) -> PossibleValueSet {
|
||||
let options_ptr = options
|
||||
.map(|op| op.as_ptr() as *mut DataFlowQueryOption)
|
||||
.unwrap_or(core::ptr::null_mut());
|
||||
let options_len = options.map(|op| op.len()).unwrap_or(0);
|
||||
let mut value = unsafe {
|
||||
BNGetMediumLevelILPossibleSSAVarValues(
|
||||
self.function.handle,
|
||||
&ssa_var.variable.raw(),
|
||||
ssa_var.version,
|
||||
self.index,
|
||||
options_ptr,
|
||||
options_len,
|
||||
)
|
||||
};
|
||||
let result = unsafe { PossibleValueSet::from_raw(value) };
|
||||
unsafe { BNFreePossibleValueSet(&mut value) }
|
||||
result
|
||||
}
|
||||
|
||||
/// return the variable version used at this instruction
|
||||
pub fn ssa_variable_version(&self, var: Variable) -> SSAVariable {
|
||||
let version = unsafe {
|
||||
BNGetMediumLevelILSSAVarVersionAtILInstruction(
|
||||
self.function.handle,
|
||||
&var.raw(),
|
||||
self.index,
|
||||
)
|
||||
};
|
||||
SSAVariable::new(var, version)
|
||||
}
|
||||
|
||||
/// Set of branching instructions that must take the true or false path to reach this instruction
|
||||
pub fn branch_dependence(&self) -> Array<BranchDependence> {
|
||||
let mut count = 0;
|
||||
let deps = unsafe {
|
||||
BNGetAllMediumLevelILBranchDependence(self.function.handle, self.index, &mut count)
|
||||
};
|
||||
assert!(!deps.is_null());
|
||||
unsafe { Array::new(deps, count, self.function.clone()) }
|
||||
}
|
||||
|
||||
pub fn branch_dependence_at(&self, instruction: MediumLevelILInstruction) -> BranchDependence {
|
||||
let deps = unsafe {
|
||||
BNGetMediumLevelILBranchDependence(self.function.handle, self.index, instruction.index)
|
||||
};
|
||||
BranchDependence {
|
||||
instruction,
|
||||
dependence: deps,
|
||||
}
|
||||
}
|
||||
|
||||
/// Version of active memory contents in SSA form for this instruction
|
||||
pub fn ssa_memory_version(&self) -> usize {
|
||||
unsafe {
|
||||
BNGetMediumLevelILSSAMemoryVersionAtILInstruction(self.function.handle, self.index)
|
||||
}
|
||||
}
|
||||
|
||||
/// Type of expression
|
||||
pub fn expr_type(&self) -> Option<Conf<Ref<Type>>> {
|
||||
let result = unsafe { BNGetMediumLevelILExprType(self.function.handle, self.index) };
|
||||
(!result.type_.is_null()).then(|| {
|
||||
Conf::new(
|
||||
unsafe { Type::ref_from_raw(result.type_) },
|
||||
result.confidence,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/// Set type of expression
|
||||
///
|
||||
/// This API is only meant for workflows or for debugging purposes, since the changes they make are not persistent
|
||||
/// and get lost after a database save and reload. To make persistent changes to the analysis, one should use other
|
||||
/// APIs to, for example, change the type of variables. The analysis will then propagate the type of the variable
|
||||
/// and update the type of related expressions.
|
||||
pub fn set_expr_type<'a, T: Into<Conf<&'a Type>>>(&self, value: T) {
|
||||
let type_: Conf<&'a Type> = value.into();
|
||||
let mut type_raw: BNTypeWithConfidence = BNTypeWithConfidence {
|
||||
type_: type_.contents.handle,
|
||||
confidence: type_.confidence,
|
||||
};
|
||||
unsafe { BNSetMediumLevelILExprType(self.function.handle, self.index, &mut type_raw) }
|
||||
}
|
||||
|
||||
pub fn variable_for_register(&self, reg_id: u32) -> Variable {
|
||||
let result = unsafe {
|
||||
BNGetMediumLevelILVariableForRegisterAtInstruction(
|
||||
self.function.handle,
|
||||
reg_id,
|
||||
self.index,
|
||||
)
|
||||
};
|
||||
unsafe { Variable::from_raw(result) }
|
||||
}
|
||||
|
||||
pub fn variable_for_flag(&self, flag_id: u32) -> Variable {
|
||||
let result = unsafe {
|
||||
BNGetMediumLevelILVariableForFlagAtInstruction(
|
||||
self.function.handle,
|
||||
flag_id,
|
||||
self.index,
|
||||
)
|
||||
};
|
||||
unsafe { Variable::from_raw(result) }
|
||||
}
|
||||
|
||||
pub fn variable_for_stack_location(&self, offset: i64) -> Variable {
|
||||
let result = unsafe {
|
||||
BNGetMediumLevelILVariableForStackLocationAtInstruction(
|
||||
self.function.handle,
|
||||
offset,
|
||||
self.index,
|
||||
)
|
||||
};
|
||||
unsafe { Variable::from_raw(result) }
|
||||
}
|
||||
|
||||
pub fn register_value(&self, reg_id: u32) -> RegisterValue {
|
||||
unsafe {
|
||||
BNGetMediumLevelILRegisterValueAtInstruction(self.function.handle, reg_id, self.index)
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn register_value_after(&self, reg_id: u32) -> RegisterValue {
|
||||
unsafe {
|
||||
BNGetMediumLevelILRegisterValueAfterInstruction(
|
||||
self.function.handle,
|
||||
reg_id,
|
||||
self.index,
|
||||
)
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn possible_register_values(
|
||||
&self,
|
||||
reg_id: u32,
|
||||
options: Option<&[DataFlowQueryOption]>,
|
||||
) -> PossibleValueSet {
|
||||
let options_ptr = options
|
||||
.map(|op| op.as_ptr() as *mut DataFlowQueryOption)
|
||||
.unwrap_or(core::ptr::null_mut());
|
||||
let options_len = options.map(|op| op.len()).unwrap_or(0);
|
||||
let mut value = unsafe {
|
||||
BNGetMediumLevelILPossibleRegisterValuesAtInstruction(
|
||||
self.function.handle,
|
||||
reg_id,
|
||||
self.index,
|
||||
options_ptr,
|
||||
options_len,
|
||||
)
|
||||
};
|
||||
let result = unsafe { PossibleValueSet::from_raw(value) };
|
||||
unsafe { BNFreePossibleValueSet(&mut value) }
|
||||
result
|
||||
}
|
||||
|
||||
pub fn possible_register_values_after(
|
||||
&self,
|
||||
reg_id: u32,
|
||||
options: Option<&[DataFlowQueryOption]>,
|
||||
) -> PossibleValueSet {
|
||||
let options_ptr = options
|
||||
.map(|op| op.as_ptr() as *mut DataFlowQueryOption)
|
||||
.unwrap_or(core::ptr::null_mut());
|
||||
let options_len = options.map(|op| op.len()).unwrap_or(0);
|
||||
let mut value = unsafe {
|
||||
BNGetMediumLevelILPossibleRegisterValuesAfterInstruction(
|
||||
self.function.handle,
|
||||
reg_id,
|
||||
self.index,
|
||||
options_ptr,
|
||||
options_len,
|
||||
)
|
||||
};
|
||||
let result = unsafe { PossibleValueSet::from_raw(value) };
|
||||
unsafe { BNFreePossibleValueSet(&mut value) }
|
||||
result
|
||||
}
|
||||
|
||||
pub fn flag_value(&self, flag_id: u32) -> RegisterValue {
|
||||
unsafe {
|
||||
BNGetMediumLevelILFlagValueAtInstruction(self.function.handle, flag_id, self.index)
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn flag_value_after(&self, flag_id: u32) -> RegisterValue {
|
||||
unsafe {
|
||||
BNGetMediumLevelILFlagValueAfterInstruction(self.function.handle, flag_id, self.index)
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn possible_flag_values(
|
||||
&self,
|
||||
flag_id: u32,
|
||||
options: Option<&[DataFlowQueryOption]>,
|
||||
) -> PossibleValueSet {
|
||||
let options_ptr = options
|
||||
.map(|op| op.as_ptr() as *mut DataFlowQueryOption)
|
||||
.unwrap_or(core::ptr::null_mut());
|
||||
let options_len = options.map(|op| op.len()).unwrap_or(0);
|
||||
let mut value = unsafe {
|
||||
BNGetMediumLevelILPossibleFlagValuesAtInstruction(
|
||||
self.function.handle,
|
||||
flag_id,
|
||||
self.index,
|
||||
options_ptr,
|
||||
options_len,
|
||||
)
|
||||
};
|
||||
let result = unsafe { PossibleValueSet::from_raw(value) };
|
||||
unsafe { BNFreePossibleValueSet(&mut value) }
|
||||
result
|
||||
}
|
||||
|
||||
pub fn possible_flag_values_after(
|
||||
&self,
|
||||
flag_id: u32,
|
||||
options: Option<&[DataFlowQueryOption]>,
|
||||
) -> PossibleValueSet {
|
||||
let options_ptr = options
|
||||
.map(|op| op.as_ptr() as *mut DataFlowQueryOption)
|
||||
.unwrap_or(core::ptr::null_mut());
|
||||
let options_len = options.map(|op| op.len()).unwrap_or(0);
|
||||
let mut value = unsafe {
|
||||
BNGetMediumLevelILPossibleFlagValuesAfterInstruction(
|
||||
self.function.handle,
|
||||
flag_id,
|
||||
self.index,
|
||||
options_ptr,
|
||||
options_len,
|
||||
)
|
||||
};
|
||||
let result = unsafe { PossibleValueSet::from_raw(value) };
|
||||
unsafe { BNFreePossibleValueSet(&mut value) }
|
||||
result
|
||||
}
|
||||
|
||||
pub fn stack_contents(&self, offset: i64, size: usize) -> RegisterValue {
|
||||
unsafe {
|
||||
BNGetMediumLevelILStackContentsAtInstruction(
|
||||
self.function.handle,
|
||||
offset,
|
||||
size,
|
||||
self.index,
|
||||
)
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn stack_contents_after(&self, offset: i64, size: usize) -> RegisterValue {
|
||||
unsafe {
|
||||
BNGetMediumLevelILStackContentsAfterInstruction(
|
||||
self.function.handle,
|
||||
offset,
|
||||
size,
|
||||
self.index,
|
||||
)
|
||||
}
|
||||
.into()
|
||||
}
|
||||
|
||||
pub fn possible_stack_contents(
|
||||
&self,
|
||||
offset: i64,
|
||||
size: usize,
|
||||
options: Option<&[DataFlowQueryOption]>,
|
||||
) -> PossibleValueSet {
|
||||
let options_ptr = options
|
||||
.map(|op| op.as_ptr() as *mut DataFlowQueryOption)
|
||||
.unwrap_or(core::ptr::null_mut());
|
||||
let options_len = options.map(|op| op.len()).unwrap_or(0);
|
||||
let mut value = unsafe {
|
||||
BNGetMediumLevelILPossibleStackContentsAtInstruction(
|
||||
self.function.handle,
|
||||
offset,
|
||||
size,
|
||||
self.index,
|
||||
options_ptr,
|
||||
options_len,
|
||||
)
|
||||
};
|
||||
let result = unsafe { PossibleValueSet::from_raw(value) };
|
||||
unsafe { BNFreePossibleValueSet(&mut value) }
|
||||
result
|
||||
}
|
||||
|
||||
pub fn possible_stack_contents_after(
|
||||
&self,
|
||||
offset: i64,
|
||||
size: usize,
|
||||
options: Option<&[DataFlowQueryOption]>,
|
||||
) -> PossibleValueSet {
|
||||
let options_ptr = options
|
||||
.map(|op| op.as_ptr() as *mut DataFlowQueryOption)
|
||||
.unwrap_or(core::ptr::null_mut());
|
||||
let options_len = options.map(|op| op.len()).unwrap_or(0);
|
||||
let mut value = unsafe {
|
||||
BNGetMediumLevelILPossibleStackContentsAfterInstruction(
|
||||
self.function.handle,
|
||||
offset,
|
||||
size,
|
||||
self.index,
|
||||
options_ptr,
|
||||
options_len,
|
||||
)
|
||||
};
|
||||
let result = unsafe { PossibleValueSet::from_raw(value) };
|
||||
unsafe { BNFreePossibleValueSet(&mut value) }
|
||||
result
|
||||
}
|
||||
|
||||
/// Gets the unique variable for a definition instruction. This unique variable can be passed
|
||||
/// to [crate::function::Function::split_variable] to split a variable at a definition. The given `var` is the
|
||||
/// assigned variable to query.
|
||||
///
|
||||
/// * `var` - variable to query
|
||||
pub fn split_var_for_definition(&self, var: Variable) -> Variable {
|
||||
let index = unsafe {
|
||||
BNGetDefaultIndexForMediumLevelILVariableDefinition(
|
||||
self.function.handle,
|
||||
&var.raw(),
|
||||
self.index,
|
||||
)
|
||||
};
|
||||
Variable::new(var.t, index, var.storage)
|
||||
}
|
||||
|
||||
/// alias for [MediumLevelILInstruction::split_var_for_definition]
|
||||
#[inline]
|
||||
pub fn get_split_var_for_definition(&self, var: &Variable) -> Variable {
|
||||
self.split_var_for_definition(*var)
|
||||
}
|
||||
|
||||
fn lift_operand(&self, expr_idx: usize) -> Box<MediumLevelILLiftedInstruction> {
|
||||
Box::new(self.function.lifted_instruction_from_idx(expr_idx))
|
||||
}
|
||||
@@ -1096,6 +1489,22 @@ impl MediumLevelILInstruction {
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for MediumLevelILInstruction {
|
||||
type Raw = usize;
|
||||
type Context = Ref<MediumLevelILFunction>;
|
||||
type Wrapped<'a> = Self;
|
||||
}
|
||||
|
||||
unsafe impl CoreArrayProviderInner for MediumLevelILInstruction {
|
||||
unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
|
||||
BNFreeILInstructionList(raw)
|
||||
}
|
||||
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
context.instruction_from_idx(*raw)
|
||||
}
|
||||
}
|
||||
|
||||
fn get_float(value: u64, size: usize) -> f64 {
|
||||
match size {
|
||||
4 => f32::from_bits(value as u32) as f64,
|
||||
@@ -1110,7 +1519,7 @@ fn get_raw_operation(function: &MediumLevelILFunction, idx: usize) -> BNMediumLe
|
||||
}
|
||||
|
||||
fn get_var(id: u64) -> Variable {
|
||||
unsafe { Variable::from_raw(BNFromVariableIdentifier(id)) }
|
||||
unsafe { Variable::from_identifier(id) }
|
||||
}
|
||||
|
||||
fn get_var_ssa(id: u64, version: usize) -> SSAVariable {
|
||||
@@ -1149,3 +1558,28 @@ fn get_call_params_ssa(
|
||||
assert_eq!(op.operation, BNMediumLevelILOperation::MLIL_CALL_PARAM_SSA);
|
||||
OperandIter::new(function, op.operands[2] as usize, op.operands[1] as usize).exprs()
|
||||
}
|
||||
|
||||
/// Conditional branching instruction and an expected conditional result
|
||||
pub struct BranchDependence {
|
||||
pub instruction: MediumLevelILInstruction,
|
||||
pub dependence: ILBranchDependence,
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for BranchDependence {
|
||||
type Raw = BNILBranchInstructionAndDependence;
|
||||
type Context = Ref<MediumLevelILFunction>;
|
||||
type Wrapped<'a> = Self;
|
||||
}
|
||||
|
||||
unsafe impl CoreArrayProviderInner for BranchDependence {
|
||||
unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
|
||||
unsafe { BNFreeILBranchDependenceList(raw) };
|
||||
}
|
||||
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
Self {
|
||||
instruction: MediumLevelILInstruction::new(context.clone(), raw.branch),
|
||||
dependence: raw.dependence,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
143
src/mlil/lift.rs
143
src/mlil/lift.rs
@@ -1,7 +1,8 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::architecture::CoreIntrinsic;
|
||||
use crate::rc::Ref;
|
||||
use crate::types::{ConstantData, ILIntrinsic, SSAVariable, Variable};
|
||||
use crate::types::{ConstantData, SSAVariable, Variable};
|
||||
|
||||
use super::operation::*;
|
||||
use super::MediumLevelILFunction;
|
||||
@@ -9,7 +10,7 @@ use super::MediumLevelILFunction;
|
||||
#[derive(Clone)]
|
||||
pub enum MediumLevelILLiftedOperand {
|
||||
ConstantData(ConstantData),
|
||||
Intrinsic(ILIntrinsic),
|
||||
Intrinsic(CoreIntrinsic),
|
||||
Expr(MediumLevelILLiftedInstruction),
|
||||
ExprList(Vec<MediumLevelILLiftedInstruction>),
|
||||
Float(f64),
|
||||
@@ -26,6 +27,8 @@ pub enum MediumLevelILLiftedOperand {
|
||||
pub struct MediumLevelILLiftedInstruction {
|
||||
pub function: Ref<MediumLevelILFunction>,
|
||||
pub address: u64,
|
||||
pub index: usize,
|
||||
pub size: usize,
|
||||
pub kind: MediumLevelILLiftedInstructionKind,
|
||||
}
|
||||
|
||||
@@ -164,6 +167,142 @@ pub enum MediumLevelILLiftedInstructionKind {
|
||||
}
|
||||
|
||||
impl MediumLevelILLiftedInstruction {
|
||||
pub fn name(&self) -> &'static str {
|
||||
use MediumLevelILLiftedInstructionKind::*;
|
||||
match self.kind {
|
||||
Nop => "Nop",
|
||||
Noret => "Noret",
|
||||
Bp => "Bp",
|
||||
Undef => "Undef",
|
||||
Unimpl => "Unimpl",
|
||||
If(_) => "If",
|
||||
FloatConst(_) => "FloatConst",
|
||||
Const(_) => "Const",
|
||||
ConstPtr(_) => "ConstPtr",
|
||||
Import(_) => "Import",
|
||||
ExternPtr(_) => "ExternPtr",
|
||||
ConstData(_) => "ConstData",
|
||||
Jump(_) => "Jump",
|
||||
RetHint(_) => "RetHint",
|
||||
StoreSsa(_) => "StoreSsa",
|
||||
StoreStructSsa(_) => "StoreStructSsa",
|
||||
StoreStruct(_) => "StoreStruct",
|
||||
Store(_) => "Store",
|
||||
JumpTo(_) => "JumpTo",
|
||||
Goto(_) => "Goto",
|
||||
FreeVarSlot(_) => "FreeVarSlot",
|
||||
SetVarField(_) => "SetVarField",
|
||||
SetVar(_) => "SetVar",
|
||||
FreeVarSlotSsa(_) => "FreeVarSlotSsa",
|
||||
SetVarSsaField(_) => "SetVarSsaField",
|
||||
SetVarAliasedField(_) => "SetVarAliasedField",
|
||||
SetVarAliased(_) => "SetVarAliased",
|
||||
SetVarSsa(_) => "SetVarSsa",
|
||||
VarPhi(_) => "VarPhi",
|
||||
MemPhi(_) => "MemPhi",
|
||||
VarSplit(_) => "VarSplit",
|
||||
SetVarSplit(_) => "SetVarSplit",
|
||||
VarSplitSsa(_) => "VarSplitSsa",
|
||||
SetVarSplitSsa(_) => "SetVarSplitSsa",
|
||||
Add(_) => "Add",
|
||||
Sub(_) => "Sub",
|
||||
And(_) => "And",
|
||||
Or(_) => "Or",
|
||||
Xor(_) => "Xor",
|
||||
Lsl(_) => "Lsl",
|
||||
Lsr(_) => "Lsr",
|
||||
Asr(_) => "Asr",
|
||||
Rol(_) => "Rol",
|
||||
Ror(_) => "Ror",
|
||||
Mul(_) => "Mul",
|
||||
MuluDp(_) => "MuluDp",
|
||||
MulsDp(_) => "MulsDp",
|
||||
Divu(_) => "Divu",
|
||||
DivuDp(_) => "DivuDp",
|
||||
Divs(_) => "Divs",
|
||||
DivsDp(_) => "DivsDp",
|
||||
Modu(_) => "Modu",
|
||||
ModuDp(_) => "ModuDp",
|
||||
Mods(_) => "Mods",
|
||||
ModsDp(_) => "ModsDp",
|
||||
CmpE(_) => "CmpE",
|
||||
CmpNe(_) => "CmpNe",
|
||||
CmpSlt(_) => "CmpSlt",
|
||||
CmpUlt(_) => "CmpUlt",
|
||||
CmpSle(_) => "CmpSle",
|
||||
CmpUle(_) => "CmpUle",
|
||||
CmpSge(_) => "CmpSge",
|
||||
CmpUge(_) => "CmpUge",
|
||||
CmpSgt(_) => "CmpSgt",
|
||||
CmpUgt(_) => "CmpUgt",
|
||||
TestBit(_) => "TestBit",
|
||||
AddOverflow(_) => "AddOverflow",
|
||||
FcmpE(_) => "FcmpE",
|
||||
FcmpNe(_) => "FcmpNe",
|
||||
FcmpLt(_) => "FcmpLt",
|
||||
FcmpLe(_) => "FcmpLe",
|
||||
FcmpGe(_) => "FcmpGe",
|
||||
FcmpGt(_) => "FcmpGt",
|
||||
FcmpO(_) => "FcmpO",
|
||||
FcmpUo(_) => "FcmpUo",
|
||||
Fadd(_) => "Fadd",
|
||||
Fsub(_) => "Fsub",
|
||||
Fmul(_) => "Fmul",
|
||||
Fdiv(_) => "Fdiv",
|
||||
Adc(_) => "Adc",
|
||||
Sbb(_) => "Sbb",
|
||||
Rlc(_) => "Rlc",
|
||||
Rrc(_) => "Rrc",
|
||||
Call(_) => "Call",
|
||||
Tailcall(_) => "Tailcall",
|
||||
Syscall(_) => "Syscall",
|
||||
Intrinsic(_) => "Intrinsic",
|
||||
IntrinsicSsa(_) => "IntrinsicSsa",
|
||||
CallSsa(_) => "CallSsa",
|
||||
TailcallSsa(_) => "TailcallSsa",
|
||||
CallUntypedSsa(_) => "CallUntypedSsa",
|
||||
TailcallUntypedSsa(_) => "TailcallUntypedSsa",
|
||||
SyscallSsa(_) => "SyscallSsa",
|
||||
SyscallUntypedSsa(_) => "SyscallUntypedSsa",
|
||||
CallUntyped(_) => "CallUntyped",
|
||||
TailcallUntyped(_) => "TailcallUntyped",
|
||||
SyscallUntyped(_) => "SyscallUntyped",
|
||||
SeparateParamList(_) => "SeparateParamList",
|
||||
SharedParamSlot(_) => "SharedParamSlot",
|
||||
Neg(_) => "Neg",
|
||||
Not(_) => "Not",
|
||||
Sx(_) => "Sx",
|
||||
Zx(_) => "Zx",
|
||||
LowPart(_) => "LowPart",
|
||||
BoolToInt(_) => "BoolToInt",
|
||||
UnimplMem(_) => "UnimplMem",
|
||||
Fsqrt(_) => "Fsqrt",
|
||||
Fneg(_) => "Fneg",
|
||||
Fabs(_) => "Fabs",
|
||||
FloatToInt(_) => "FloatToInt",
|
||||
IntToFloat(_) => "IntToFloat",
|
||||
FloatConv(_) => "FloatConv",
|
||||
RoundToInt(_) => "RoundToInt",
|
||||
Floor(_) => "Floor",
|
||||
Ceil(_) => "Ceil",
|
||||
Ftrunc(_) => "Ftrunc",
|
||||
Load(_) => "Load",
|
||||
LoadStruct(_) => "LoadStruct",
|
||||
LoadStructSsa(_) => "LoadStructSsa",
|
||||
LoadSsa(_) => "LoadSsa",
|
||||
Ret(_) => "Ret",
|
||||
Var(_) => "Var",
|
||||
AddressOf(_) => "AddressOf",
|
||||
VarField(_) => "VarField",
|
||||
AddressOfField(_) => "AddressOfField",
|
||||
VarSsa(_) => "VarSsa",
|
||||
VarAliased(_) => "VarAliased",
|
||||
VarSsaField(_) => "VarSsaField",
|
||||
VarAliasedField(_) => "VarAliasedField",
|
||||
Trap(_) => "Trap",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn operands(&self) -> Vec<(&'static str, MediumLevelILLiftedOperand)> {
|
||||
use MediumLevelILLiftedInstructionKind::*;
|
||||
use MediumLevelILLiftedOperand as Operand;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
use crate::types::{ConstantData, ILIntrinsic, SSAVariable, Variable};
|
||||
use crate::{architecture::CoreIntrinsic, types::{ConstantData, SSAVariable, Variable}};
|
||||
|
||||
use super::MediumLevelILLiftedInstruction;
|
||||
|
||||
@@ -355,7 +355,7 @@ pub struct Intrinsic {
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct LiftedIntrinsic {
|
||||
pub output: Vec<Variable>,
|
||||
pub intrinsic: ILIntrinsic,
|
||||
pub intrinsic: CoreIntrinsic,
|
||||
pub params: Vec<MediumLevelILLiftedInstruction>,
|
||||
}
|
||||
|
||||
@@ -371,7 +371,7 @@ pub struct IntrinsicSsa {
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct LiftedIntrinsicSsa {
|
||||
pub output: Vec<SSAVariable>,
|
||||
pub intrinsic: ILIntrinsic,
|
||||
pub intrinsic: CoreIntrinsic,
|
||||
pub params: Vec<MediumLevelILLiftedInstruction>,
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
use binaryninjacore_sys::BNFromVariableIdentifier;
|
||||
use binaryninjacore_sys::BNGetHighLevelILByIndex;
|
||||
use binaryninjacore_sys::BNGetMediumLevelILByIndex;
|
||||
use binaryninjacore_sys::BNHighLevelILOperation;
|
||||
@@ -215,7 +214,7 @@ impl<F: ILFunction + RefCountable> ExactSizeIterator for OperandSSAVarIter<F> {
|
||||
}
|
||||
|
||||
pub fn get_var(id: u64) -> Variable {
|
||||
unsafe { Variable::from_raw(BNFromVariableIdentifier(id)) }
|
||||
unsafe { Variable::from_identifier(id) }
|
||||
}
|
||||
|
||||
pub fn get_var_ssa(id: u64, version: usize) -> SSAVariable {
|
||||
|
||||
@@ -23,6 +23,7 @@ use crate::{
|
||||
callingconvention::CallingConvention,
|
||||
rc::*,
|
||||
string::*,
|
||||
typelibrary::TypeLibrary,
|
||||
types::{QualifiedName, QualifiedNameAndType, Type},
|
||||
};
|
||||
|
||||
@@ -163,6 +164,15 @@ impl Platform {
|
||||
unsafe { CoreArchitecture::from_raw(BNGetPlatformArchitecture(self.handle)) }
|
||||
}
|
||||
|
||||
pub fn get_type_libraries_by_name(&self, name: &QualifiedName) -> Array<TypeLibrary> {
|
||||
let mut count = 0;
|
||||
let result = unsafe {
|
||||
BNGetPlatformTypeLibrariesByName(self.handle, &name.0 as *const _ as *mut _, &mut count)
|
||||
};
|
||||
assert!(!result.is_null());
|
||||
unsafe { Array::new(result, count, ()) }
|
||||
}
|
||||
|
||||
pub fn register_os<S: BnStrCompatible>(&self, os: S) {
|
||||
let os = os.into_bytes_with_nul();
|
||||
|
||||
@@ -365,18 +375,14 @@ unsafe impl RefCountable for Platform {
|
||||
impl CoreArrayProvider for Platform {
|
||||
type Raw = *mut BNPlatform;
|
||||
type Context = ();
|
||||
type Wrapped<'a> = Guard<'a, Platform>;
|
||||
}
|
||||
|
||||
unsafe impl CoreOwnedArrayProvider for Platform {
|
||||
unsafe impl CoreArrayProviderInner for Platform {
|
||||
unsafe fn free(raw: *mut *mut BNPlatform, count: usize, _context: &()) {
|
||||
BNFreePlatformList(raw, count);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> CoreArrayWrapper<'a> for Platform {
|
||||
type Wrapped = Guard<'a, Platform>;
|
||||
|
||||
unsafe fn wrap_raw(raw: &'a *mut BNPlatform, context: &'a ()) -> Guard<'a, Platform> {
|
||||
unsafe fn wrap_raw<'a>(raw: &'a *mut BNPlatform, context: &'a ()) -> Self::Wrapped<'a> {
|
||||
debug_assert!(!raw.is_null());
|
||||
Guard::new(Platform { handle: *raw }, context)
|
||||
}
|
||||
|
||||
1464
src/project.rs
Normal file
1464
src/project.rs
Normal file
File diff suppressed because it is too large
Load Diff
117
src/rc.rs
117
src/rc.rs
@@ -35,7 +35,7 @@ use std::slice;
|
||||
// `T` does not have the `Drop` impl in order to allow more
|
||||
// efficient handling of core owned objects we receive pointers
|
||||
// to in callbacks
|
||||
pub unsafe trait RefCountable: ToOwned<Owned = Ref<Self>> + Sized {
|
||||
pub(crate) unsafe trait RefCountable: ToOwned<Owned = Ref<Self>> + Sized {
|
||||
unsafe fn inc_ref(handle: &Self) -> Ref<Self>;
|
||||
unsafe fn dec_ref(handle: &Self);
|
||||
}
|
||||
@@ -43,10 +43,12 @@ pub unsafe trait RefCountable: ToOwned<Owned = Ref<Self>> + Sized {
|
||||
// Represents an 'owned' reference tracked by the core
|
||||
// that we are responsible for cleaning up once we're
|
||||
// done with the encapsulated value.
|
||||
#[allow(private_bounds)]
|
||||
pub struct Ref<T: RefCountable> {
|
||||
contents: T,
|
||||
}
|
||||
|
||||
#[allow(private_bounds)]
|
||||
impl<T: RefCountable> Ref<T> {
|
||||
/// Safety: You need to make sure wherever you got the contents from incremented the ref count already. Anywhere the core passes out an object to the API does this.
|
||||
pub(crate) unsafe fn new(contents: T) -> Self {
|
||||
@@ -151,6 +153,7 @@ impl<'a, T> Guard<'a, T> {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(private_bounds)]
|
||||
impl<'a, T> Guard<'a, T>
|
||||
where
|
||||
T: RefCountable,
|
||||
@@ -190,23 +193,18 @@ impl<'a, T> Borrow<T> for Guard<'a, T> {
|
||||
pub trait CoreArrayProvider {
|
||||
type Raw;
|
||||
type Context;
|
||||
type Wrapped<'a>
|
||||
where
|
||||
Self: 'a;
|
||||
}
|
||||
|
||||
pub unsafe trait CoreOwnedArrayProvider: CoreArrayProvider {
|
||||
pub(crate) unsafe trait CoreArrayProviderInner: CoreArrayProvider {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, context: &Self::Context);
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a>;
|
||||
}
|
||||
|
||||
pub unsafe trait CoreArrayWrapper<'a>: CoreArrayProvider
|
||||
where
|
||||
Self::Raw: 'a,
|
||||
Self::Context: 'a,
|
||||
{
|
||||
type Wrapped: 'a;
|
||||
|
||||
unsafe fn wrap_raw(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped;
|
||||
}
|
||||
|
||||
pub struct Array<P: CoreOwnedArrayProvider> {
|
||||
#[allow(private_bounds)]
|
||||
pub struct Array<P: CoreArrayProviderInner> {
|
||||
contents: *mut P::Raw,
|
||||
count: usize,
|
||||
context: P::Context,
|
||||
@@ -214,18 +212,19 @@ pub struct Array<P: CoreOwnedArrayProvider> {
|
||||
|
||||
unsafe impl<P> Sync for Array<P>
|
||||
where
|
||||
P: CoreOwnedArrayProvider,
|
||||
P: CoreArrayProviderInner,
|
||||
P::Context: Sync,
|
||||
{
|
||||
}
|
||||
unsafe impl<P> Send for Array<P>
|
||||
where
|
||||
P: CoreOwnedArrayProvider,
|
||||
P: CoreArrayProviderInner,
|
||||
P::Context: Send,
|
||||
{
|
||||
}
|
||||
|
||||
impl<P: CoreOwnedArrayProvider> Array<P> {
|
||||
#[allow(private_bounds)]
|
||||
impl<P: CoreArrayProviderInner> Array<P> {
|
||||
pub(crate) unsafe fn new(raw: *mut P::Raw, count: usize, context: P::Context) -> Self {
|
||||
Self {
|
||||
contents: raw,
|
||||
@@ -243,23 +242,19 @@ impl<P: CoreOwnedArrayProvider> Array<P> {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.count == 0
|
||||
}
|
||||
|
||||
pub fn into_raw_parts(self) -> (*mut P::Raw, usize) {
|
||||
let me = mem::ManuallyDrop::new(self);
|
||||
(me.contents, me.count)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, P: 'a + CoreArrayWrapper<'a> + CoreOwnedArrayProvider> Array<P> {
|
||||
#[allow(private_bounds)]
|
||||
impl<P: CoreArrayProviderInner> Array<P> {
|
||||
#[inline]
|
||||
pub fn get(&'a self, index: usize) -> P::Wrapped {
|
||||
pub fn get(&self, index: usize) -> P::Wrapped<'_> {
|
||||
unsafe {
|
||||
let backing = slice::from_raw_parts(self.contents, self.count);
|
||||
P::wrap_raw(&backing[index], &self.context)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&'a self) -> ArrayIter<'a, P> {
|
||||
pub fn iter(&self) -> ArrayIter<P> {
|
||||
ArrayIter {
|
||||
it: unsafe { slice::from_raw_parts(self.contents, self.count).iter() },
|
||||
context: &self.context,
|
||||
@@ -267,8 +262,8 @@ impl<'a, P: 'a + CoreArrayWrapper<'a> + CoreOwnedArrayProvider> Array<P> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, P: 'a + CoreArrayWrapper<'a> + CoreOwnedArrayProvider> IntoIterator for &'a Array<P> {
|
||||
type Item = P::Wrapped;
|
||||
impl<'a, P: CoreArrayProviderInner> IntoIterator for &'a Array<P> {
|
||||
type Item = P::Wrapped<'a>;
|
||||
type IntoIter = ArrayIter<'a, P>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
@@ -276,7 +271,7 @@ impl<'a, P: 'a + CoreArrayWrapper<'a> + CoreOwnedArrayProvider> IntoIterator for
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: CoreOwnedArrayProvider> Drop for Array<P> {
|
||||
impl<P: CoreArrayProviderInner> Drop for Array<P> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
P::free(self.contents, self.count, &self.context);
|
||||
@@ -284,7 +279,8 @@ impl<P: CoreOwnedArrayProvider> Drop for Array<P> {
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ArrayGuard<P: CoreArrayProvider> {
|
||||
#[allow(private_bounds)]
|
||||
pub struct ArrayGuard<P: CoreArrayProviderInner> {
|
||||
contents: *mut P::Raw,
|
||||
count: usize,
|
||||
context: P::Context,
|
||||
@@ -292,18 +288,19 @@ pub struct ArrayGuard<P: CoreArrayProvider> {
|
||||
|
||||
unsafe impl<P> Sync for ArrayGuard<P>
|
||||
where
|
||||
P: CoreArrayProvider,
|
||||
P: CoreArrayProviderInner,
|
||||
P::Context: Sync,
|
||||
{
|
||||
}
|
||||
unsafe impl<P> Send for ArrayGuard<P>
|
||||
where
|
||||
P: CoreArrayProvider,
|
||||
P: CoreArrayProviderInner,
|
||||
P::Context: Send,
|
||||
{
|
||||
}
|
||||
|
||||
impl<P: CoreArrayProvider> ArrayGuard<P> {
|
||||
#[allow(private_bounds)]
|
||||
impl<P: CoreArrayProviderInner> ArrayGuard<P> {
|
||||
pub(crate) unsafe fn new(raw: *mut P::Raw, count: usize, context: P::Context) -> Self {
|
||||
Self {
|
||||
contents: raw,
|
||||
@@ -323,16 +320,17 @@ impl<P: CoreArrayProvider> ArrayGuard<P> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, P: 'a + CoreArrayWrapper<'a> + CoreArrayProvider> ArrayGuard<P> {
|
||||
#[allow(private_bounds)]
|
||||
impl<P: CoreArrayProviderInner> ArrayGuard<P> {
|
||||
#[inline]
|
||||
pub fn get(&'a self, index: usize) -> P::Wrapped {
|
||||
pub fn get(&self, index: usize) -> P::Wrapped<'_> {
|
||||
unsafe {
|
||||
let backing = slice::from_raw_parts(self.contents, self.count);
|
||||
P::wrap_raw(&backing[index], &self.context)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn iter(&'a self) -> ArrayIter<'a, P> {
|
||||
pub fn iter(&self) -> ArrayIter<P> {
|
||||
ArrayIter {
|
||||
it: unsafe { slice::from_raw_parts(self.contents, self.count).iter() },
|
||||
context: &self.context,
|
||||
@@ -340,8 +338,8 @@ impl<'a, P: 'a + CoreArrayWrapper<'a> + CoreArrayProvider> ArrayGuard<P> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, P: 'a + CoreArrayWrapper<'a> + CoreArrayProvider> IntoIterator for &'a ArrayGuard<P> {
|
||||
type Item = P::Wrapped;
|
||||
impl<'a, P: CoreArrayProviderInner> IntoIterator for &'a ArrayGuard<P> {
|
||||
type Item = P::Wrapped<'a>;
|
||||
type IntoIter = ArrayIter<'a, P>;
|
||||
|
||||
fn into_iter(self) -> Self::IntoIter {
|
||||
@@ -349,29 +347,30 @@ impl<'a, P: 'a + CoreArrayWrapper<'a> + CoreArrayProvider> IntoIterator for &'a
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(private_bounds)]
|
||||
pub struct ArrayIter<'a, P>
|
||||
where
|
||||
P: 'a + CoreArrayWrapper<'a>,
|
||||
P: CoreArrayProviderInner,
|
||||
{
|
||||
it: slice::Iter<'a, P::Raw>,
|
||||
context: &'a P::Context,
|
||||
}
|
||||
|
||||
unsafe impl<'a, P> Send for ArrayIter<'a, P>
|
||||
unsafe impl<P> Send for ArrayIter<'_, P>
|
||||
where
|
||||
P: CoreArrayWrapper<'a>,
|
||||
P: CoreArrayProviderInner,
|
||||
P::Context: Sync,
|
||||
{
|
||||
}
|
||||
|
||||
impl<'a, P> Iterator for ArrayIter<'a, P>
|
||||
where
|
||||
P: 'a + CoreArrayWrapper<'a>,
|
||||
P: 'a + CoreArrayProviderInner,
|
||||
{
|
||||
type Item = P::Wrapped;
|
||||
type Item = P::Wrapped<'a>;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<P::Wrapped> {
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.it
|
||||
.next()
|
||||
.map(|r| unsafe { P::wrap_raw(r, self.context) })
|
||||
@@ -385,7 +384,7 @@ where
|
||||
|
||||
impl<'a, P> ExactSizeIterator for ArrayIter<'a, P>
|
||||
where
|
||||
P: 'a + CoreArrayWrapper<'a>,
|
||||
P: 'a + CoreArrayProviderInner,
|
||||
{
|
||||
#[inline]
|
||||
fn len(&self) -> usize {
|
||||
@@ -395,10 +394,10 @@ where
|
||||
|
||||
impl<'a, P> DoubleEndedIterator for ArrayIter<'a, P>
|
||||
where
|
||||
P: 'a + CoreArrayWrapper<'a>,
|
||||
P: 'a + CoreArrayProviderInner,
|
||||
{
|
||||
#[inline]
|
||||
fn next_back(&mut self) -> Option<P::Wrapped> {
|
||||
fn next_back(&mut self) -> Option<P::Wrapped<'a>> {
|
||||
self.it
|
||||
.next_back()
|
||||
.map(|r| unsafe { P::wrap_raw(r, self.context) })
|
||||
@@ -411,21 +410,23 @@ use rayon::prelude::*;
|
||||
#[cfg(feature = "rayon")]
|
||||
use rayon::iter::plumbing::*;
|
||||
|
||||
#[allow(private_bounds)]
|
||||
#[cfg(feature = "rayon")]
|
||||
impl<'a, P> Array<P>
|
||||
impl<P> Array<P>
|
||||
where
|
||||
P: 'a + CoreArrayWrapper<'a> + CoreOwnedArrayProvider,
|
||||
P: CoreArrayProviderInner,
|
||||
P::Context: Sync,
|
||||
P::Wrapped: Send,
|
||||
for<'a> P::Wrapped<'a>: Send,
|
||||
{
|
||||
pub fn par_iter(&'a self) -> ParArrayIter<'a, P> {
|
||||
pub fn par_iter(&self) -> ParArrayIter<'_, P> {
|
||||
ParArrayIter { it: self.iter() }
|
||||
}
|
||||
}
|
||||
#[allow(private_bounds)]
|
||||
#[cfg(feature = "rayon")]
|
||||
pub struct ParArrayIter<'a, P>
|
||||
where
|
||||
P: 'a + CoreArrayWrapper<'a>,
|
||||
P: CoreArrayProviderInner,
|
||||
ArrayIter<'a, P>: Send,
|
||||
{
|
||||
it: ArrayIter<'a, P>,
|
||||
@@ -434,11 +435,11 @@ where
|
||||
#[cfg(feature = "rayon")]
|
||||
impl<'a, P> ParallelIterator for ParArrayIter<'a, P>
|
||||
where
|
||||
P: 'a + CoreArrayWrapper<'a>,
|
||||
P::Wrapped: Send,
|
||||
P: 'a + CoreArrayProviderInner,
|
||||
P::Wrapped<'a>: Send,
|
||||
ArrayIter<'a, P>: Send,
|
||||
{
|
||||
type Item = P::Wrapped;
|
||||
type Item = P::Wrapped<'a>;
|
||||
|
||||
fn drive_unindexed<C>(self, consumer: C) -> C::Result
|
||||
where
|
||||
@@ -455,8 +456,8 @@ where
|
||||
#[cfg(feature = "rayon")]
|
||||
impl<'a, P> IndexedParallelIterator for ParArrayIter<'a, P>
|
||||
where
|
||||
P: 'a + CoreArrayWrapper<'a>,
|
||||
P::Wrapped: Send,
|
||||
P: 'a + CoreArrayProviderInner,
|
||||
P::Wrapped<'a>: Send,
|
||||
ArrayIter<'a, P>: Send,
|
||||
{
|
||||
fn drive<C>(self, consumer: C) -> C::Result
|
||||
@@ -481,7 +482,7 @@ where
|
||||
#[cfg(feature = "rayon")]
|
||||
struct ArrayIterProducer<'a, P>
|
||||
where
|
||||
P: 'a + CoreArrayWrapper<'a>,
|
||||
P: 'a + CoreArrayProviderInner,
|
||||
ArrayIter<'a, P>: Send,
|
||||
{
|
||||
it: ArrayIter<'a, P>,
|
||||
@@ -490,10 +491,10 @@ where
|
||||
#[cfg(feature = "rayon")]
|
||||
impl<'a, P> Producer for ArrayIterProducer<'a, P>
|
||||
where
|
||||
P: 'a + CoreArrayWrapper<'a>,
|
||||
P: 'a + CoreArrayProviderInner,
|
||||
ArrayIter<'a, P>: Send,
|
||||
{
|
||||
type Item = P::Wrapped;
|
||||
type Item = P::Wrapped<'a>;
|
||||
type IntoIter = ArrayIter<'a, P>;
|
||||
|
||||
fn into_iter(self) -> ArrayIter<'a, P> {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::architecture::CoreArchitecture;
|
||||
use crate::function::Function;
|
||||
use crate::rc::{CoreArrayProvider, CoreArrayWrapper, CoreOwnedArrayProvider, Ref};
|
||||
use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Guard, Ref};
|
||||
use binaryninjacore_sys::{BNFreeCodeReferences, BNFreeDataReferences, BNReferenceSource};
|
||||
use std::mem::ManuallyDrop;
|
||||
|
||||
@@ -56,19 +56,15 @@ impl<'a> CodeReference {
|
||||
impl CoreArrayProvider for CodeReference {
|
||||
type Raw = BNReferenceSource;
|
||||
type Context = ();
|
||||
type Wrapped<'a> = Guard<'a, CodeReference>;
|
||||
}
|
||||
|
||||
unsafe impl CoreOwnedArrayProvider for CodeReference {
|
||||
unsafe impl CoreArrayProviderInner for CodeReference {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeCodeReferences(raw, count)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> CoreArrayWrapper<'a> for CodeReference {
|
||||
type Wrapped = CodeReference;
|
||||
|
||||
unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
|
||||
CodeReference::new(raw)
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
Guard::new(CodeReference::new(raw), &())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,18 +73,14 @@ unsafe impl<'a> CoreArrayWrapper<'a> for CodeReference {
|
||||
impl CoreArrayProvider for DataReference {
|
||||
type Raw = u64;
|
||||
type Context = ();
|
||||
type Wrapped<'a> = DataReference;
|
||||
}
|
||||
|
||||
unsafe impl CoreOwnedArrayProvider for DataReference {
|
||||
unsafe impl CoreArrayProviderInner for DataReference {
|
||||
unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
|
||||
BNFreeDataReferences(raw)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> CoreArrayWrapper<'a> for DataReference {
|
||||
type Wrapped = DataReference;
|
||||
|
||||
unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
DataReference { address: *raw }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
use crate::rc::Guard;
|
||||
use crate::string::BnStrCompatible;
|
||||
use crate::{
|
||||
architecture::{Architecture, CoreArchitecture},
|
||||
binaryview::BinaryView,
|
||||
llil,
|
||||
rc::{CoreArrayProvider, CoreArrayWrapper, CoreOwnedArrayProvider, Ref, RefCountable},
|
||||
rc::{CoreArrayProvider, CoreArrayProviderInner, Ref, RefCountable},
|
||||
symbol::Symbol,
|
||||
};
|
||||
use binaryninjacore_sys::*;
|
||||
use std::borrow::Borrow;
|
||||
use std::mem::MaybeUninit;
|
||||
use std::os::raw::c_void;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
@@ -219,18 +221,15 @@ impl Relocation {
|
||||
impl CoreArrayProvider for Relocation {
|
||||
type Raw = *mut BNRelocation;
|
||||
type Context = ();
|
||||
type Wrapped<'a> = Guard<'a, Relocation>;
|
||||
}
|
||||
|
||||
unsafe impl CoreOwnedArrayProvider for Relocation {
|
||||
unsafe impl CoreArrayProviderInner for Relocation {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeRelocationList(raw, count);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> CoreArrayWrapper<'a> for Relocation {
|
||||
type Wrapped = Relocation;
|
||||
unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
|
||||
Relocation(*raw)
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
Guard::new(Relocation(*raw), &())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -501,12 +500,9 @@ where
|
||||
|
||||
let name = name.into_bytes_with_nul();
|
||||
|
||||
let uninit_handler = RelocationHandlerBuilder {
|
||||
handler: unsafe { std::mem::zeroed() },
|
||||
};
|
||||
let raw = Box::into_raw(Box::new(uninit_handler));
|
||||
let raw = Box::leak(Box::new(MaybeUninit::<RelocationHandlerBuilder<_>>::zeroed()));
|
||||
let mut custom_handler = BNCustomRelocationHandler {
|
||||
context: raw as *mut _,
|
||||
context: raw.as_mut_ptr() as *mut _,
|
||||
freeObject: Some(cb_free::<R>),
|
||||
getRelocationInfo: Some(cb_get_relocation_info::<R>),
|
||||
applyRelocation: Some(cb_apply_relocation::<R>),
|
||||
@@ -517,13 +513,12 @@ where
|
||||
assert!(!handle_raw.is_null());
|
||||
let handle = CoreRelocationHandler(handle_raw);
|
||||
let custom_handle = CustomRelocationHandlerHandle {
|
||||
handle: raw as *mut R,
|
||||
handle: raw.as_mut_ptr() as *mut R,
|
||||
};
|
||||
unsafe {
|
||||
core::ptr::write(
|
||||
&mut raw.as_mut().unwrap().handler,
|
||||
func(custom_handle, CoreRelocationHandler(handle.0)),
|
||||
);
|
||||
raw.write(RelocationHandlerBuilder {
|
||||
handler: func(custom_handle, CoreRelocationHandler(handle.0)),
|
||||
});
|
||||
|
||||
BNArchitectureRegisterRelocationHandler(
|
||||
arch.handle().as_ref().0,
|
||||
|
||||
@@ -72,8 +72,11 @@ impl Section {
|
||||
|
||||
/// You need to create a section builder, customize that section, then add it to a binary view:
|
||||
///
|
||||
/// ```
|
||||
/// bv.add_section(Section::new().align(4).entry_size(4))
|
||||
/// ```no_run
|
||||
/// # use binaryninja::section::Section;
|
||||
/// # use binaryninja::binaryview::BinaryViewExt;
|
||||
/// let bv = binaryninja::load("example").unwrap();
|
||||
/// bv.add_section(Section::builder("example", 0..1024).align(4).entry_size(4))
|
||||
/// ```
|
||||
pub fn builder<S: BnStrCompatible>(name: S, range: Range<u64>) -> SectionBuilder<S> {
|
||||
SectionBuilder::new(name, range)
|
||||
@@ -171,18 +174,14 @@ unsafe impl RefCountable for Section {
|
||||
impl CoreArrayProvider for Section {
|
||||
type Raw = *mut BNSection;
|
||||
type Context = ();
|
||||
type Wrapped<'a> = Guard<'a, Section>;
|
||||
}
|
||||
|
||||
unsafe impl CoreOwnedArrayProvider for Section {
|
||||
unsafe impl CoreArrayProviderInner for Section {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeSectionList(raw, count);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> CoreArrayWrapper<'a> for Section {
|
||||
type Wrapped = Guard<'a, Section>;
|
||||
|
||||
unsafe fn wrap_raw(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped {
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
Guard::new(Section::from_raw(*raw), context)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -117,8 +117,11 @@ impl Segment {
|
||||
|
||||
/// You need to create a segment builder, customize that segment, then add it to a binary view:
|
||||
///
|
||||
/// ```
|
||||
/// bv.add_segment(Segment::new().align(4).entry_size(4))
|
||||
/// ```no_run
|
||||
/// # use binaryninja::segment::Segment;
|
||||
/// # use binaryninja::binaryview::BinaryViewExt;
|
||||
/// let bv = binaryninja::load("example").unwrap();
|
||||
/// bv.add_segment(Segment::builder(0..0x1000).writable(true).readable(true))
|
||||
/// ```
|
||||
pub fn builder(ea_range: Range<u64>) -> SegmentBuilder {
|
||||
SegmentBuilder::new(ea_range)
|
||||
@@ -201,18 +204,14 @@ unsafe impl RefCountable for Segment {
|
||||
impl CoreArrayProvider for Segment {
|
||||
type Raw = *mut BNSegment;
|
||||
type Context = ();
|
||||
type Wrapped<'a> = Guard<'a, Segment>;
|
||||
}
|
||||
|
||||
unsafe impl CoreOwnedArrayProvider for Segment {
|
||||
unsafe impl CoreArrayProviderInner for Segment {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeSegmentList(raw, count);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> CoreArrayWrapper<'a> for Segment {
|
||||
type Wrapped = Guard<'a, Segment>;
|
||||
|
||||
unsafe fn wrap_raw(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped {
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
Guard::new(Segment::from_raw(*raw), context)
|
||||
}
|
||||
}
|
||||
|
||||
130
src/string.rs
130
src/string.rs
@@ -14,7 +14,7 @@
|
||||
|
||||
//! String wrappers for core-owned strings and strings being passed to the core
|
||||
|
||||
use std::borrow::{Borrow, Cow};
|
||||
use std::borrow::Cow;
|
||||
use std::ffi::{CStr, CString};
|
||||
use std::fmt;
|
||||
use std::hash::{Hash, Hasher};
|
||||
@@ -33,61 +33,9 @@ pub(crate) fn raw_to_string(ptr: *const raw::c_char) -> Option<String> {
|
||||
}
|
||||
}
|
||||
|
||||
/// These are strings that the core will both allocate and free.
|
||||
/// We just have a reference to these strings and want to be able use them, but aren't responsible for cleanup
|
||||
#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct BnStr {
|
||||
raw: [u8],
|
||||
}
|
||||
|
||||
impl BnStr {
|
||||
pub(crate) unsafe fn from_raw<'a>(ptr: *const raw::c_char) -> &'a Self {
|
||||
mem::transmute(CStr::from_ptr(ptr).to_bytes_with_nul())
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
self.as_cstr().to_str().unwrap()
|
||||
}
|
||||
|
||||
pub fn as_cstr(&self) -> &CStr {
|
||||
unsafe { CStr::from_bytes_with_nul_unchecked(&self.raw) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for BnStr {
|
||||
type Target = str;
|
||||
|
||||
fn deref(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for BnStr {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
&self.raw
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<str> for BnStr {
|
||||
fn as_ref(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl Borrow<str> for BnStr {
|
||||
fn borrow(&self) -> &str {
|
||||
self.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for BnStr {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.as_cstr().to_string_lossy())
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
/// Is the quivalent of `core::ffi::CString` but using the allocation and free
|
||||
/// functions provided by binaryninja_sys.
|
||||
#[repr(transparent)]
|
||||
pub struct BnString {
|
||||
raw: *mut raw::c_char,
|
||||
}
|
||||
@@ -131,8 +79,28 @@ impl BnString {
|
||||
res
|
||||
}
|
||||
|
||||
pub(crate) fn as_raw(&self) -> &raw::c_char {
|
||||
unsafe { &*self.raw }
|
||||
}
|
||||
|
||||
pub fn as_str(&self) -> &str {
|
||||
unsafe { BnStr::from_raw(self.raw).as_str() }
|
||||
unsafe { CStr::from_ptr(self.raw).to_str().unwrap() }
|
||||
}
|
||||
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
self.as_str().as_bytes()
|
||||
}
|
||||
|
||||
pub fn as_bytes_with_null(&self) -> &[u8] {
|
||||
self.deref().to_bytes()
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.as_ref().len()
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.as_ref().is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,16 +126,16 @@ impl Clone for BnString {
|
||||
}
|
||||
|
||||
impl Deref for BnString {
|
||||
type Target = BnStr;
|
||||
type Target = CStr;
|
||||
|
||||
fn deref(&self) -> &BnStr {
|
||||
unsafe { BnStr::from_raw(self.raw) }
|
||||
fn deref(&self) -> &CStr {
|
||||
unsafe { CStr::from_ptr(self.raw) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRef<[u8]> for BnString {
|
||||
fn as_ref(&self) -> &[u8] {
|
||||
self.as_cstr().to_bytes_with_nul()
|
||||
self.to_bytes_with_nul()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -187,33 +155,29 @@ impl Eq for BnString {}
|
||||
|
||||
impl fmt::Display for BnString {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.as_cstr().to_string_lossy())
|
||||
write!(f, "{}", self.to_string_lossy())
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for BnString {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(f, "{}", self.as_cstr().to_string_lossy())
|
||||
write!(f, "{}", self.to_string_lossy())
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for BnString {
|
||||
type Raw = *mut raw::c_char;
|
||||
type Context = ();
|
||||
type Wrapped<'a> = &'a str;
|
||||
}
|
||||
|
||||
unsafe impl CoreOwnedArrayProvider for BnString {
|
||||
unsafe impl CoreArrayProviderInner for BnString {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
use binaryninjacore_sys::BNFreeStringList;
|
||||
BNFreeStringList(raw, count);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> CoreArrayWrapper<'a> for BnString {
|
||||
type Wrapped = &'a BnStr;
|
||||
|
||||
unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
|
||||
BnStr::from_raw(*raw)
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
CStr::from_ptr(*raw).to_str().unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,11 +186,11 @@ pub unsafe trait BnStrCompatible {
|
||||
fn into_bytes_with_nul(self) -> Self::Result;
|
||||
}
|
||||
|
||||
unsafe impl<'a> BnStrCompatible for &'a BnStr {
|
||||
unsafe impl<'a> BnStrCompatible for &'a CStr {
|
||||
type Result = &'a [u8];
|
||||
|
||||
fn into_bytes_with_nul(self) -> Self::Result {
|
||||
self.as_cstr().to_bytes_with_nul()
|
||||
self.to_bytes_with_nul()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,14 +202,6 @@ unsafe impl BnStrCompatible for BnString {
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> BnStrCompatible for &'a CStr {
|
||||
type Result = &'a [u8];
|
||||
|
||||
fn into_bytes_with_nul(self) -> Self::Result {
|
||||
self.to_bytes_with_nul()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl BnStrCompatible for CString {
|
||||
type Result = Vec<u8>;
|
||||
|
||||
@@ -294,3 +250,15 @@ unsafe impl BnStrCompatible for &QualifiedName {
|
||||
self.string().into_bytes_with_nul()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait IntoJson {
|
||||
type Output: BnStrCompatible;
|
||||
fn get_json_string(self) -> Result<Self::Output, ()>;
|
||||
}
|
||||
|
||||
impl<S: BnStrCompatible> IntoJson for S {
|
||||
type Output = S;
|
||||
fn get_json_string(self) -> Result<Self::Output, ()> {
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,8 +230,10 @@ impl Symbol {
|
||||
|
||||
/// To create a new symbol, you need to create a symbol builder, customize that symbol, then add `SymbolBuilder::create` it into a `Ref<Symbol>`:
|
||||
///
|
||||
/// ```
|
||||
/// Symbol::new().short_name("hello").full_name("hello").create();
|
||||
/// ```no_run
|
||||
/// # use binaryninja::symbol::Symbol;
|
||||
/// # use binaryninja::symbol::SymbolType;
|
||||
/// Symbol::builder(SymbolType::Data, "hello", 0x1337).short_name("hello").full_name("hello").create();
|
||||
/// ```
|
||||
pub fn builder(ty: SymbolType, raw_name: &str, addr: u64) -> SymbolBuilder {
|
||||
SymbolBuilder::new(ty, raw_name, addr)
|
||||
@@ -246,24 +248,15 @@ impl Symbol {
|
||||
}
|
||||
|
||||
pub fn full_name(&self) -> BnString {
|
||||
unsafe {
|
||||
let name = BNGetSymbolFullName(self.handle);
|
||||
BnString::from_raw(name)
|
||||
}
|
||||
unsafe { BnString::from_raw(BNGetSymbolFullName(self.handle)) }
|
||||
}
|
||||
|
||||
pub fn short_name(&self) -> BnString {
|
||||
unsafe {
|
||||
let name = BNGetSymbolShortName(self.handle);
|
||||
BnString::from_raw(name)
|
||||
}
|
||||
unsafe { BnString::from_raw(BNGetSymbolShortName(self.handle)) }
|
||||
}
|
||||
|
||||
pub fn raw_name(&self) -> BnString {
|
||||
unsafe {
|
||||
let name = BNGetSymbolRawName(self.handle);
|
||||
BnString::from_raw(name)
|
||||
}
|
||||
unsafe { BnString::from_raw(BNGetSymbolRawName(self.handle)) }
|
||||
}
|
||||
|
||||
pub fn address(&self) -> u64 {
|
||||
@@ -326,18 +319,14 @@ unsafe impl RefCountable for Symbol {
|
||||
impl CoreArrayProvider for Symbol {
|
||||
type Raw = *mut BNSymbol;
|
||||
type Context = ();
|
||||
type Wrapped<'a> = Guard<'a, Symbol>;
|
||||
}
|
||||
|
||||
unsafe impl CoreOwnedArrayProvider for Symbol {
|
||||
unsafe impl CoreArrayProviderInner for Symbol {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeSymbolList(raw, count);
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl<'a> CoreArrayWrapper<'a> for Symbol {
|
||||
type Wrapped = Guard<'a, Symbol>;
|
||||
|
||||
unsafe fn wrap_raw(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped {
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
Guard::new(Symbol::from_raw(*raw), context)
|
||||
}
|
||||
}
|
||||
|
||||
76
src/tags.rs
76
src/tags.rs
@@ -16,8 +16,10 @@
|
||||
|
||||
use binaryninjacore_sys::*;
|
||||
|
||||
use crate::architecture::CoreArchitecture;
|
||||
use crate::binaryview::BinaryView;
|
||||
|
||||
use crate::function::Function;
|
||||
use crate::rc::*;
|
||||
use crate::string::*;
|
||||
|
||||
@@ -77,6 +79,21 @@ impl ToOwned for Tag {
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for Tag {
|
||||
type Raw = *mut BNTag;
|
||||
type Context = ();
|
||||
type Wrapped<'a> = Guard<'a, Self>;
|
||||
}
|
||||
|
||||
unsafe impl CoreArrayProviderInner for Tag {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeTagList(raw, count)
|
||||
}
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
Guard::new(Self { handle: *raw }, &context)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Send for Tag {}
|
||||
unsafe impl Sync for Tag {}
|
||||
|
||||
@@ -115,7 +132,7 @@ impl TagType {
|
||||
pub fn set_icon<S: BnStrCompatible>(&self, icon: S) {
|
||||
let icon = icon.into_bytes_with_nul();
|
||||
unsafe {
|
||||
BNTagTypeSetName(self.handle, icon.as_ref().as_ptr() as *mut _);
|
||||
BNTagTypeSetIcon(self.handle, icon.as_ref().as_ptr() as *mut _);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,3 +193,60 @@ impl ToOwned for TagType {
|
||||
|
||||
unsafe impl Send for TagType {}
|
||||
unsafe impl Sync for TagType {}
|
||||
|
||||
pub type TagReferenceType = BNTagReferenceType;
|
||||
|
||||
pub struct TagReference {
|
||||
ref_type: TagReferenceType,
|
||||
auto_defined: bool,
|
||||
tag: Ref<Tag>,
|
||||
arch: CoreArchitecture,
|
||||
func: Ref<Function>,
|
||||
addr: u64,
|
||||
}
|
||||
|
||||
impl TagReference {
|
||||
unsafe fn from_borrowed_raw(value: &BNTagReference) -> Self {
|
||||
Self {
|
||||
ref_type: value.refType,
|
||||
auto_defined: value.autoDefined,
|
||||
tag: Tag { handle: value.tag }.to_owned(),
|
||||
arch: CoreArchitecture::from_raw(value.arch),
|
||||
func: Function { handle: value.func }.to_owned(),
|
||||
addr: value.addr,
|
||||
}
|
||||
}
|
||||
pub fn ref_type(&self) -> TagReferenceType {
|
||||
self.ref_type
|
||||
}
|
||||
pub fn auto(&self) -> bool {
|
||||
self.auto_defined
|
||||
}
|
||||
pub fn tag(&self) -> &Tag {
|
||||
&self.tag
|
||||
}
|
||||
pub fn arch(&self) -> CoreArchitecture {
|
||||
self.arch
|
||||
}
|
||||
pub fn functions(&self) -> &Function {
|
||||
&self.func
|
||||
}
|
||||
pub fn address(&self) -> u64 {
|
||||
self.addr
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for TagReference {
|
||||
type Raw = BNTagReference;
|
||||
type Context = ();
|
||||
type Wrapped<'a> = Self;
|
||||
}
|
||||
|
||||
unsafe impl CoreArrayProviderInner for TagReference {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeTagReferences(raw, count)
|
||||
}
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
Self::from_borrowed_raw(raw)
|
||||
}
|
||||
}
|
||||
|
||||
947
src/typearchive.rs
Normal file
947
src/typearchive.rs
Normal file
@@ -0,0 +1,947 @@
|
||||
use core::{ffi, mem, ptr};
|
||||
|
||||
use binaryninjacore_sys::*;
|
||||
|
||||
use crate::databuffer::DataBuffer;
|
||||
use crate::metadata::Metadata;
|
||||
use crate::platform::Platform;
|
||||
use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref};
|
||||
use crate::string::{BnStrCompatible, BnString};
|
||||
use crate::types::{QualifiedName, QualifiedNameAndType, QualifiedNameTypeAndId, Type};
|
||||
|
||||
/// 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.
|
||||
#[repr(transparent)]
|
||||
pub struct TypeArchive {
|
||||
handle: ptr::NonNull<BNTypeArchive>,
|
||||
}
|
||||
|
||||
impl Drop for TypeArchive {
|
||||
fn drop(&mut self) {
|
||||
unsafe { BNFreeTypeArchiveReference(self.as_raw()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Clone for TypeArchive {
|
||||
fn clone(&self) -> Self {
|
||||
unsafe {
|
||||
Self::from_raw(ptr::NonNull::new(BNNewTypeArchiveReference(self.as_raw())).unwrap())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for TypeArchive {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.id() == other.id()
|
||||
}
|
||||
}
|
||||
impl Eq for TypeArchive {}
|
||||
|
||||
impl core::hash::Hash for TypeArchive {
|
||||
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
|
||||
(self.handle.as_ptr() as usize).hash(state);
|
||||
}
|
||||
}
|
||||
|
||||
impl core::fmt::Debug for TypeArchive {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
let path = self.path().map(|x| x.to_string());
|
||||
f.debug_struct("TypeArchive").field("path", &path).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl TypeArchive {
|
||||
pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNTypeArchive>) -> Self {
|
||||
Self { handle }
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn ref_from_raw(handle: &*mut BNTypeArchive) -> &Self {
|
||||
assert!(!handle.is_null());
|
||||
mem::transmute(handle)
|
||||
}
|
||||
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
pub(crate) unsafe fn as_raw(&self) -> &mut BNTypeArchive {
|
||||
&mut *self.handle.as_ptr()
|
||||
}
|
||||
|
||||
/// Open the Type Archive at the given path, if it exists.
|
||||
pub fn open<S: BnStrCompatible>(path: S) -> Option<TypeArchive> {
|
||||
let path = path.into_bytes_with_nul();
|
||||
let handle = unsafe { BNOpenTypeArchive(path.as_ref().as_ptr() as *const ffi::c_char) };
|
||||
ptr::NonNull::new(handle).map(|handle| unsafe { TypeArchive::from_raw(handle) })
|
||||
}
|
||||
|
||||
/// Create a Type Archive at the given path, returning None if it could not be created.
|
||||
pub fn create<S: BnStrCompatible>(path: S, platform: &Platform) -> Option<TypeArchive> {
|
||||
let path = path.into_bytes_with_nul();
|
||||
let handle = unsafe {
|
||||
BNCreateTypeArchive(
|
||||
path.as_ref().as_ptr() as *const ffi::c_char,
|
||||
platform.handle,
|
||||
)
|
||||
};
|
||||
ptr::NonNull::new(handle).map(|handle| unsafe { TypeArchive::from_raw(handle) })
|
||||
}
|
||||
|
||||
/// Create a Type Archive at the given path and id, returning None if it could not be created.
|
||||
pub fn create_with_id<P: BnStrCompatible, I: BnStrCompatible>(
|
||||
path: P,
|
||||
id: I,
|
||||
platform: &Platform,
|
||||
) -> Option<TypeArchive> {
|
||||
let path = path.into_bytes_with_nul();
|
||||
let id = id.into_bytes_with_nul();
|
||||
let handle = unsafe {
|
||||
BNCreateTypeArchiveWithId(
|
||||
path.as_ref().as_ptr() as *const ffi::c_char,
|
||||
platform.handle,
|
||||
id.as_ref().as_ptr() as *const ffi::c_char,
|
||||
)
|
||||
};
|
||||
ptr::NonNull::new(handle).map(|handle| unsafe { TypeArchive::from_raw(handle) })
|
||||
}
|
||||
|
||||
/// Get a reference to the Type Archive with the known id, if one exists.
|
||||
pub fn lookup_by_id<S: BnStrCompatible>(id: S) -> Option<TypeArchive> {
|
||||
let id = id.into_bytes_with_nul();
|
||||
let handle = unsafe { BNLookupTypeArchiveById(id.as_ref().as_ptr() as *const ffi::c_char) };
|
||||
ptr::NonNull::new(handle).map(|handle| unsafe { TypeArchive::from_raw(handle) })
|
||||
}
|
||||
|
||||
/// Get the path to the Type Archive's file
|
||||
pub fn path(&self) -> Option<BnString> {
|
||||
let result = unsafe { BNGetTypeArchivePath(self.as_raw()) };
|
||||
(!result.is_null()).then(|| unsafe { BnString::from_raw(result) })
|
||||
}
|
||||
|
||||
/// Get the guid for a Type Archive
|
||||
pub fn id(&self) -> Option<BnString> {
|
||||
let result = unsafe { BNGetTypeArchiveId(self.as_raw()) };
|
||||
(!result.is_null()).then(|| unsafe { BnString::from_raw(result) })
|
||||
}
|
||||
|
||||
/// Get the associated Platform for a Type Archive
|
||||
pub fn platform(&self) -> Ref<Platform> {
|
||||
let result = unsafe { BNGetTypeArchivePlatform(self.as_raw()) };
|
||||
assert!(!result.is_null());
|
||||
unsafe { Platform::ref_from_raw(result) }
|
||||
}
|
||||
|
||||
/// Get the id of the current snapshot in the type archive
|
||||
pub fn current_snapshot_id(&self) -> BnString {
|
||||
let result = unsafe { BNGetTypeArchiveCurrentSnapshotId(self.as_raw()) };
|
||||
assert!(!result.is_null());
|
||||
unsafe { BnString::from_raw(result) }
|
||||
}
|
||||
|
||||
/// Revert the type archive's current snapshot to the given snapshot
|
||||
pub fn set_current_snapshot_id<S: BnStrCompatible>(&self, id: S) {
|
||||
let id = id.into_bytes_with_nul();
|
||||
unsafe {
|
||||
BNSetTypeArchiveCurrentSnapshot(
|
||||
self.as_raw(),
|
||||
id.as_ref().as_ptr() as *const ffi::c_char,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a list of every snapshot's id
|
||||
pub fn all_snapshot_ids(&self) -> Array<BnString> {
|
||||
let mut count = 0;
|
||||
let result = unsafe { BNGetTypeArchiveAllSnapshotIds(self.as_raw(), &mut count) };
|
||||
assert!(!result.is_null());
|
||||
unsafe { Array::new(result, count, ()) }
|
||||
}
|
||||
|
||||
/// Get the ids of the parents to the given snapshot
|
||||
pub fn get_snapshot_parent_ids<S: BnStrCompatible>(
|
||||
&self,
|
||||
snapshot: S,
|
||||
) -> Option<Array<BnString>> {
|
||||
let snapshot = snapshot.into_bytes_with_nul();
|
||||
let mut count = 0;
|
||||
let result = unsafe {
|
||||
BNGetTypeArchiveSnapshotParentIds(
|
||||
self.as_raw(),
|
||||
snapshot.as_ref().as_ptr() as *const ffi::c_char,
|
||||
&mut count,
|
||||
)
|
||||
};
|
||||
(!result.is_null()).then(|| unsafe { Array::new(result, count, ()) })
|
||||
}
|
||||
|
||||
/// Get the ids of the children to the given snapshot
|
||||
pub fn get_snapshot_child_ids<S: BnStrCompatible>(
|
||||
&self,
|
||||
snapshot: S,
|
||||
) -> Option<Array<BnString>> {
|
||||
let snapshot = snapshot.into_bytes_with_nul();
|
||||
let mut count = 0;
|
||||
let result = unsafe {
|
||||
BNGetTypeArchiveSnapshotChildIds(
|
||||
self.as_raw(),
|
||||
snapshot.as_ref().as_ptr() as *const ffi::c_char,
|
||||
&mut count,
|
||||
)
|
||||
};
|
||||
(!result.is_null()).then(|| unsafe { Array::new(result, count, ()) })
|
||||
}
|
||||
|
||||
/// Add named types to the type archive. Type must have all dependant named types added
|
||||
/// prior to being added, or this function will fail.
|
||||
/// If the type already exists, it will be overwritten.
|
||||
///
|
||||
/// * `name` - Name of new type
|
||||
/// * `type` - Definition of new type
|
||||
pub fn add_type(&self, name: &QualifiedNameAndType) {
|
||||
self.add_types(core::slice::from_ref(name))
|
||||
}
|
||||
|
||||
/// Add named types to the type archive. Types must have all dependant named
|
||||
/// types prior to being added, or included in the list, or this function will fail.
|
||||
/// Types already existing with any added names will be overwritten.
|
||||
///
|
||||
/// * `new_types` - Names and definitions of new types
|
||||
pub fn add_types(&self, new_types: &[QualifiedNameAndType]) {
|
||||
// SAFETY BNQualifiedNameAndType and QualifiedNameAndType are transparent
|
||||
let new_types_raw: &[BNQualifiedNameAndType] = unsafe { mem::transmute(new_types) };
|
||||
let result = unsafe {
|
||||
BNAddTypeArchiveTypes(self.as_raw(), new_types_raw.as_ptr(), new_types.len())
|
||||
};
|
||||
assert!(result);
|
||||
}
|
||||
|
||||
/// Change the name of an existing type in the type archive.
|
||||
///
|
||||
/// * `old_name` - Old type name in archive
|
||||
/// * `new_name` - New type name
|
||||
pub fn rename_type(&self, old_name: &QualifiedName, new_name: &QualifiedNameAndType) {
|
||||
let id = self
|
||||
.get_type_id(old_name, self.current_snapshot_id())
|
||||
.unwrap();
|
||||
return self.rename_type_by_id(id, new_name.name());
|
||||
}
|
||||
|
||||
/// Change the name of an existing type in the type archive.
|
||||
///
|
||||
/// * `id` - Old id of type in archive
|
||||
/// * `new_name` - New type name
|
||||
pub fn rename_type_by_id<S: BnStrCompatible>(&self, id: S, new_name: &QualifiedName) {
|
||||
let id = id.into_bytes_with_nul();
|
||||
let result = unsafe {
|
||||
BNRenameTypeArchiveType(
|
||||
self.as_raw(),
|
||||
id.as_ref().as_ptr() as *const ffi::c_char,
|
||||
&new_name.0,
|
||||
)
|
||||
};
|
||||
assert!(result);
|
||||
}
|
||||
|
||||
/// Delete an existing type in the type archive.
|
||||
pub fn delete_type(&self, name: &QualifiedName) {
|
||||
let id = self.get_type_id(name, self.current_snapshot_id());
|
||||
let Some(id) = id else {
|
||||
panic!("Unknown type {}", name.string())
|
||||
};
|
||||
self.delete_type_by_id(id);
|
||||
}
|
||||
|
||||
/// Delete an existing type in the type archive.
|
||||
pub fn delete_type_by_id<S: BnStrCompatible>(&self, id: S) {
|
||||
let id = id.into_bytes_with_nul();
|
||||
let result = unsafe {
|
||||
BNDeleteTypeArchiveType(self.as_raw(), id.as_ref().as_ptr() as *const ffi::c_char)
|
||||
};
|
||||
assert!(result);
|
||||
}
|
||||
|
||||
/// Retrieve a stored type in the archive
|
||||
///
|
||||
/// * `name` - Type name
|
||||
/// * `snapshot` - Snapshot id to search for types
|
||||
pub fn get_type_by_name<S: BnStrCompatible>(
|
||||
&self,
|
||||
name: &QualifiedName,
|
||||
snapshot: S,
|
||||
) -> Option<Ref<Type>> {
|
||||
let snapshot = snapshot.into_bytes_with_nul();
|
||||
let result = unsafe {
|
||||
BNGetTypeArchiveTypeByName(
|
||||
self.as_raw(),
|
||||
&name.0,
|
||||
snapshot.as_ref().as_ptr() as *const ffi::c_char,
|
||||
)
|
||||
};
|
||||
(!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) })
|
||||
}
|
||||
|
||||
/// Retrieve a stored type in the archive by id
|
||||
///
|
||||
/// * `id` - Type id
|
||||
/// * `snapshot` - Snapshot id to search for types
|
||||
pub fn get_type_by_id<I: BnStrCompatible, S: BnStrCompatible>(
|
||||
&self,
|
||||
id: I,
|
||||
snapshot: S,
|
||||
) -> Option<Ref<Type>> {
|
||||
let snapshot = snapshot.into_bytes_with_nul();
|
||||
let id = id.into_bytes_with_nul();
|
||||
let result = unsafe {
|
||||
BNGetTypeArchiveTypeById(
|
||||
self.as_raw(),
|
||||
id.as_ref().as_ptr() as *const ffi::c_char,
|
||||
snapshot.as_ref().as_ptr() as *const ffi::c_char,
|
||||
)
|
||||
};
|
||||
(!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) })
|
||||
}
|
||||
|
||||
/// Retrieve a type's name by its id
|
||||
///
|
||||
/// * `id` - Type id
|
||||
/// * `snapshot` - Snapshot id to search for types
|
||||
pub fn get_type_name_by_id<I: BnStrCompatible, S: BnStrCompatible>(
|
||||
&self,
|
||||
id: I,
|
||||
snapshot: S,
|
||||
) -> QualifiedName {
|
||||
let snapshot = snapshot.into_bytes_with_nul();
|
||||
let id = id.into_bytes_with_nul();
|
||||
let result = unsafe {
|
||||
BNGetTypeArchiveTypeName(
|
||||
self.as_raw(),
|
||||
id.as_ref().as_ptr() as *const ffi::c_char,
|
||||
snapshot.as_ref().as_ptr() as *const ffi::c_char,
|
||||
)
|
||||
};
|
||||
QualifiedName(result)
|
||||
}
|
||||
|
||||
/// Retrieve a type's id by its name
|
||||
///
|
||||
/// * `name` - Type name
|
||||
/// * `snapshot` - Snapshot id to search for types
|
||||
pub fn get_type_id<S: BnStrCompatible>(
|
||||
&self,
|
||||
name: &QualifiedName,
|
||||
snapshot: S,
|
||||
) -> Option<BnString> {
|
||||
let snapshot = snapshot.into_bytes_with_nul();
|
||||
let result = unsafe {
|
||||
BNGetTypeArchiveTypeId(
|
||||
self.as_raw(),
|
||||
&name.0,
|
||||
snapshot.as_ref().as_ptr() as *const ffi::c_char,
|
||||
)
|
||||
};
|
||||
(!result.is_null()).then(|| unsafe { BnString::from_raw(result) })
|
||||
}
|
||||
|
||||
/// Retrieve all stored types in the archive at a snapshot
|
||||
///
|
||||
/// * `snapshot` - Snapshot id to search for types
|
||||
pub fn get_types_and_ids<S: BnStrCompatible>(
|
||||
&self,
|
||||
snapshot: S,
|
||||
) -> Array<QualifiedNameTypeAndId> {
|
||||
let snapshot = snapshot.into_bytes_with_nul();
|
||||
let mut count = 0;
|
||||
let result = unsafe {
|
||||
BNGetTypeArchiveTypes(
|
||||
self.as_raw(),
|
||||
snapshot.as_ref().as_ptr() as *const ffi::c_char,
|
||||
&mut count,
|
||||
)
|
||||
};
|
||||
assert!(!result.is_null());
|
||||
unsafe { Array::new(result, count, ()) }
|
||||
}
|
||||
|
||||
/// Get a list of all types' ids in the archive at a snapshot
|
||||
///
|
||||
/// * `snapshot` - Snapshot id to search for types
|
||||
pub fn get_type_ids<S: BnStrCompatible>(&self, snapshot: S) -> Array<BnString> {
|
||||
let snapshot = snapshot.into_bytes_with_nul();
|
||||
let mut count = 0;
|
||||
let result = unsafe {
|
||||
BNGetTypeArchiveTypeIds(
|
||||
self.as_raw(),
|
||||
snapshot.as_ref().as_ptr() as *const ffi::c_char,
|
||||
&mut count,
|
||||
)
|
||||
};
|
||||
assert!(!result.is_null());
|
||||
unsafe { Array::new(result, count, ()) }
|
||||
}
|
||||
|
||||
/// Get a list of all types' names in the archive at a snapshot
|
||||
///
|
||||
/// * `snapshot` - Snapshot id to search for types
|
||||
pub fn get_type_names<S: BnStrCompatible>(&self, snapshot: S) -> Array<QualifiedName> {
|
||||
let snapshot = snapshot.into_bytes_with_nul();
|
||||
let mut count = 0;
|
||||
let result = unsafe {
|
||||
BNGetTypeArchiveTypeNames(
|
||||
self.as_raw(),
|
||||
snapshot.as_ref().as_ptr() as *const ffi::c_char,
|
||||
&mut count,
|
||||
)
|
||||
};
|
||||
assert!(!result.is_null());
|
||||
unsafe { Array::new(result, count, ()) }
|
||||
}
|
||||
|
||||
/// Get a list of all types' names and ids in the archive at a current snapshot
|
||||
|
||||
/// * `snapshot` - Snapshot id to search for types
|
||||
pub fn get_type_names_and_ids<S: BnStrCompatible>(
|
||||
&self,
|
||||
snapshot: S,
|
||||
) -> (Array<QualifiedName>, Array<BnString>) {
|
||||
let snapshot = snapshot.into_bytes_with_nul();
|
||||
let mut count = 0;
|
||||
let mut names = ptr::null_mut();
|
||||
let mut ids = ptr::null_mut();
|
||||
let result = unsafe {
|
||||
BNGetTypeArchiveTypeNamesAndIds(
|
||||
self.as_raw(),
|
||||
snapshot.as_ref().as_ptr() as *const ffi::c_char,
|
||||
&mut names,
|
||||
&mut ids,
|
||||
&mut count,
|
||||
)
|
||||
};
|
||||
assert!(result);
|
||||
(unsafe { Array::new(names, count, ()) }, unsafe {
|
||||
Array::new(ids, count, ())
|
||||
})
|
||||
}
|
||||
|
||||
/// Get all types a given type references directly
|
||||
///
|
||||
/// * `id` - Source type id
|
||||
/// * `snapshot` - Snapshot id to search for types
|
||||
pub fn get_outgoing_direct_references<I: BnStrCompatible, S: BnStrCompatible>(
|
||||
&self,
|
||||
id: I,
|
||||
snapshot: S,
|
||||
) -> Array<BnString> {
|
||||
let snapshot = snapshot.into_bytes_with_nul();
|
||||
let id = id.into_bytes_with_nul();
|
||||
let mut count = 0;
|
||||
let result = unsafe {
|
||||
BNGetTypeArchiveOutgoingDirectTypeReferences(
|
||||
self.as_raw(),
|
||||
id.as_ref().as_ptr() as *const ffi::c_char,
|
||||
snapshot.as_ref().as_ptr() as *const ffi::c_char,
|
||||
&mut count,
|
||||
)
|
||||
};
|
||||
assert!(!result.is_null());
|
||||
unsafe { Array::new(result, count, ()) }
|
||||
}
|
||||
|
||||
/// Get all types a given type references, and any types that the referenced types reference
|
||||
///
|
||||
/// :param id: Source type id
|
||||
/// :param snapshot: Snapshot id to search for types
|
||||
pub fn get_outgoing_recursive_references<I: BnStrCompatible, S: BnStrCompatible>(
|
||||
&self,
|
||||
id: I,
|
||||
snapshot: S,
|
||||
) -> Array<BnString> {
|
||||
let snapshot = snapshot.into_bytes_with_nul();
|
||||
let id = id.into_bytes_with_nul();
|
||||
let mut count = 0;
|
||||
let result = unsafe {
|
||||
BNGetTypeArchiveOutgoingRecursiveTypeReferences(
|
||||
self.as_raw(),
|
||||
id.as_ref().as_ptr() as *const ffi::c_char,
|
||||
snapshot.as_ref().as_ptr() as *const ffi::c_char,
|
||||
&mut count,
|
||||
)
|
||||
};
|
||||
assert!(!result.is_null());
|
||||
unsafe { Array::new(result, count, ()) }
|
||||
}
|
||||
|
||||
/// Get all types that reference a given type
|
||||
///
|
||||
/// * `id` - Target type id
|
||||
/// * `snapshot` - Snapshot id to search for types
|
||||
pub fn get_incoming_direct_references<I: BnStrCompatible, S: BnStrCompatible>(
|
||||
&self,
|
||||
id: I,
|
||||
snapshot: S,
|
||||
) -> Array<BnString> {
|
||||
let snapshot = snapshot.into_bytes_with_nul();
|
||||
let id = id.into_bytes_with_nul();
|
||||
let mut count = 0;
|
||||
let result = unsafe {
|
||||
BNGetTypeArchiveIncomingDirectTypeReferences(
|
||||
self.as_raw(),
|
||||
id.as_ref().as_ptr() as *const ffi::c_char,
|
||||
snapshot.as_ref().as_ptr() as *const ffi::c_char,
|
||||
&mut count,
|
||||
)
|
||||
};
|
||||
assert!(!result.is_null());
|
||||
unsafe { Array::new(result, count, ()) }
|
||||
}
|
||||
|
||||
/// Get all types that reference a given type, and all types that reference them, recursively
|
||||
///
|
||||
/// * `id` - Target type id
|
||||
/// * `snapshot` - Snapshot id to search for types, or empty string to search the latest snapshot
|
||||
pub fn get_incoming_recursive_references<I: BnStrCompatible, S: BnStrCompatible>(
|
||||
&self,
|
||||
id: I,
|
||||
snapshot: S,
|
||||
) -> Array<BnString> {
|
||||
let snapshot = snapshot.into_bytes_with_nul();
|
||||
let id = id.into_bytes_with_nul();
|
||||
let mut count = 0;
|
||||
let result = unsafe {
|
||||
BNGetTypeArchiveIncomingRecursiveTypeReferences(
|
||||
self.as_raw(),
|
||||
id.as_ref().as_ptr() as *const ffi::c_char,
|
||||
snapshot.as_ref().as_ptr() as *const ffi::c_char,
|
||||
&mut count,
|
||||
)
|
||||
};
|
||||
assert!(!result.is_null());
|
||||
unsafe { Array::new(result, count, ()) }
|
||||
}
|
||||
|
||||
/// Look up a metadata entry in the archive
|
||||
pub fn query_metadata<S: BnStrCompatible>(&self, key: S) -> Option<Ref<Metadata>> {
|
||||
let key = key.into_bytes_with_nul();
|
||||
let result = unsafe {
|
||||
BNTypeArchiveQueryMetadata(self.as_raw(), key.as_ref().as_ptr() as *const ffi::c_char)
|
||||
};
|
||||
(!result.is_null()).then(|| unsafe { Metadata::ref_from_raw(result) })
|
||||
}
|
||||
|
||||
/// Store a key/value pair in the archive's metadata storage
|
||||
///
|
||||
/// * `key` - key value to associate the Metadata object with
|
||||
/// * `md` - object to store.
|
||||
pub fn store_metadata<S: BnStrCompatible>(&self, key: S, md: &Metadata) {
|
||||
let key = key.into_bytes_with_nul();
|
||||
let result = unsafe {
|
||||
BNTypeArchiveStoreMetadata(
|
||||
self.as_raw(),
|
||||
key.as_ref().as_ptr() as *const ffi::c_char,
|
||||
md.handle,
|
||||
)
|
||||
};
|
||||
assert!(result);
|
||||
}
|
||||
|
||||
/// Delete a given metadata entry in the archive from the `key`
|
||||
pub fn remove_metadata<S: BnStrCompatible>(&self, key: S) -> bool {
|
||||
let key = key.into_bytes_with_nul();
|
||||
unsafe {
|
||||
BNTypeArchiveRemoveMetadata(self.as_raw(), key.as_ref().as_ptr() as *const ffi::c_char)
|
||||
}
|
||||
}
|
||||
|
||||
/// Turn a given `snapshot` id into a data stream
|
||||
pub fn serialize_snapshot<S: BnStrCompatible>(&self, snapshot: S) -> DataBuffer {
|
||||
let snapshot = snapshot.into_bytes_with_nul();
|
||||
let result = unsafe {
|
||||
BNTypeArchiveSerializeSnapshot(
|
||||
self.as_raw(),
|
||||
snapshot.as_ref().as_ptr() as *const ffi::c_char,
|
||||
)
|
||||
};
|
||||
assert!(!result.is_null());
|
||||
DataBuffer::from_raw(result)
|
||||
}
|
||||
|
||||
/// Take a serialized snapshot `data` stream and create a new snapshot from it
|
||||
pub fn deserialize_snapshot(&self, data: &DataBuffer) -> BnString {
|
||||
let result = unsafe { BNTypeArchiveDeserializeSnapshot(self.as_raw(), data.as_raw()) };
|
||||
assert!(!result.is_null());
|
||||
unsafe { BnString::from_raw(result) }
|
||||
}
|
||||
|
||||
/// Register a notification listener
|
||||
pub fn register_notification_callback<T: TypeArchiveNotificationCallback>(
|
||||
&self,
|
||||
callback: T,
|
||||
) -> TypeArchiveCallbackHandle<T> {
|
||||
// SAFETY free on [TypeArchiveCallbackHandle::Drop]
|
||||
let callback = Box::leak(Box::new(callback));
|
||||
let mut notification = BNTypeArchiveNotification {
|
||||
context: callback as *mut T as *mut ffi::c_void,
|
||||
typeAdded: Some(cb_type_added::<T>),
|
||||
typeUpdated: Some(cb_type_updated::<T>),
|
||||
typeRenamed: Some(cb_type_renamed::<T>),
|
||||
typeDeleted: Some(cb_type_deleted::<T>),
|
||||
};
|
||||
unsafe { BNRegisterTypeArchiveNotification(self.as_raw(), &mut notification) }
|
||||
TypeArchiveCallbackHandle {
|
||||
callback,
|
||||
type_archive: self.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE NotificationClosure is left private, there is no need for the user
|
||||
// to know or use it.
|
||||
#[allow(private_interfaces)]
|
||||
pub fn register_notification_closure<A, U, R, D>(
|
||||
&self,
|
||||
type_added: A,
|
||||
type_updated: U,
|
||||
type_renamed: R,
|
||||
type_deleted: D,
|
||||
) -> TypeArchiveCallbackHandle<NotificationClosure<A, U, R, D>>
|
||||
where
|
||||
A: FnMut(&TypeArchive, &str, &Type),
|
||||
U: FnMut(&TypeArchive, &str, &Type, &Type),
|
||||
R: FnMut(&TypeArchive, &str, &QualifiedName, &QualifiedName),
|
||||
D: FnMut(&TypeArchive, &str, &Type),
|
||||
{
|
||||
self.register_notification_callback(NotificationClosure {
|
||||
fun_type_added: type_added,
|
||||
fun_type_updated: type_updated,
|
||||
fun_type_renamed: type_renamed,
|
||||
fun_type_deleted: type_deleted,
|
||||
})
|
||||
}
|
||||
|
||||
/// Close a type archive, disconnecting it from any active views and closing
|
||||
/// any open file handles
|
||||
pub fn close(self) {
|
||||
unsafe { BNCloseTypeArchive(self.as_raw()) }
|
||||
// NOTE self must be dropped after, don't make it `&self`
|
||||
}
|
||||
|
||||
/// Determine if `file` is a Type Archive
|
||||
pub fn is_type_archive<P: BnStrCompatible>(file: P) -> bool {
|
||||
let file = file.into_bytes_with_nul();
|
||||
unsafe { BNIsTypeArchive(file.as_ref().as_ptr() as *const ffi::c_char) }
|
||||
}
|
||||
|
||||
// TODO implement TypeContainer
|
||||
///// Get the TypeContainer interface for this Type Archive, presenting types
|
||||
///// at the current snapshot in the archive.
|
||||
//pub fn type_container(&self) -> TypeContainer {
|
||||
// let result = unsafe { BNGetTypeArchiveTypeContainer(self.as_raw()) };
|
||||
// unsafe { TypeContainer::from_raw(ptr::NonNull::new(result).unwrap()) }
|
||||
//}
|
||||
|
||||
/// Do some function in a transaction making a new snapshot whose id is passed to func. If func throws,
|
||||
/// the transaction will be rolled back and the snapshot will not be created.
|
||||
///
|
||||
/// * `func` - Function to call
|
||||
/// * `parents` - Parent snapshot ids
|
||||
///
|
||||
/// Returns Created snapshot id
|
||||
pub fn new_snapshot_transaction<P, F>(&self, mut function: F, parents: &[BnString]) -> BnString
|
||||
where
|
||||
P: BnStrCompatible,
|
||||
F: FnMut(&str) -> bool,
|
||||
{
|
||||
unsafe extern "C" fn cb_callback<F: FnMut(&str) -> bool>(
|
||||
ctxt: *mut ffi::c_void,
|
||||
id: *const ffi::c_char,
|
||||
) -> bool {
|
||||
let fun: &mut F = &mut *(ctxt as *mut F);
|
||||
fun(&ffi::CStr::from_ptr(id).to_string_lossy())
|
||||
}
|
||||
|
||||
// SAFETY BnString and `*const ffi::c_char` are transparent
|
||||
let parents_raw = parents.as_ptr() as *const *const ffi::c_char;
|
||||
|
||||
let result = unsafe {
|
||||
BNTypeArchiveNewSnapshotTransaction(
|
||||
self.as_raw(),
|
||||
Some(cb_callback::<F>),
|
||||
&mut function as *mut F as *mut ffi::c_void,
|
||||
parents_raw,
|
||||
parents.len(),
|
||||
)
|
||||
};
|
||||
assert!(!result.is_null());
|
||||
unsafe { BnString::from_raw(result) }
|
||||
}
|
||||
|
||||
/// Merge two snapshots in the archive to produce a new snapshot
|
||||
///
|
||||
/// * `base_snapshot` - Common ancestor of snapshots
|
||||
/// * `first_snapshot` - First snapshot to merge
|
||||
/// * `second_snapshot` - Second snapshot to merge
|
||||
/// * `merge_conflicts` - List of all conflicting types, id <-> target snapshot
|
||||
/// * `progress` - Function to call for progress updates
|
||||
///
|
||||
/// Returns Snapshot id, if merge was successful, otherwise the List of
|
||||
/// conflicting type ids
|
||||
pub fn merge_snapshots<B, F, S, P, M, MI, MK>(
|
||||
&self,
|
||||
base_snapshot: B,
|
||||
first_snapshot: F,
|
||||
second_snapshot: S,
|
||||
merge_conflicts: M,
|
||||
mut progress: P,
|
||||
) -> Result<BnString, Array<BnString>>
|
||||
where
|
||||
B: BnStrCompatible,
|
||||
F: BnStrCompatible,
|
||||
S: BnStrCompatible,
|
||||
P: FnMut(usize, usize) -> bool,
|
||||
M: IntoIterator<Item = (MI, MK)>,
|
||||
MI: BnStrCompatible,
|
||||
MK: BnStrCompatible,
|
||||
{
|
||||
unsafe extern "C" fn cb_callback<F: FnMut(usize, usize) -> bool>(
|
||||
ctxt: *mut ffi::c_void,
|
||||
progress: usize,
|
||||
total: usize,
|
||||
) -> bool {
|
||||
let ctxt: &mut F = &mut *(ctxt as *mut F);
|
||||
ctxt(progress, total)
|
||||
}
|
||||
|
||||
let base_snapshot = base_snapshot.into_bytes_with_nul();
|
||||
let first_snapshot = first_snapshot.into_bytes_with_nul();
|
||||
let second_snapshot = second_snapshot.into_bytes_with_nul();
|
||||
let (merge_keys, merge_values): (Vec<BnString>, Vec<BnString>) = merge_conflicts
|
||||
.into_iter()
|
||||
.map(|(k, v)| (BnString::new(k), BnString::new(v)))
|
||||
.unzip();
|
||||
// SAFETY BnString and `*const ffi::c_char` are transparent
|
||||
let merge_keys_raw = merge_keys.as_ptr() as *const *const ffi::c_char;
|
||||
let merge_values_raw = merge_values.as_ptr() as *const *const ffi::c_char;
|
||||
|
||||
let mut conflicts_errors = ptr::null_mut();
|
||||
let mut conflicts_errors_count = 0;
|
||||
|
||||
let mut result = ptr::null_mut();
|
||||
|
||||
let success = unsafe {
|
||||
BNTypeArchiveMergeSnapshots(
|
||||
self.as_raw(),
|
||||
base_snapshot.as_ref().as_ptr() as *const ffi::c_char,
|
||||
first_snapshot.as_ref().as_ptr() as *const ffi::c_char,
|
||||
second_snapshot.as_ref().as_ptr() as *const ffi::c_char,
|
||||
merge_keys_raw,
|
||||
merge_values_raw,
|
||||
merge_keys.len(),
|
||||
&mut conflicts_errors,
|
||||
&mut conflicts_errors_count,
|
||||
&mut result,
|
||||
Some(cb_callback::<P>),
|
||||
(&mut progress) as *mut P as *mut ffi::c_void,
|
||||
)
|
||||
};
|
||||
if success {
|
||||
assert!(!result.is_null());
|
||||
Ok(unsafe { BnString::from_raw(result) })
|
||||
} else {
|
||||
assert!(!conflicts_errors.is_null());
|
||||
Err(unsafe { Array::new(conflicts_errors, conflicts_errors_count, ()) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for TypeArchive {
|
||||
type Raw = *mut BNTypeArchive;
|
||||
type Context = ();
|
||||
type Wrapped<'a> = &'a TypeArchive;
|
||||
}
|
||||
|
||||
unsafe impl CoreArrayProviderInner for TypeArchive {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeTypeArchiveList(raw, count)
|
||||
}
|
||||
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
Self::ref_from_raw(raw)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TypeArchiveCallbackHandle<T: TypeArchiveNotificationCallback> {
|
||||
callback: *mut T,
|
||||
type_archive: TypeArchive,
|
||||
}
|
||||
|
||||
impl<T: TypeArchiveNotificationCallback> Drop for TypeArchiveCallbackHandle<T> {
|
||||
fn drop(&mut self) {
|
||||
let mut notification = BNTypeArchiveNotification {
|
||||
context: self.callback as *mut ffi::c_void,
|
||||
typeAdded: Some(cb_type_added::<T>),
|
||||
typeUpdated: Some(cb_type_updated::<T>),
|
||||
typeRenamed: Some(cb_type_renamed::<T>),
|
||||
typeDeleted: Some(cb_type_deleted::<T>),
|
||||
};
|
||||
// unregister the notification callback
|
||||
unsafe {
|
||||
BNUnregisterTypeArchiveNotification(self.type_archive.as_raw(), &mut notification)
|
||||
}
|
||||
// free the context created at [TypeArchive::register_notification_callback]
|
||||
drop(unsafe { Box::from_raw(self.callback) });
|
||||
}
|
||||
}
|
||||
|
||||
pub trait TypeArchiveNotificationCallback {
|
||||
/// Called when a type is added to the archive
|
||||
///
|
||||
/// * `archive` - Source Type archive
|
||||
/// * `id` - Id of type added
|
||||
/// * `definition` - Definition of type
|
||||
fn type_added(&mut self, _archive: &TypeArchive, _id: &str, _definition: &Type) {}
|
||||
|
||||
/// Called when a type in the archive is updated to a new definition
|
||||
///
|
||||
/// * `archive` - Source Type archive
|
||||
/// * `id` - Id of type
|
||||
/// * `old_definition` - Previous definition
|
||||
/// * `new_definition` - Current definition
|
||||
fn type_updated(
|
||||
&mut self,
|
||||
_archive: &TypeArchive,
|
||||
_id: &str,
|
||||
_old_definition: &Type,
|
||||
_new_definition: &Type,
|
||||
) {
|
||||
}
|
||||
|
||||
/// Called when a type in the archive is renamed
|
||||
///
|
||||
/// * `archive` - Source Type archive
|
||||
/// * `id` - Type id
|
||||
/// * `old_name` - Previous name
|
||||
/// * `new_name` - Current name
|
||||
fn type_renamed(
|
||||
&mut self,
|
||||
_archive: &TypeArchive,
|
||||
_id: &str,
|
||||
_old_name: &QualifiedName,
|
||||
_new_name: &QualifiedName,
|
||||
) {
|
||||
}
|
||||
|
||||
/// Called when a type in the archive is deleted from the archive
|
||||
///
|
||||
/// * `archive` - Source Type archive
|
||||
/// * `id` - Id of type deleted
|
||||
/// * `definition` - Definition of type deleted
|
||||
fn type_deleted(&mut self, _archive: &TypeArchive, _id: &str, _definition: &Type) {}
|
||||
}
|
||||
|
||||
struct NotificationClosure<A, U, R, D>
|
||||
where
|
||||
A: FnMut(&TypeArchive, &str, &Type),
|
||||
U: FnMut(&TypeArchive, &str, &Type, &Type),
|
||||
R: FnMut(&TypeArchive, &str, &QualifiedName, &QualifiedName),
|
||||
D: FnMut(&TypeArchive, &str, &Type),
|
||||
{
|
||||
fun_type_added: A,
|
||||
fun_type_updated: U,
|
||||
fun_type_renamed: R,
|
||||
fun_type_deleted: D,
|
||||
}
|
||||
|
||||
impl<A, U, R, D> TypeArchiveNotificationCallback for NotificationClosure<A, U, R, D>
|
||||
where
|
||||
A: FnMut(&TypeArchive, &str, &Type),
|
||||
U: FnMut(&TypeArchive, &str, &Type, &Type),
|
||||
R: FnMut(&TypeArchive, &str, &QualifiedName, &QualifiedName),
|
||||
D: FnMut(&TypeArchive, &str, &Type),
|
||||
{
|
||||
fn type_added(&mut self, archive: &TypeArchive, id: &str, definition: &Type) {
|
||||
(self.fun_type_added)(archive, id, definition)
|
||||
}
|
||||
|
||||
fn type_updated(
|
||||
&mut self,
|
||||
archive: &TypeArchive,
|
||||
id: &str,
|
||||
old_definition: &Type,
|
||||
new_definition: &Type,
|
||||
) {
|
||||
(self.fun_type_updated)(archive, id, old_definition, new_definition)
|
||||
}
|
||||
|
||||
fn type_renamed(
|
||||
&mut self,
|
||||
archive: &TypeArchive,
|
||||
id: &str,
|
||||
old_name: &QualifiedName,
|
||||
new_name: &QualifiedName,
|
||||
) {
|
||||
(self.fun_type_renamed)(archive, id, old_name, new_name)
|
||||
}
|
||||
|
||||
fn type_deleted(&mut self, archive: &TypeArchive, id: &str, definition: &Type) {
|
||||
(self.fun_type_deleted)(archive, id, definition)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe extern "C" fn cb_type_added<T: TypeArchiveNotificationCallback>(
|
||||
ctxt: *mut ::std::os::raw::c_void,
|
||||
archive: *mut BNTypeArchive,
|
||||
id: *const ::std::os::raw::c_char,
|
||||
definition: *mut BNType,
|
||||
) {
|
||||
let ctxt: &mut T = &mut *(ctxt as *mut T);
|
||||
ctxt.type_added(
|
||||
unsafe { TypeArchive::ref_from_raw(&archive) },
|
||||
unsafe { ffi::CStr::from_ptr(id).to_string_lossy().as_ref() },
|
||||
&Type { handle: definition },
|
||||
)
|
||||
}
|
||||
unsafe extern "C" fn cb_type_updated<T: TypeArchiveNotificationCallback>(
|
||||
ctxt: *mut ::std::os::raw::c_void,
|
||||
archive: *mut BNTypeArchive,
|
||||
id: *const ::std::os::raw::c_char,
|
||||
old_definition: *mut BNType,
|
||||
new_definition: *mut BNType,
|
||||
) {
|
||||
let ctxt: &mut T = &mut *(ctxt as *mut T);
|
||||
ctxt.type_updated(
|
||||
unsafe { TypeArchive::ref_from_raw(&archive) },
|
||||
unsafe { ffi::CStr::from_ptr(id).to_string_lossy().as_ref() },
|
||||
&Type {
|
||||
handle: old_definition,
|
||||
},
|
||||
&Type {
|
||||
handle: new_definition,
|
||||
},
|
||||
)
|
||||
}
|
||||
unsafe extern "C" fn cb_type_renamed<T: TypeArchiveNotificationCallback>(
|
||||
ctxt: *mut ::std::os::raw::c_void,
|
||||
archive: *mut BNTypeArchive,
|
||||
id: *const ::std::os::raw::c_char,
|
||||
old_name: *const BNQualifiedName,
|
||||
new_name: *const BNQualifiedName,
|
||||
) {
|
||||
let ctxt: &mut T = &mut *(ctxt as *mut T);
|
||||
let old_name = mem::ManuallyDrop::new(QualifiedName(*old_name));
|
||||
let new_name = mem::ManuallyDrop::new(QualifiedName(*new_name));
|
||||
ctxt.type_renamed(
|
||||
unsafe { TypeArchive::ref_from_raw(&archive) },
|
||||
unsafe { ffi::CStr::from_ptr(id).to_string_lossy().as_ref() },
|
||||
&old_name,
|
||||
&new_name,
|
||||
)
|
||||
}
|
||||
unsafe extern "C" fn cb_type_deleted<T: TypeArchiveNotificationCallback>(
|
||||
ctxt: *mut ::std::os::raw::c_void,
|
||||
archive: *mut BNTypeArchive,
|
||||
id: *const ::std::os::raw::c_char,
|
||||
definition: *mut BNType,
|
||||
) {
|
||||
let ctxt: &mut T = &mut *(ctxt as *mut T);
|
||||
ctxt.type_deleted(
|
||||
unsafe { TypeArchive::ref_from_raw(&archive) },
|
||||
unsafe { ffi::CStr::from_ptr(id).to_string_lossy().as_ref() },
|
||||
&Type { handle: definition },
|
||||
)
|
||||
}
|
||||
367
src/typelibrary.rs
Normal file
367
src/typelibrary.rs
Normal file
@@ -0,0 +1,367 @@
|
||||
use binaryninjacore_sys::*;
|
||||
|
||||
use core::{ffi, mem, ptr};
|
||||
|
||||
use crate::{
|
||||
architecture::CoreArchitecture,
|
||||
metadata::Metadata,
|
||||
platform::Platform,
|
||||
rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref},
|
||||
string::{BnStrCompatible, BnString},
|
||||
types::{QualifiedName, QualifiedNameAndType, Type},
|
||||
};
|
||||
|
||||
#[repr(transparent)]
|
||||
pub struct TypeLibrary {
|
||||
handle: ptr::NonNull<BNTypeLibrary>,
|
||||
}
|
||||
|
||||
impl TypeLibrary {
|
||||
pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNTypeLibrary>) -> Self {
|
||||
Self { handle }
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn ref_from_raw(handle: &*mut BNTypeLibrary) -> &Self {
|
||||
assert!(!handle.is_null());
|
||||
mem::transmute(handle)
|
||||
}
|
||||
|
||||
#[allow(clippy::mut_from_ref)]
|
||||
pub(crate) unsafe fn as_raw(&self) -> &mut BNTypeLibrary {
|
||||
&mut *self.handle.as_ptr()
|
||||
}
|
||||
|
||||
pub fn new_reference(&self) -> Self {
|
||||
unsafe {
|
||||
Self::from_raw(ptr::NonNull::new(BNNewTypeLibraryReference(self.as_raw())).unwrap())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_duplicated(&self) -> Self {
|
||||
unsafe { Self::from_raw(ptr::NonNull::new(BNDuplicateTypeLibrary(self.as_raw())).unwrap()) }
|
||||
}
|
||||
|
||||
/// Creates an empty type library object with a random GUID and the provided name.
|
||||
pub fn new<S: BnStrCompatible>(arch: CoreArchitecture, name: S) -> TypeLibrary {
|
||||
let name = name.into_bytes_with_nul();
|
||||
let new_lib =
|
||||
unsafe { BNNewTypeLibrary(arch.0, name.as_ref().as_ptr() as *const ffi::c_char) };
|
||||
unsafe { TypeLibrary::from_raw(ptr::NonNull::new(new_lib).unwrap()) }
|
||||
}
|
||||
|
||||
pub fn all(arch: CoreArchitecture) -> Array<TypeLibrary> {
|
||||
let mut count = 0;
|
||||
let result = unsafe { BNGetArchitectureTypeLibraries(arch.0, &mut count) };
|
||||
assert!(!result.is_null());
|
||||
unsafe { Array::new(result, count, ()) }
|
||||
}
|
||||
|
||||
/// Decompresses a type library file to a file on disk.
|
||||
pub fn decompress_to_file<P: BnStrCompatible, O: BnStrCompatible>(path: P, output: O) -> bool {
|
||||
let path = path.into_bytes_with_nul();
|
||||
let output = output.into_bytes_with_nul();
|
||||
unsafe {
|
||||
BNTypeLibraryDecompressToFile(
|
||||
path.as_ref().as_ptr() as *const ffi::c_char,
|
||||
output.as_ref().as_ptr() as *const ffi::c_char,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Loads a finalized type library instance from file
|
||||
pub fn load_from_file<S: BnStrCompatible>(path: S) -> Option<TypeLibrary> {
|
||||
let path = path.into_bytes_with_nul();
|
||||
let handle =
|
||||
unsafe { BNLoadTypeLibraryFromFile(path.as_ref().as_ptr() as *const ffi::c_char) };
|
||||
ptr::NonNull::new(handle).map(|h| unsafe { TypeLibrary::from_raw(h) })
|
||||
}
|
||||
|
||||
/// Saves a finalized type library instance to file
|
||||
pub fn write_to_file<S: BnStrCompatible>(&self, path: S) -> bool {
|
||||
let path = path.into_bytes_with_nul();
|
||||
unsafe {
|
||||
BNWriteTypeLibraryToFile(self.as_raw(), path.as_ref().as_ptr() as *const ffi::c_char)
|
||||
}
|
||||
}
|
||||
|
||||
/// Looks up the first type library found with a matching name. Keep in mind that names are not
|
||||
/// necessarily unique.
|
||||
pub fn from_name<S: BnStrCompatible>(arch: CoreArchitecture, name: S) -> Option<TypeLibrary> {
|
||||
let name = name.into_bytes_with_nul();
|
||||
let handle = unsafe {
|
||||
BNLookupTypeLibraryByName(arch.0, name.as_ref().as_ptr() as *const ffi::c_char)
|
||||
};
|
||||
ptr::NonNull::new(handle).map(|h| unsafe { TypeLibrary::from_raw(h) })
|
||||
}
|
||||
|
||||
/// Attempts to grab a type library associated with the provided Architecture and GUID pair
|
||||
pub fn from_guid<S: BnStrCompatible>(arch: CoreArchitecture, guid: S) -> Option<TypeLibrary> {
|
||||
let guid = guid.into_bytes_with_nul();
|
||||
let handle = unsafe {
|
||||
BNLookupTypeLibraryByGuid(arch.0, guid.as_ref().as_ptr() as *const ffi::c_char)
|
||||
};
|
||||
ptr::NonNull::new(handle).map(|h| unsafe { TypeLibrary::from_raw(h) })
|
||||
}
|
||||
|
||||
/// The Architecture this type library is associated with
|
||||
pub fn arch(&self) -> CoreArchitecture {
|
||||
let arch = unsafe { BNGetTypeLibraryArchitecture(self.as_raw()) };
|
||||
assert!(!arch.is_null());
|
||||
CoreArchitecture(arch)
|
||||
}
|
||||
|
||||
/// The primary name associated with this type library
|
||||
pub fn name(&self) -> Option<BnString> {
|
||||
let result = unsafe { BNGetTypeLibraryName(self.as_raw()) };
|
||||
(!result.is_null()).then(|| unsafe { BnString::from_raw(result) })
|
||||
}
|
||||
|
||||
/// Sets the name of a type library instance that has not been finalized
|
||||
pub fn set_name<S: BnStrCompatible>(&self, value: S) {
|
||||
let value = value.into_bytes_with_nul();
|
||||
unsafe {
|
||||
BNSetTypeLibraryName(self.as_raw(), value.as_ref().as_ptr() as *const ffi::c_char)
|
||||
}
|
||||
}
|
||||
|
||||
/// The `dependency_name` of a library is the name used to record dependencies across
|
||||
/// type libraries. This allows, for example, a library with the name "musl_libc" to have
|
||||
/// dependencies on it recorded as "libc_generic", allowing a type library to be used across
|
||||
/// multiple platforms where each has a specific libc that also provides the name "libc_generic"
|
||||
/// as an `alternate_name`.
|
||||
pub fn dependency_name(&self) -> Option<BnString> {
|
||||
let result = unsafe { BNGetTypeLibraryDependencyName(self.as_raw()) };
|
||||
(!result.is_null()).then(|| unsafe { BnString::from_raw(result) })
|
||||
}
|
||||
|
||||
/// Sets the dependency name of a type library instance that has not been finalized
|
||||
pub fn set_dependency_name<S: BnStrCompatible>(&self, value: S) {
|
||||
let value = value.into_bytes_with_nul();
|
||||
unsafe {
|
||||
BNSetTypeLibraryDependencyName(
|
||||
self.as_raw(),
|
||||
value.as_ref().as_ptr() as *const ffi::c_char,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the GUID associated with the type library
|
||||
pub fn guid(&self) -> Option<BnString> {
|
||||
let result = unsafe { BNGetTypeLibraryGuid(self.as_raw()) };
|
||||
(!result.is_null()).then(|| unsafe { BnString::from_raw(result) })
|
||||
}
|
||||
|
||||
/// Sets the GUID of a type library instance that has not been finalized
|
||||
pub fn set_guid<S: BnStrCompatible>(&self, value: S) {
|
||||
let value = value.into_bytes_with_nul();
|
||||
unsafe {
|
||||
BNSetTypeLibraryGuid(self.as_raw(), value.as_ref().as_ptr() as *const ffi::c_char)
|
||||
}
|
||||
}
|
||||
|
||||
/// A list of extra names that will be considered a match by [Platform::get_type_libraries_by_name]
|
||||
pub fn alternate_names(&self) -> Array<BnString> {
|
||||
let mut count = 0;
|
||||
let result = unsafe { BNGetTypeLibraryAlternateNames(self.as_raw(), &mut count) };
|
||||
assert!(!result.is_null());
|
||||
unsafe { Array::new(result, count, ()) }
|
||||
}
|
||||
|
||||
/// Adds an extra name to this type library used during library lookups and dependency resolution
|
||||
pub fn add_alternate_name<S: BnStrCompatible>(&self, value: S) {
|
||||
let value = value.into_bytes_with_nul();
|
||||
unsafe {
|
||||
BNAddTypeLibraryAlternateName(
|
||||
self.as_raw(),
|
||||
value.as_ref().as_ptr() as *const ffi::c_char,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a list of all platform names that this type library will register with during platform
|
||||
/// type registration.
|
||||
///
|
||||
/// This returns strings, not Platform objects, as type libraries can be distributed with support for
|
||||
/// Platforms that may not be present.
|
||||
pub fn platform_names(&self) -> Array<BnString> {
|
||||
let mut count = 0;
|
||||
let result = unsafe { BNGetTypeLibraryPlatforms(self.as_raw(), &mut count) };
|
||||
assert!(!result.is_null());
|
||||
unsafe { Array::new(result, count, ()) }
|
||||
}
|
||||
|
||||
/// Associate a platform with a type library instance that has not been finalized.
|
||||
///
|
||||
/// This will cause the library to be searchable by [Platform::get_type_libraries_by_name]
|
||||
/// when loaded.
|
||||
///
|
||||
/// This does not have side affects until finalization of the type library.
|
||||
pub fn add_platform(&self, plat: &Platform) {
|
||||
unsafe { BNAddTypeLibraryPlatform(self.as_raw(), plat.handle) }
|
||||
}
|
||||
|
||||
/// Clears the list of platforms associated with a type library instance that has not been finalized
|
||||
pub fn clear_platforms(&self) {
|
||||
unsafe { BNClearTypeLibraryPlatforms(self.as_raw()) }
|
||||
}
|
||||
|
||||
/// Flags a newly created type library instance as finalized and makes it available for Platform and Architecture
|
||||
/// type library searches
|
||||
pub fn finalize(&self) -> bool {
|
||||
unsafe { BNFinalizeTypeLibrary(self.as_raw()) }
|
||||
}
|
||||
|
||||
/// Retrieves a metadata associated with the given key stored in the type library
|
||||
pub fn query_metadata<S: BnStrCompatible>(&self, key: S) -> Option<Metadata> {
|
||||
let key = key.into_bytes_with_nul();
|
||||
let result = unsafe {
|
||||
BNTypeLibraryQueryMetadata(self.as_raw(), key.as_ref().as_ptr() as *const ffi::c_char)
|
||||
};
|
||||
(!result.is_null()).then(|| unsafe { Metadata::from_raw(result) })
|
||||
}
|
||||
|
||||
/// Stores an object for the given key in the current type library. Objects stored using
|
||||
/// `store_metadata` can be retrieved from any reference to the library. Objects stored are not arbitrary python
|
||||
/// objects! The values stored must be able to be held in a Metadata object. See [Metadata]
|
||||
/// for more information. Python objects could obviously be serialized using pickle but this intentionally
|
||||
/// a task left to the user since there is the potential security issues.
|
||||
///
|
||||
/// This is primarily intended as a way to store Platform specific information relevant to BinaryView implementations;
|
||||
/// for example the PE BinaryViewType uses type library metadata to retrieve ordinal information, when available.
|
||||
///
|
||||
/// * `key` - key value to associate the Metadata object with
|
||||
/// * `md` - object to store.
|
||||
pub fn store_metadata<S: BnStrCompatible>(&self, key: S, md: &Metadata) {
|
||||
let key = key.into_bytes_with_nul();
|
||||
unsafe {
|
||||
BNTypeLibraryStoreMetadata(
|
||||
self.as_raw(),
|
||||
key.as_ref().as_ptr() as *const ffi::c_char,
|
||||
md.handle,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Removes the metadata associated with key from the current type library.
|
||||
pub fn remove_metadata<S: BnStrCompatible>(&self, key: S) {
|
||||
let key = key.into_bytes_with_nul();
|
||||
unsafe {
|
||||
BNTypeLibraryRemoveMetadata(self.as_raw(), key.as_ref().as_ptr() as *const ffi::c_char)
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieves the metadata associated with the current type library.
|
||||
pub fn metadata(&self) -> Metadata {
|
||||
let md_handle = unsafe { BNTypeLibraryGetMetadata(self.as_raw()) };
|
||||
assert!(!md_handle.is_null());
|
||||
unsafe { Metadata::from_raw(md_handle) }
|
||||
}
|
||||
|
||||
// TODO: implement TypeContainer
|
||||
// /// Type Container for all TYPES within the Type Library. Objects are not included.
|
||||
// /// The Type Container's Platform will be the first platform associated with the Type Library.
|
||||
// pub fn type_container(&self) -> TypeContainer {
|
||||
// let result = unsafe{ BNGetTypeLibraryTypeContainer(self.as_raw())};
|
||||
// unsafe{TypeContainer::from_raw(ptr::NonNull::new(result).unwrap())}
|
||||
// }
|
||||
|
||||
/// Directly inserts a named object into the type library's object store.
|
||||
/// This is not done recursively, so care should be taken that types referring to other types
|
||||
/// through NamedTypeReferences are already appropriately prepared.
|
||||
///
|
||||
/// To add types and objects from an existing BinaryView, it is recommended to use
|
||||
/// `export_object_to_library <binaryview.BinaryView.export_object_to_library>`, which will automatically pull in
|
||||
/// all referenced types and record additional dependencies as needed.
|
||||
pub fn add_named_object(&self, name: &QualifiedName, type_: &Type) {
|
||||
unsafe {
|
||||
BNAddTypeLibraryNamedObject(self.as_raw(), &name.0 as *const _ as *mut _, type_.handle)
|
||||
}
|
||||
}
|
||||
|
||||
/// Directly inserts a named object into the type library's object store.
|
||||
/// This is not done recursively, so care should be taken that types referring to other types
|
||||
/// through NamedTypeReferences are already appropriately prepared.
|
||||
///
|
||||
/// To add types and objects from an existing BinaryView, it is recommended to use
|
||||
/// `export_type_to_library <binaryview.BinaryView.export_type_to_library>`, which will automatically pull in
|
||||
/// all referenced types and record additional dependencies as needed.
|
||||
pub fn add_named_type(&self, name: &QualifiedNameAndType, type_: &Type) {
|
||||
unsafe {
|
||||
BNAddTypeLibraryNamedType(self.as_raw(), &name.0 as *const _ as *mut _, type_.handle)
|
||||
}
|
||||
}
|
||||
|
||||
/// Manually flag NamedTypeReferences to the given QualifiedName as originating from another source
|
||||
/// TypeLibrary with the given dependency name.
|
||||
///
|
||||
/// <div class="warning">
|
||||
///
|
||||
/// Use this api with extreme caution.
|
||||
///
|
||||
/// </div/
|
||||
pub fn add_type_source<S: BnStrCompatible>(&self, name: &QualifiedName, source: S) {
|
||||
let source = source.into_bytes_with_nul();
|
||||
unsafe {
|
||||
BNAddTypeLibraryNamedTypeSource(
|
||||
self.as_raw(),
|
||||
&name.0 as *const _ as *mut _,
|
||||
source.as_ref().as_ptr() as *const ffi::c_char,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Direct extracts a reference to a contained object -- when
|
||||
/// attempting to extract types from a library into a BinaryView, consider using
|
||||
/// `import_library_object <binaryview.BinaryView.import_library_object>` instead.
|
||||
pub fn get_named_object(&self, name: &QualifiedName) -> Option<Ref<Type>> {
|
||||
let t =
|
||||
unsafe { BNGetTypeLibraryNamedObject(self.as_raw(), &name.0 as *const _ as *mut _) };
|
||||
(!t.is_null()).then(|| unsafe { Type::ref_from_raw(t) })
|
||||
}
|
||||
|
||||
/// Direct extracts a reference to a contained type -- when
|
||||
/// attempting to extract types from a library into a BinaryView, consider using
|
||||
/// `import_library_type <binaryview.BinaryView.import_library_type>` instead.
|
||||
pub fn get_named_type(&self, name: &QualifiedName) -> Option<Ref<Type>> {
|
||||
let t = unsafe { BNGetTypeLibraryNamedType(self.as_raw(), &name.0 as *const _ as *mut _) };
|
||||
(!t.is_null()).then(|| unsafe { Type::ref_from_raw(t) })
|
||||
}
|
||||
|
||||
/// A dict containing all named objects (functions, exported variables) provided by a type library
|
||||
pub fn named_objects(&self) -> Array<QualifiedNameAndType> {
|
||||
let mut count = 0;
|
||||
let result = unsafe { BNGetTypeLibraryNamedObjects(self.as_raw(), &mut count) };
|
||||
assert!(!result.is_null());
|
||||
unsafe { Array::new(result, count, ()) }
|
||||
}
|
||||
|
||||
/// A dict containing all named types provided by a type library
|
||||
pub fn named_types(&self) -> Array<QualifiedNameAndType> {
|
||||
let mut count = 0;
|
||||
let result = unsafe { BNGetTypeLibraryNamedTypes(self.as_raw(), &mut count) };
|
||||
assert!(!result.is_null());
|
||||
unsafe { Array::new(result, count, ()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for TypeLibrary {
|
||||
fn drop(&mut self) {
|
||||
unsafe { BNFreeTypeLibrary(self.as_raw()) }
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for TypeLibrary {
|
||||
type Raw = *mut BNTypeLibrary;
|
||||
type Context = ();
|
||||
type Wrapped<'a> = &'a Self;
|
||||
}
|
||||
|
||||
unsafe impl CoreArrayProviderInner for TypeLibrary {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeTypeLibraryList(raw, count)
|
||||
}
|
||||
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
Self::ref_from_raw(raw)
|
||||
}
|
||||
}
|
||||
1427
src/types.rs
1427
src/types.rs
File diff suppressed because it is too large
Load Diff
272
src/update.rs
Normal file
272
src/update.rs
Normal file
@@ -0,0 +1,272 @@
|
||||
use core::{ffi, mem, ptr};
|
||||
use std::time::{Duration, SystemTime, UNIX_EPOCH};
|
||||
|
||||
use binaryninjacore_sys::*;
|
||||
|
||||
use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner};
|
||||
use crate::string::BnString;
|
||||
|
||||
pub type UpdateResult = BNUpdateResult;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct UpdateChannel {
|
||||
pub name: BnString,
|
||||
pub description: BnString,
|
||||
pub latest_version: BnString,
|
||||
// NOTE don't allow the user to create his own UpdateChannel
|
||||
_lock: core::marker::PhantomData<()>,
|
||||
}
|
||||
|
||||
impl UpdateChannel {
|
||||
pub(crate) unsafe fn ref_from_raw(handle: &BNUpdateChannel) -> &Self {
|
||||
mem::transmute(handle)
|
||||
}
|
||||
|
||||
pub fn all() -> Result<Array<UpdateChannel>, BnString> {
|
||||
let mut count = 0;
|
||||
let mut errors = ptr::null_mut();
|
||||
let result = unsafe { BNGetUpdateChannels(&mut count, &mut errors) };
|
||||
if !errors.is_null() {
|
||||
Err(unsafe { BnString::from_raw(errors) })
|
||||
} else {
|
||||
assert!(!result.is_null());
|
||||
Ok(unsafe { Array::new(result, count, ()) })
|
||||
}
|
||||
}
|
||||
|
||||
/// List of versions
|
||||
pub fn versions(&self) -> Result<Array<UpdateVersion>, BnString> {
|
||||
let mut count = 0;
|
||||
let mut errors = ptr::null_mut();
|
||||
let result =
|
||||
unsafe { BNGetUpdateChannelVersions(self.name.as_ptr(), &mut count, &mut errors) };
|
||||
if !errors.is_null() {
|
||||
Err(unsafe { BnString::from_raw(errors) })
|
||||
} else {
|
||||
assert!(!result.is_null());
|
||||
Ok(unsafe { Array::new(result, count, ()) })
|
||||
}
|
||||
}
|
||||
|
||||
/// Latest version
|
||||
pub fn latest_version(&self) -> Result<UpdateVersion, BnString> {
|
||||
let last_version = &self.latest_version;
|
||||
let versions = self.versions()?;
|
||||
for version in &versions {
|
||||
if &version.version == last_version {
|
||||
return Ok(version.clone());
|
||||
}
|
||||
}
|
||||
panic!();
|
||||
}
|
||||
|
||||
/// Whether updates are available
|
||||
pub fn updates_available(&self) -> Result<bool, BnString> {
|
||||
let mut errors = ptr::null_mut();
|
||||
let result = unsafe {
|
||||
BNAreUpdatesAvailable(
|
||||
self.name.as_ptr(),
|
||||
ptr::null_mut(),
|
||||
ptr::null_mut(),
|
||||
&mut errors,
|
||||
)
|
||||
};
|
||||
if !errors.is_null() {
|
||||
Err(unsafe { BnString::from_raw(errors) })
|
||||
} else {
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_to_latest(&self) -> Result<UpdateResult, BnString> {
|
||||
let mut errors = ptr::null_mut();
|
||||
let result = unsafe {
|
||||
BNUpdateToLatestVersion(
|
||||
self.name.as_ptr(),
|
||||
&mut errors,
|
||||
Some(cb_progress_nop),
|
||||
ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
if !errors.is_null() {
|
||||
Err(unsafe { BnString::from_raw(errors) })
|
||||
} else {
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_to_latest_with_progress<F>(
|
||||
&self,
|
||||
mut progress: F,
|
||||
) -> Result<UpdateResult, BnString>
|
||||
where
|
||||
F: FnMut(u64, u64) -> bool,
|
||||
{
|
||||
let mut errors = ptr::null_mut();
|
||||
let result = unsafe {
|
||||
BNUpdateToLatestVersion(
|
||||
self.name.as_ptr(),
|
||||
&mut errors,
|
||||
Some(cb_progress::<F>),
|
||||
&mut progress as *mut _ as *mut ffi::c_void,
|
||||
)
|
||||
};
|
||||
if !errors.is_null() {
|
||||
Err(unsafe { BnString::from_raw(errors) })
|
||||
} else {
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&self, version: &UpdateVersion) -> Result<UpdateResult, BnString> {
|
||||
let mut errors = ptr::null_mut();
|
||||
let result = unsafe {
|
||||
BNUpdateToVersion(
|
||||
self.name.as_ptr(),
|
||||
version.version.as_ptr(),
|
||||
&mut errors,
|
||||
Some(cb_progress_nop),
|
||||
ptr::null_mut(),
|
||||
)
|
||||
};
|
||||
if !errors.is_null() {
|
||||
Err(unsafe { BnString::from_raw(errors) })
|
||||
} else {
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update_with_progress<F>(
|
||||
&self,
|
||||
version: &UpdateVersion,
|
||||
mut progress: F,
|
||||
) -> Result<UpdateResult, BnString>
|
||||
where
|
||||
F: FnMut(u64, u64) -> bool,
|
||||
{
|
||||
let mut errors = ptr::null_mut();
|
||||
let result = unsafe {
|
||||
BNUpdateToVersion(
|
||||
self.name.as_ptr(),
|
||||
version.version.as_ptr(),
|
||||
&mut errors,
|
||||
Some(cb_progress::<F>),
|
||||
&mut progress as *mut _ as *mut ffi::c_void,
|
||||
)
|
||||
};
|
||||
if !errors.is_null() {
|
||||
Err(unsafe { BnString::from_raw(errors) })
|
||||
} else {
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for UpdateChannel {
|
||||
type Raw = BNUpdateChannel;
|
||||
type Context = ();
|
||||
type Wrapped<'a> = &'a Self;
|
||||
}
|
||||
|
||||
unsafe impl CoreArrayProviderInner for UpdateChannel {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeUpdateChannelList(raw, count)
|
||||
}
|
||||
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
UpdateChannel::ref_from_raw(raw)
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Clone)]
|
||||
pub struct UpdateVersion {
|
||||
pub version: BnString,
|
||||
pub notes: BnString,
|
||||
time: u64,
|
||||
// NOTE don't allow the user to create his own UpdateVersion
|
||||
_lock: core::marker::PhantomData<()>,
|
||||
}
|
||||
|
||||
impl UpdateVersion {
|
||||
pub(crate) unsafe fn ref_from_raw(handle: &BNUpdateVersion) -> &Self {
|
||||
mem::transmute(handle)
|
||||
}
|
||||
|
||||
pub fn time(&self) -> SystemTime {
|
||||
UNIX_EPOCH + Duration::from_secs(self.time)
|
||||
}
|
||||
|
||||
pub fn set_time(&mut self, time: SystemTime) {
|
||||
let epoch = time.duration_since(UNIX_EPOCH).unwrap();
|
||||
self.time = epoch.as_secs();
|
||||
}
|
||||
}
|
||||
|
||||
impl CoreArrayProvider for UpdateVersion {
|
||||
type Raw = BNUpdateVersion;
|
||||
type Context = ();
|
||||
type Wrapped<'a> = &'a Self;
|
||||
}
|
||||
|
||||
unsafe impl CoreArrayProviderInner for UpdateVersion {
|
||||
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
|
||||
BNFreeUpdateChannelVersionList(raw, count)
|
||||
}
|
||||
|
||||
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
|
||||
UpdateVersion::ref_from_raw(raw)
|
||||
}
|
||||
}
|
||||
|
||||
/// queries if auto updates are enabled.
|
||||
pub fn are_auto_updates_enabled() -> bool {
|
||||
unsafe { BNAreAutoUpdatesEnabled() }
|
||||
}
|
||||
|
||||
/// sets auto update enabled status.
|
||||
pub fn set_auto_updates_enabled(enabled: bool) {
|
||||
unsafe { BNSetAutoUpdatesEnabled(enabled) }
|
||||
}
|
||||
|
||||
/// returns the time stamp for the last time updates were checked.
|
||||
pub fn get_time_since_last_update_check() -> u64 {
|
||||
unsafe { BNGetTimeSinceLastUpdateCheck() }
|
||||
}
|
||||
|
||||
/// whether an update has been downloaded and is waiting installation
|
||||
pub fn is_update_installation_pending() -> bool {
|
||||
unsafe { BNIsUpdateInstallationPending() }
|
||||
}
|
||||
|
||||
/// installs any pending updates
|
||||
pub fn install_pending_update() -> Result<(), BnString> {
|
||||
let mut errors = ptr::null_mut();
|
||||
unsafe { BNInstallPendingUpdate(&mut errors) };
|
||||
if !errors.is_null() {
|
||||
Err(unsafe { BnString::from_raw(errors) })
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn updates_checked() {
|
||||
unsafe { BNUpdatesChecked() }
|
||||
}
|
||||
|
||||
unsafe extern "C" fn cb_progress_nop(
|
||||
_ctxt: *mut ::std::os::raw::c_void,
|
||||
_progress: u64,
|
||||
_total: u64,
|
||||
) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
unsafe extern "C" fn cb_progress<F: FnMut(u64, u64) -> bool>(
|
||||
ctxt: *mut ::std::os::raw::c_void,
|
||||
progress: u64,
|
||||
total: u64,
|
||||
) -> bool {
|
||||
let ctxt: &mut F = &mut *(ctxt as *mut F);
|
||||
ctxt(progress, total)
|
||||
}
|
||||
Reference in New Issue
Block a user