summaryrefslogtreecommitdiff
path: root/rust/src
diff options
context:
space:
mode:
authorMason Reed <mason@vector35.com>2024-12-22 01:21:02 -0500
committerMason Reed <mason@vector35.com>2025-01-25 00:52:29 -0500
commit6a63c17853009ce7a0071458e39c140a09440230 (patch)
treead59ab3b356b0a2c60423e55f62c996891788788 /rust/src
parentbfa1d409b2d5e734804a5fdb4c68c90a93712f1f (diff)
Rust refactor
Moves a bunch of stuff out of src/types.rs that did not belong: - Confidence - Variable - Function specific stuff - Refactored InstructionInfo, see the msp430 and riscv examples. - Renamed Function::from_raw to Function::ref_from_raw and fixed places where the ref was incremented twice - Fixed FunctionRecognizer leaking functions (see above) - Fixed some apis incorrectly returning Result where Option is clearer - Started to move destructured types to the From trait for converting to an from ffi types, see Location for an example - Started to remove bad module level imports (importing std modules like mem all over the place) - Moved some wrapper handle types to named handle field (this improves readability), see CoreArchitecture for an example - Removed some unintuitive getters, this is bad practice for Rust code, just access the field directly, see DataVariable for an example - General code cleanup, purposely did not run rustfmt, that will be a single seperate commit More rust cleanup - Fixed invalid views being able to invoke UB when dealing with databases - Cleaned up some helper code in dwarf_import - Fixed inverted is_null checks causing crashes! Oops! More rust cleanup Still a WIP, I think branch info is still invalid, need to figure out the issue there. - Fixed some invalid Ref lifetimes when constructing indirectly, see Array<DataVariable> for example - Added some more comments - Removed some "magic" functions like MLIL Function::ssa_variables There are still a bunch of invalid lifetimes that aren't crashing us due to the usage of those API's not living long enough. But they ARE an issue. More rust cleanup Trying to comment more TODO's as I go along. - Renamed llil::Function to llil::LowLevelILFunction for clarity and consistency - Take base structures by ref in StructureBuilder::set_base_structures to prevent premature drops - Added more raw to wrapper conversions - Removed UB prone apis - Getting rid of more core module references, use std! - Removed improper Guard usage in array impls for wrapper types with no context - Untangling the UB of the Label api, still only half done :/ More rust cleanup - Misc formatting - Made Logger ref counted - Fixed leaking name of logger every time something was logged - Fixed the last (hopefully) of the unresolved labels - Simplified some code Fix leak in DebugInfo::AddType componentArray was never freed Add more HLIL related functions to rust More rust cleanup improve the CustomBinaryView init process Canonicalize path in `create_delete_empty` test Link core in rust When linking you must depend on the -sys crate. This is because linker arguments (what says where to find binaryninjacore) are NOT transitive. The top level application crate MUST provide it. For more information see: - https://github.com/rust-lang/cargo/issues/9554#issuecomment-857882964 - https://github.com/oxidecomputer/omicron/pull/225 Remove vendored pdb crate Use cargo to manage the git repo ref instead Fix misc rustdoc warnings Move actual plugins out of `rust/examples` and into `plugins` This is where all shipped public plugins that are not arch/view/platform/lang will be at from now on Originally they were in the rust workspace, meaning they all shared a Cargo.lock which is ill-advised. More rust cleanup - More clarification on plugin/executable requirements - Made examples actually rust examples - Add Display impl for InstructionTextToken - Renamed feature "noexports" to "no_exports" Move under_construction.png to assets directory This really did bother me Remove unneeded `extern crate bindgen` Replace nop'd log::info with println We don't register a compatible log sink so they will just get sent into the void Move inline tests into tests directory This is being done in the hopes of curbing the multi-thousand line files that will occur once we flesh out the tests Format rust code Update rust ci Still need to add support for running tests in ci More rust cleanup - Architecture id's are now typed accordingly - Fix some clippy lints - Make instruction index public in LLIL - Removed useless helper functions - LLIL expressions and instruction indexes are now typed accordingly Generate binaryninjacore-sys documentation This should show binaryninjacore-sys alongside binaryninja crate More rust cleanup - Remove lazy_static dependency - Remove hacky impl Debug for Type and just use the display impl - Add more debug impls - Reorder some top level namespace items - Add first type test Remove unneeded script helper in rust api More rust cleanup - Added main thread handler api - Register a headless main thread handler by default in initialization - Refactor QualifiedName to be properly owned - Loosened some type constraints on some apis involving QualifiedName - Fixed some apis that were crashing due to incorrect param types - Removed extern crate cruft for log crate - Simplified headless initialization using more wrapper apis - Fixed segments leaking because of no ref wrapper, see BinaryViewExt::segment_at - Added rstest to manage headless init in unit tests - Added some more unit tests - Refactored demangler api to be more ergonomic - Fixed minidump plugin not building More rust cleanup - Fixup usage of QualifiedName in plugins - Make QualifiedName more usable Implement rust TypeParser fix Platform TypeParser related functions separate User and System implementations of TypeParserResult Implement rust TypeContainer More rust cleanup - Hopefully fixed the rust.yml CI - Added TypePrinter API (this is still WIP and will crash) - Added TypeParser API - Added TypeContainer API - More work around QualifiedName apis Oh your suppose to do this Add workflow_dispatch trigger to rust.yml More rust fixes - Swapped some usage of raw 255 to MAX_CONFIDENCE, no one likes magic numbers - New InstructionTextToken API, complete with owned data, this still needs a lot of testing. - InstructionTextTokenKind describes a destructured InstructionTextToken, this should make token usage much clearer, some docs pending - Added some misc Default and Debug impls - Updated TypePrinter and architectures to use new InstructionTextToken API Misc formatting changes More rust cleanup - Fixed MANY memory leaks (most due to QualifiedName) - Made StructureBuilder more builder and less structure - Fixed CMakeLists.txt that were globbing entire api, resulting in 100 second slowdown on cmake generation - Added more Debug impl's - Added some more tests - Fixed TypeParserResult UB - Moved the From impls to blank impl for clarity, we have multiple different variants of core to rust for some structures, it should be explicit which one you are choosing. - PossibleValueSet should now be able to allocate so we can go from rust to core with those variants that require allocation - Misc doc code formatting Misc clippy lints and clippy CI Co-authored-by: Michael Krasnitski <michael.krasnitski@gmail.com> Fix typo in rust CI Misc rust formatting Fix misc typos and add typos to rust CI Add cargo workspace This will help tooling and external contributors get a map of the rust crates within binaryninja-api More rust cleanup - Format all rust plugins - Fix some tests that were out of date - Simplify WARP tests to only binaries, building object files from source is a pain - Link to core in all rust plugins - Fix some memory leaks - Update warp insta snapshots - Fix some misc clippy lints Run rust tests in CI This commit also coincides with the creation of the "testing" environment which exposes a BN_SERIAL secret for pulling a headless Binary Ninja Install missing wayland dependency in github CI Apparently its needed for linux file picker for the WARP integration Set the BINARYNINJADIR so rust can find binaryninjacore in CI The chances of this working are low Misc remove unused dependency Rust misc formatting fixes Improve initialization in rust headless scripts Provide sensible errors and validation to rust headless scripts, solves https://github.com/Vector35/binaryninja-api/issues/5796 Add BN_LICENSE environment variable to rust CI We pass the serial to download binary ninja, but we never provided the license for core initialization Fix typo More rust cleanup - Improved binary view initialization (see init_with_opts) - Allow floating license to free itself before initialization - Add initialization unit test - Add better Debug impls for some common types - Use Path api for opening binary views, this is not breaking as it uses the AsRef impl - Bump rayon dependency and constrain dependencies to x.x Update readme and include that directly in the rustdocs More rust documentation changes Add format comment to InitializationOptions::with_license Misc formatting and clippy lint allow More rust cleanup - Remove under_construction.png from the build.rs it has been removed - Use InstructionIndex in more places - Add missing PartialOrd and Ord impls for id types More rust cleanup - Make workflow cloning explicit - Add workflow tests - Add missing property string list getting for settings - Remove IntoActivityName (see https://github.com/Vector35/binaryninja-api/pull/6257) More rust cleanup This commit is half done Misc rust formatting More rust cleanup - Renamed common name conflictions (I will put my justification in the PR) - Fixed invalid instruction retrieval for LLIL - Added common aliases for llil function, instruction and expression types (see my comment in the PR) - Refactored the instruction retrieval for LLIL, MLIL and HLIL - Added instruction index types to MLIL and HLIL - Moved llil module to lowlevelil module (mlil and hlil will be moved as well) - Added preliminary LLIL unit testing Fix typos Misc clippy fixes More rust cleanup - Normalized modules - Split some code out into own files - Fixed some UB in type archive and projects - Improved API around type archives and projects substantially - Added ProgressExecutor abstraction for functions which have a progress callback - Improved background task documentation and added unit tests - Added worker thread api and unit tests - Moved some owned types to ref types, this is still not complete, but this is the path forward. - Add external location/library accessors to the binary view - Added some misc documentation - Replaced mod.rs with the module name source file Still need to normalize some paths and also update some documentation surrounding that change. Update some tests and examples Fix background task tests colliding We were creating multiple background tasks with the same progress text on multiple threads More rust cleanup - Fixed progress executor freeing itself after one iteration - Updated the last of the doc imports - Moved mainthread to main_thread - Made project creation and opening failable We could probably AsRef<ProgressExecutor> to get around the allocation and free inside the function bodies, not high priority as those functions are long running anyways. Move binary view initialization into function body for LLIL test Normalize test file names More rust cleanup - Updated README to clarify offline documentation - Refactored settings api - Added settings example to show dumping settings value and specific properties - Use the workspace to depend on binaryninja and binaryninjacore-sys - Remove binaryninjacore-sys as a workspace member (its not really required) Update workflow test to new settings api More rust cleanup - Rename Label to LowLevelILLabel - Update the functions label map automatically This fixed a major api blunder where the label map is returned as a reference and originally resulted in UB prone lifetime semantics. It was temporarily "fixed" with explicit label updates in the architecture implementation code. But that was less than ideal and was easy to mess up. Now the label map will be updated automatically as the location of labels is now tracked. Misc clippy lints More rust cleanup - Get rid of RawFunctionViewType - Add better Debug impl for Function More rust cleanup - Fixed the documentation icon using local files (thank you @mkrasnitski) - Fixed labels being updated and overwriting the label location used to update the label map More rust cleanup - Added unit tests for MLIL and HLIL - "Fixed" MLIL, LLIL, and HLIL having issues regarding Instruction vs Expression indexes - Renamed CallingConvention to CoreCallingConvention and removed architecture generic - Renamed CallingConventionBase to CallingConvention - Simplified calling convention code and fixed some bugs with implicit registers - Added impl Debug to MLIL and HLIL instructions Still need to at some point add an Expression to MLIL and HLIL. We also might want to look into having the Instruction kind just return the expression kind. Misc clippy lint More rust cleanup - Allow calling conventions to be registered for multiple architectures - Swapped a unreachable statement to an unimplemented statement More rust cleanup - Fixed the issue with PDB types, this has caused me an insane amount of grief - Fixed LLIL visit_tree missing LLIL_LOAD expressions - Added LLIL visitor test - Made all WARP file pickers use the rfd crate Use the dev branch of Binary Ninja in rust CI Misc rust fmt More rust cleanup - Refactored BinaryReader and BinaryWriter - Added some warnings to high_level_il and medium_level_il modules that they are unstable - Add BinaryReader and BinaryWriter tests - Changed BinaryView len to return u64 (that is what the core returns) - Misc formatting changes - Remove extern uses in lib.rs Add impl Debug for BinaryReader and BinaryWriter Turn off broken tests Add more info to the rust README.md More rust cleanup - Make EdgeStyle type not wrap raw - Regression tests for WARP will run on all bins in the out dir now impl rust Collaboration and Remote API Fix typo Update collaboration API Makes collaboration more in line with the refactor. Still a lot of work to do. Namely still need: - Proper errors - _with_opts functions - More ergonomic api - Better connection procedure - Updated documentation - A LOT of unit tests - An example - Typed id's for everything (i dont want BnString as the id!!!) - NEED to refactor the progress callbacks into the new progress api, but we should pull in some of the stuff the collab progress has - Elimination of apis that are dumb helpers Separate out the rust testing and use pull_request_target pull_request_target allows PR's to access the headless license, for this to be safe we need to prevent people from running the job. To prevent the job from being ran we add an environment requirement on testing that a reviewer must review the code and then manually approve it to run. More rust cleanup - Use GroupId instead of u64 - Use ProgressCallback in place of ProgressExecutor - Misc cleanup of FileMetadata - Add `save_to_path` and `save_to_accessor` to save modified binaries - Added binary_view unit tests - Added collaboration unit tests - Fixed a few issues with the collaboration apis - Renamed Command registration functions so that there is no import ambiguity - Split out RemoteUndoEntry - Collaboration apis now have a explicit `_with_progress` set of apis - Misc clippy lint fixes Fix some typos More rust cleanup - Add extra info to README.md - Refactor components api - Add components unit test Add testing and documentation to contributing section in README.md Fix misc doc comments
Diffstat (limited to 'rust/src')
-rw-r--r--rust/src/architecture.rs1581
-rw-r--r--rust/src/background_task.rs (renamed from rust/src/backgroundtask.rs)40
-rw-r--r--rust/src/basic_block.rs (renamed from rust/src/basicblock.rs)106
-rw-r--r--rust/src/binary_reader.rs173
-rw-r--r--rust/src/binary_view.rs (renamed from rust/src/binaryview.rs)976
-rw-r--r--rust/src/binary_writer.rs152
-rw-r--r--rust/src/binaryreader.rs101
-rw-r--r--rust/src/binarywriter.rs99
-rw-r--r--rust/src/calling_convention.rs (renamed from rust/src/callingconvention.rs)475
-rw-r--r--rust/src/collaboration.rs153
-rw-r--r--rust/src/collaboration/changeset.rs116
-rw-r--r--rust/src/collaboration/file.rs647
-rw-r--r--rust/src/collaboration/folder.rs180
-rw-r--r--rust/src/collaboration/group.rs196
-rw-r--r--rust/src/collaboration/merge.rs204
-rw-r--r--rust/src/collaboration/permission.rs159
-rw-r--r--rust/src/collaboration/project.rs1001
-rw-r--r--rust/src/collaboration/remote.rs959
-rw-r--r--rust/src/collaboration/snapshot.rs368
-rw-r--r--rust/src/collaboration/sync.rs941
-rw-r--r--rust/src/collaboration/undo.rs166
-rw-r--r--rust/src/collaboration/user.rs154
-rw-r--r--rust/src/command.rs42
-rw-r--r--rust/src/component.rs199
-rw-r--r--rust/src/confidence.rs299
-rw-r--r--rust/src/custom_binary_view.rs (renamed from rust/src/custombinaryview.rs)300
-rw-r--r--rust/src/data_buffer.rs (renamed from rust/src/databuffer.rs)107
-rw-r--r--rust/src/database.rs617
-rw-r--r--rust/src/database/kvs.rs135
-rw-r--r--rust/src/database/snapshot.rs277
-rw-r--r--rust/src/database/undo.rs168
-rw-r--r--rust/src/debuginfo.rs336
-rw-r--r--rust/src/demangle.rs323
-rw-r--r--rust/src/disassembly.rs1208
-rw-r--r--rust/src/download_provider.rs (renamed from rust/src/downloadprovider.rs)7
-rw-r--r--rust/src/enterprise.rs84
-rw-r--r--rust/src/external_library.rs224
-rw-r--r--rust/src/externallibrary.rs209
-rw-r--r--rust/src/ffi.rs9
-rw-r--r--rust/src/file_accessor.rs (renamed from rust/src/fileaccessor.rs)2
-rw-r--r--rust/src/file_metadata.rs (renamed from rust/src/filemetadata.rs)205
-rw-r--r--rust/src/flowgraph.rs145
-rw-r--r--rust/src/function.rs1169
-rw-r--r--rust/src/function_recognizer.rs (renamed from rust/src/functionrecognizer.rs)47
-rw-r--r--rust/src/headless.rs335
-rw-r--r--rust/src/high_level_il.rs (renamed from rust/src/hlil/mod.rs)1
-rw-r--r--rust/src/high_level_il/block.rs (renamed from rust/src/hlil/block.rs)29
-rw-r--r--rust/src/high_level_il/function.rs293
-rw-r--r--rust/src/high_level_il/instruction.rs (renamed from rust/src/hlil/instruction.rs)421
-rw-r--r--rust/src/high_level_il/lift.rs (renamed from rust/src/hlil/lift.rs)11
-rw-r--r--rust/src/high_level_il/operation.rs (renamed from rust/src/hlil/operation.rs)106
-rw-r--r--rust/src/hlil/function.rs152
-rw-r--r--rust/src/interaction.rs14
-rw-r--r--rust/src/lib.rs660
-rw-r--r--rust/src/linear_view.rs (renamed from rust/src/linearview.rs)157
-rw-r--r--rust/src/llil/block.rs103
-rw-r--r--rust/src/llil/expression.rs851
-rw-r--r--rust/src/llil/instruction.rs209
-rw-r--r--rust/src/llil/mod.rs95
-rw-r--r--rust/src/llil/operation.rs825
-rw-r--r--rust/src/logger.rs64
-rw-r--r--rust/src/low_level_il.rs99
-rw-r--r--rust/src/low_level_il/block.rs108
-rw-r--r--rust/src/low_level_il/expression.rs715
-rw-r--r--rust/src/low_level_il/function.rs (renamed from rust/src/llil/function.rs)200
-rw-r--r--rust/src/low_level_il/instruction.rs348
-rw-r--r--rust/src/low_level_il/lifting.rs (renamed from rust/src/llil/lifting.rs)857
-rw-r--r--rust/src/low_level_il/operation.rs1367
-rw-r--r--rust/src/main_thread.rs129
-rw-r--r--rust/src/medium_level_il.rs (renamed from rust/src/mlil/mod.rs)4
-rw-r--r--rust/src/medium_level_il/block.rs (renamed from rust/src/mlil/block.rs)71
-rw-r--r--rust/src/medium_level_il/function.rs718
-rw-r--r--rust/src/medium_level_il/instruction.rs (renamed from rust/src/mlil/instruction.rs)740
-rw-r--r--rust/src/medium_level_il/lift.rs (renamed from rust/src/mlil/lift.rs)18
-rw-r--r--rust/src/medium_level_il/operation.rs (renamed from rust/src/mlil/operation.rs)92
-rw-r--r--rust/src/metadata.rs8
-rw-r--r--rust/src/mlil/function.rs727
-rw-r--r--rust/src/operand_iter.rs69
-rw-r--r--rust/src/platform.rs271
-rw-r--r--rust/src/progress.rs271
-rw-r--r--rust/src/project.rs1142
-rw-r--r--rust/src/project/file.rs167
-rw-r--r--rust/src/project/folder.rs165
-rw-r--r--rust/src/rc.rs57
-rw-r--r--rust/src/references.rs98
-rw-r--r--rust/src/relocation.rs35
-rw-r--r--rust/src/section.rs36
-rw-r--r--rust/src/segment.rs33
-rw-r--r--rust/src/settings.rs469
-rw-r--r--rust/src/string.rs92
-rw-r--r--rust/src/symbol.rs20
-rw-r--r--rust/src/tags.rs110
-rw-r--r--rust/src/template_simplifier.rs (renamed from rust/src/templatesimplifier.rs)2
-rw-r--r--rust/src/type_archive.rs1231
-rw-r--r--rust/src/type_container.rs439
-rw-r--r--rust/src/type_library.rs (renamed from rust/src/typelibrary.rs)45
-rw-r--r--rust/src/type_parser.rs697
-rw-r--r--rust/src/type_printer.rs981
-rw-r--r--rust/src/typearchive.rs947
-rw-r--r--rust/src/types.rs3300
-rw-r--r--rust/src/update.rs274
-rw-r--r--rust/src/variable.rs910
-rw-r--r--rust/src/worker_thread.rs71
-rw-r--r--rust/src/workflow.rs237
104 files changed, 24142 insertions, 13613 deletions
diff --git a/rust/src/architecture.rs b/rust/src/architecture.rs
index 027af77f..09808b39 100644
--- a/rust/src/architecture.rs
+++ b/rust/src/architecture.rs
@@ -17,34 +17,81 @@
// container abstraction to avoid Vec<> (want CoreArchFlagList, CoreArchRegList)
// RegisterInfo purge
use binaryninjacore_sys::*;
-
-use std::{
- borrow::{Borrow, Cow},
- collections::HashMap,
- ffi::{c_char, c_int, CStr, CString},
- hash::Hash,
- mem::{zeroed, MaybeUninit},
- ops, ptr, slice,
-};
+use std::fmt::{Debug, Formatter};
use crate::{
- callingconvention::CallingConvention,
- databuffer::DataBuffer,
+ calling_convention::CoreCallingConvention,
+ data_buffer::DataBuffer,
disassembly::InstructionTextToken,
- llil::{
- get_default_flag_cond_llil, get_default_flag_write_llil, FlagWriteOp, LiftedExpr, Lifter,
- },
+ low_level_il::{MutableLiftedILExpr, MutableLiftedILFunction},
platform::Platform,
rc::*,
relocation::CoreRelocationHandler,
string::BnStrCompatible,
string::*,
- types::{Conf, NameAndType, Type},
- {BranchType, Endianness},
+ types::{NameAndType, Type},
+ Endianness,
+};
+use std::ops::Deref;
+use std::{
+ borrow::{Borrow, Cow},
+ collections::HashMap,
+ ffi::{c_char, c_int, c_void, CStr, CString},
+ fmt::Display,
+ hash::Hash,
+ mem::MaybeUninit,
};
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
-pub enum BranchInfo {
+use crate::function_recognizer::FunctionRecognizer;
+use crate::relocation::{CustomRelocationHandlerHandle, RelocationHandler};
+
+use crate::confidence::Conf;
+use crate::low_level_il::expression::ValueExpr;
+use crate::low_level_il::lifting::{
+ get_default_flag_cond_llil, get_default_flag_write_llil, LowLevelILFlagWriteOp,
+};
+pub use binaryninjacore_sys::BNFlagRole as FlagRole;
+pub use binaryninjacore_sys::BNImplicitRegisterExtend as ImplicitRegisterExtend;
+pub use binaryninjacore_sys::BNLowLevelILFlagCondition as FlagCondition;
+
+macro_rules! newtype {
+ ($name:ident, $inner_type:ty) => {
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
+ pub struct $name(pub $inner_type);
+
+ impl From<$inner_type> for $name {
+ fn from(value: $inner_type) -> Self {
+ Self(value)
+ }
+ }
+
+ impl From<$name> for $inner_type {
+ fn from(value: $name) -> Self {
+ value.0
+ }
+ }
+
+ impl Display for $name {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self.0)
+ }
+ }
+ };
+}
+
+newtype!(RegisterId, u32);
+newtype!(RegisterStackId, u32);
+newtype!(FlagId, u32);
+// TODO: Make this NonZero<u32>?
+newtype!(FlagWriteId, u32);
+newtype!(FlagClassId, u32);
+newtype!(FlagGroupId, u32);
+newtype!(IntrinsicId, u32);
+
+#[derive(Default, Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub enum BranchKind {
+ #[default]
+ Unresolved,
Unconditional(u64),
False(u64),
True(u64),
@@ -53,134 +100,180 @@ pub enum BranchInfo {
SystemCall,
Indirect,
Exception,
- Unresolved,
UserDefined,
}
-pub struct BranchIter<'a>(&'a InstructionInfo, ops::Range<usize>);
-impl<'a> Iterator for BranchIter<'a> {
- type Item = (BranchInfo, Option<CoreArchitecture>);
-
- fn next(&mut self) -> Option<Self::Item> {
- use crate::BranchType::*;
+#[derive(Default, Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub struct BranchInfo {
+ /// If `None` the target architecture is the same as the branch instruction.
+ pub arch: Option<CoreArchitecture>,
+ pub kind: BranchKind,
+}
- match self.1.next() {
- Some(i) => {
- let target = (self.0).0.branchTarget[i];
- let arch = (self.0).0.branchArch[i];
- let arch = if arch.is_null() {
- None
- } else {
- Some(CoreArchitecture(arch))
- };
+impl BranchInfo {
+ /// Branches to an instruction with the current architecture.
+ pub fn new(kind: BranchKind) -> Self {
+ Self { arch: None, kind }
+ }
- let res = match (self.0).0.branchType[i] {
- UnconditionalBranch => BranchInfo::Unconditional(target),
- FalseBranch => BranchInfo::False(target),
- TrueBranch => BranchInfo::True(target),
- CallDestination => BranchInfo::Call(target),
- FunctionReturn => BranchInfo::FunctionReturn,
- SystemCall => BranchInfo::SystemCall,
- IndirectBranch => BranchInfo::Indirect,
- ExceptionBranch => BranchInfo::Exception,
- UnresolvedBranch => BranchInfo::Unresolved,
- UserDefinedBranch => BranchInfo::UserDefined,
- };
+ /// Branches to an instruction with an explicit architecture.
+ ///
+ /// Use this if your architecture can transition to another architecture with a branch.
+ pub fn new_with_arch(kind: BranchKind, arch: CoreArchitecture) -> Self {
+ Self {
+ arch: Some(arch),
+ kind,
+ }
+ }
- Some((res, arch))
- }
+ pub fn target(&self) -> Option<u64> {
+ match self.kind {
+ BranchKind::Unconditional(target) => Some(target),
+ BranchKind::False(target) => Some(target),
+ BranchKind::True(target) => Some(target),
+ BranchKind::Call(target) => Some(target),
_ => None,
}
}
}
-#[repr(C)]
-pub struct InstructionInfo(BNInstructionInfo);
-impl InstructionInfo {
- pub fn new(len: usize, delay_slots: u8) -> Self {
- InstructionInfo(BNInstructionInfo {
- length: len,
- archTransitionByTargetAddr: false,
- delaySlots: delay_slots,
- branchCount: 0usize,
- branchType: [BranchType::UnresolvedBranch; 3],
- branchTarget: [0u64; 3],
- branchArch: [ptr::null_mut(); 3],
- })
+impl From<BranchInfo> for BNBranchType {
+ fn from(value: BranchInfo) -> Self {
+ match value.kind {
+ BranchKind::Unresolved => BNBranchType::UnresolvedBranch,
+ BranchKind::Unconditional(_) => BNBranchType::UnconditionalBranch,
+ BranchKind::False(_) => BNBranchType::FalseBranch,
+ BranchKind::True(_) => BNBranchType::TrueBranch,
+ BranchKind::Call(_) => BNBranchType::CallDestination,
+ BranchKind::FunctionReturn => BNBranchType::FunctionReturn,
+ BranchKind::SystemCall => BNBranchType::SystemCall,
+ BranchKind::Indirect => BNBranchType::IndirectBranch,
+ BranchKind::Exception => BNBranchType::ExceptionBranch,
+ BranchKind::UserDefined => BNBranchType::UserDefinedBranch,
+ }
}
+}
- pub fn len(&self) -> usize {
- self.0.length
+impl From<BranchKind> for BranchInfo {
+ fn from(value: BranchKind) -> Self {
+ Self {
+ arch: None,
+ kind: value,
+ }
}
+}
- pub fn is_empty(&self) -> bool {
- self.0.length == 0
- }
+/// This is the number of branches that can be specified in an [`InstructionInfo`].
+pub const NUM_BRANCH_INFO: usize = 3;
- pub fn branch_count(&self) -> usize {
- self.0.branchCount
- }
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub struct InstructionInfo {
+ pub length: usize,
+ // TODO: This field name is really long...
+ pub arch_transition_by_target_addr: bool,
+ pub delay_slots: u8,
+ pub branches: [Option<BranchInfo>; NUM_BRANCH_INFO],
+}
- pub fn delay_slots(&self) -> u8 {
- self.0.delaySlots
+impl InstructionInfo {
+ // TODO: `new_with_delay_slot`?
+ pub fn new(length: usize, delay_slots: u8) -> Self {
+ Self {
+ length,
+ arch_transition_by_target_addr: false,
+ delay_slots,
+ branches: Default::default(),
+ }
}
- pub fn branches(&self) -> BranchIter {
- BranchIter(self, 0..self.branch_count())
+ pub fn add_branch(&mut self, branch_info: impl Into<BranchInfo>) {
+ // Will go through each slot and attempt to add the branch info.
+ // TODO: Return a result with BranchInfoSlotsFilled error.
+ for branch in &mut self.branches {
+ if branch.is_none() {
+ *branch = Some(branch_info.into());
+ return;
+ }
+ }
}
+}
- pub fn allow_arch_transition_by_target_addr(&mut self, transition: bool) {
- self.0.archTransitionByTargetAddr = transition;
+impl From<BNInstructionInfo> for InstructionInfo {
+ fn from(value: BNInstructionInfo) -> Self {
+ // TODO: This is quite ugly, but we destructure the branch info so this will have to do.
+ let mut branch_info = [None; NUM_BRANCH_INFO];
+ #[allow(clippy::needless_range_loop)]
+ for i in 0..value.branchCount.min(NUM_BRANCH_INFO) {
+ let branch_target = value.branchTarget[i];
+ branch_info[i] = Some(BranchInfo {
+ kind: match value.branchType[i] {
+ BNBranchType::UnconditionalBranch => BranchKind::Unconditional(branch_target),
+ BNBranchType::FalseBranch => BranchKind::False(branch_target),
+ BNBranchType::TrueBranch => BranchKind::True(branch_target),
+ BNBranchType::CallDestination => BranchKind::Call(branch_target),
+ BNBranchType::FunctionReturn => BranchKind::FunctionReturn,
+ BNBranchType::SystemCall => BranchKind::SystemCall,
+ BNBranchType::IndirectBranch => BranchKind::Indirect,
+ BNBranchType::ExceptionBranch => BranchKind::Exception,
+ BNBranchType::UnresolvedBranch => BranchKind::Unresolved,
+ BNBranchType::UserDefinedBranch => BranchKind::UserDefined,
+ },
+ arch: if value.branchArch[i].is_null() {
+ None
+ } else {
+ Some(unsafe { CoreArchitecture::from_raw(value.branchArch[i]) })
+ },
+ });
+ }
+ Self {
+ length: value.length,
+ arch_transition_by_target_addr: value.archTransitionByTargetAddr,
+ delay_slots: value.delaySlots,
+ branches: branch_info,
+ }
}
+}
- pub fn add_branch(&mut self, branch: BranchInfo, arch: Option<CoreArchitecture>) {
- if self.0.branchCount < self.0.branchType.len() {
- let idx = self.0.branchCount;
-
- let ty = match branch {
- BranchInfo::Unconditional(t) => {
- self.0.branchTarget[idx] = t;
- BranchType::UnconditionalBranch
- }
- BranchInfo::False(t) => {
- self.0.branchTarget[idx] = t;
- BranchType::FalseBranch
- }
- BranchInfo::True(t) => {
- self.0.branchTarget[idx] = t;
- BranchType::TrueBranch
- }
- BranchInfo::Call(t) => {
- self.0.branchTarget[idx] = t;
- BranchType::CallDestination
- }
- BranchInfo::FunctionReturn => BranchType::FunctionReturn,
- BranchInfo::SystemCall => BranchType::SystemCall,
- BranchInfo::Indirect => BranchType::IndirectBranch,
- BranchInfo::Exception => BranchType::ExceptionBranch,
- BranchInfo::Unresolved => BranchType::UnresolvedBranch,
- BranchInfo::UserDefined => BranchType::UserDefinedBranch,
- };
-
- self.0.branchType[idx] = ty;
- self.0.branchArch[idx] = match arch {
- Some(a) => a.0,
- _ => ptr::null_mut(),
- };
-
- self.0.branchCount += 1;
- } else {
- error!("Attempt to branch to instruction with no additional branch space!");
+impl From<InstructionInfo> for BNInstructionInfo {
+ fn from(value: InstructionInfo) -> Self {
+ let branch_count = value.branches.into_iter().filter(Option::is_some).count();
+ // TODO: This is quite ugly, but we destructure the branch info so this will have to do.
+ let branch_info_0 = value.branches[0].unwrap_or_default();
+ let branch_info_1 = value.branches[1].unwrap_or_default();
+ let branch_info_2 = value.branches[2].unwrap_or_default();
+ Self {
+ length: value.length,
+ branchCount: branch_count,
+ archTransitionByTargetAddr: value.arch_transition_by_target_addr,
+ delaySlots: value.delay_slots,
+ branchType: [
+ branch_info_0.into(),
+ branch_info_1.into(),
+ branch_info_2.into(),
+ ],
+ branchTarget: [
+ branch_info_0.target().unwrap_or_default(),
+ branch_info_1.target().unwrap_or_default(),
+ branch_info_2.target().unwrap_or_default(),
+ ],
+ branchArch: [
+ branch_info_0
+ .arch
+ .map(|a| a.handle)
+ .unwrap_or(std::ptr::null_mut()),
+ branch_info_1
+ .arch
+ .map(|a| a.handle)
+ .unwrap_or(std::ptr::null_mut()),
+ branch_info_2
+ .arch
+ .map(|a| a.handle)
+ .unwrap_or(std::ptr::null_mut()),
+ ],
}
}
}
-use crate::functionrecognizer::FunctionRecognizer;
-use crate::relocation::{CustomRelocationHandlerHandle, RelocationHandler};
-pub use binaryninjacore_sys::BNFlagRole as FlagRole;
-pub use binaryninjacore_sys::BNImplicitRegisterExtend as ImplicitRegisterExtend;
-pub use binaryninjacore_sys::BNLowLevelILFlagCondition as FlagCondition;
-
pub trait RegisterInfo: Sized {
type RegType: Register<InfoType = Self>;
@@ -190,7 +283,7 @@ pub trait RegisterInfo: Sized {
fn implicit_extend(&self) -> ImplicitRegisterExtend;
}
-pub trait Register: Sized + Clone + Copy + Hash + Eq {
+pub trait Register: Debug + Sized + Clone + Copy + Hash + Eq {
type InfoType: RegisterInfo<RegType = Self>;
fn name(&self) -> Cow<str>;
@@ -199,7 +292,7 @@ pub trait Register: Sized + Clone + Copy + Hash + Eq {
/// Unique identifier for this `Register`.
///
/// *MUST* be in the range [0, 0x7fff_ffff]
- fn id(&self) -> u32;
+ fn id(&self) -> RegisterId;
}
pub trait RegisterStackInfo: Sized {
@@ -207,8 +300,8 @@ pub trait RegisterStackInfo: Sized {
type RegType: Register<InfoType = Self::RegInfoType>;
type RegInfoType: RegisterInfo<RegType = Self::RegType>;
- fn storage_regs(&self) -> (Self::RegType, u32);
- fn top_relative_regs(&self) -> Option<(Self::RegType, u32)>;
+ fn storage_regs(&self) -> (Self::RegType, usize);
+ fn top_relative_regs(&self) -> Option<(Self::RegType, usize)>;
fn stack_top_reg(&self) -> Self::RegType;
}
@@ -227,10 +320,10 @@ pub trait RegisterStack: Sized + Clone + Copy {
/// Unique identifier for this `RegisterStack`.
///
/// *MUST* be in the range [0, 0x7fff_ffff]
- fn id(&self) -> u32;
+ fn id(&self) -> RegisterStackId;
}
-pub trait Flag: Sized + Clone + Copy + Hash + Eq {
+pub trait Flag: Debug + Sized + Clone + Copy + Hash + Eq {
type FlagClass: FlagClass;
fn name(&self) -> Cow<str>;
@@ -239,7 +332,7 @@ pub trait Flag: Sized + Clone + Copy + Hash + Eq {
/// Unique identifier for this `Flag`.
///
/// *MUST* be in the range [0, 0x7fff_ffff]
- fn id(&self) -> u32;
+ fn id(&self) -> FlagId;
}
pub trait FlagWrite: Sized + Clone + Copy {
@@ -253,7 +346,7 @@ pub trait FlagWrite: Sized + Clone + Copy {
///
/// *MUST NOT* be 0.
/// *MUST* be in the range [1, 0x7fff_ffff]
- fn id(&self) -> u32;
+ fn id(&self) -> FlagWriteId;
fn flags_written(&self) -> Vec<Self::FlagType>;
}
@@ -265,10 +358,10 @@ pub trait FlagClass: Sized + Clone + Copy + Hash + Eq {
///
/// *MUST NOT* be 0.
/// *MUST* be in the range [1, 0x7fff_ffff]
- fn id(&self) -> u32;
+ fn id(&self) -> FlagClassId;
}
-pub trait FlagGroup: Sized + Clone + Copy {
+pub trait FlagGroup: Debug + Sized + Clone + Copy {
type FlagType: Flag;
type FlagClass: FlagClass;
@@ -277,7 +370,7 @@ pub trait FlagGroup: Sized + Clone + Copy {
/// Unique identifier for this `FlagGroup`.
///
/// *MUST* be in the range [0, 0x7fff_ffff]
- fn id(&self) -> u32;
+ fn id(&self) -> FlagGroupId;
/// Returns the list of flags that need to be resolved in order
/// to take the clean flag resolution path -- at time of writing,
@@ -306,16 +399,22 @@ pub trait FlagGroup: Sized + Clone + Copy {
fn flag_conditions(&self) -> HashMap<Self::FlagClass, FlagCondition>;
}
-pub trait Intrinsic: Sized + Clone + Copy {
+pub trait Intrinsic: Debug + Sized + Clone + Copy {
fn name(&self) -> Cow<str>;
/// Unique identifier for this `Intrinsic`.
- fn id(&self) -> u32;
+ fn id(&self) -> IntrinsicId;
+
+ /// The intrinsic class for this `Intrinsic`.
+ fn class(&self) -> BNIntrinsicClass {
+ BNIntrinsicClass::GeneralIntrinsicClass
+ }
- /// Reeturns the list of the input names and types for this intrinsic.
- fn inputs(&self) -> Vec<Ref<NameAndType>>;
+ // TODO: Maybe just return `(String, Conf<Ref<Type>>)`?
+ /// List of the input names and types for this intrinsic.
+ fn inputs(&self) -> Vec<NameAndType>;
- /// Returns the list of the output types for this intrinsic.
+ /// List of the output types for this intrinsic.
fn outputs(&self) -> Vec<Conf<Ref<Type>>>;
}
@@ -349,7 +448,7 @@ pub trait Architecture: 'static + Sized + AsRef<CoreArchitecture> {
fn max_instr_len(&self) -> usize;
fn opcode_display_len(&self) -> usize;
- fn associated_arch_by_addr(&self, addr: &mut u64) -> CoreArchitecture;
+ fn associated_arch_by_addr(&self, addr: u64) -> CoreArchitecture;
fn instruction_info(&self, data: &[u8], addr: u64) -> Option<InstructionInfo>;
fn instruction_text(
@@ -361,7 +460,7 @@ pub trait Architecture: 'static + Sized + AsRef<CoreArchitecture> {
&self,
data: &[u8],
addr: u64,
- il: &mut Lifter<Self>,
+ il: &mut MutableLiftedILFunction<Self>,
) -> Option<(usize, bool)>;
/// Fallback flag value calculation path. This method is invoked when the core is unable to
@@ -377,9 +476,9 @@ pub trait Architecture: 'static + Sized + AsRef<CoreArchitecture> {
&self,
flag: Self::Flag,
flag_write_type: Self::FlagWrite,
- op: FlagWriteOp<Self::Register>,
- il: &'a mut Lifter<Self>,
- ) -> Option<LiftedExpr<'a, Self>> {
+ op: LowLevelILFlagWriteOp<Self::Register>,
+ il: &'a mut MutableLiftedILFunction<Self>,
+ ) -> Option<MutableLiftedILExpr<'a, Self, ValueExpr>> {
let role = flag.role(flag_write_type.class());
Some(get_default_flag_write_llil(self, role, op, il))
}
@@ -407,8 +506,8 @@ pub trait Architecture: 'static + Sized + AsRef<CoreArchitecture> {
&self,
cond: FlagCondition,
class: Option<Self::FlagClass>,
- il: &'a mut Lifter<Self>,
- ) -> Option<LiftedExpr<'a, Self>> {
+ il: &'a mut MutableLiftedILFunction<Self>,
+ ) -> Option<MutableLiftedILExpr<'a, Self, ValueExpr>> {
Some(get_default_flag_cond_llil(self, cond, class, il))
}
@@ -429,8 +528,8 @@ pub trait Architecture: 'static + Sized + AsRef<CoreArchitecture> {
fn flag_group_llil<'a>(
&self,
_group: Self::FlagGroup,
- _il: &'a mut Lifter<Self>,
- ) -> Option<LiftedExpr<'a, Self>> {
+ _il: &'a mut MutableLiftedILFunction<Self>,
+ ) -> Option<MutableLiftedILExpr<'a, Self, ValueExpr>> {
None
}
@@ -465,32 +564,32 @@ pub trait Architecture: 'static + Sized + AsRef<CoreArchitecture> {
None
}
- fn register_from_id(&self, id: u32) -> Option<Self::Register>;
+ fn register_from_id(&self, id: RegisterId) -> Option<Self::Register>;
- fn register_stack_from_id(&self, _id: u32) -> Option<Self::RegisterStack> {
+ fn register_stack_from_id(&self, _id: RegisterStackId) -> Option<Self::RegisterStack> {
None
}
- fn flag_from_id(&self, _id: u32) -> Option<Self::Flag> {
+ fn flag_from_id(&self, _id: FlagId) -> Option<Self::Flag> {
None
}
- fn flag_write_from_id(&self, _id: u32) -> Option<Self::FlagWrite> {
+ fn flag_write_from_id(&self, _id: FlagWriteId) -> Option<Self::FlagWrite> {
None
}
- fn flag_class_from_id(&self, _id: u32) -> Option<Self::FlagClass> {
+ fn flag_class_from_id(&self, _id: FlagClassId) -> Option<Self::FlagClass> {
None
}
- fn flag_group_from_id(&self, _id: u32) -> Option<Self::FlagGroup> {
+ fn flag_group_from_id(&self, _id: FlagGroupId) -> Option<Self::FlagGroup> {
None
}
fn intrinsics(&self) -> Vec<Self::Intrinsic> {
Vec::new()
}
- fn intrinsic_class(&self, _id: u32) -> binaryninjacore_sys::BNIntrinsicClass {
- binaryninjacore_sys::BNIntrinsicClass::GeneralIntrinsicClass
+ fn intrinsic_class(&self, _id: IntrinsicId) -> BNIntrinsicClass {
+ BNIntrinsicClass::GeneralIntrinsicClass
}
- fn intrinsic_from_id(&self, _id: u32) -> Option<Self::Intrinsic> {
+ fn intrinsic_from_id(&self, _id: IntrinsicId) -> Option<Self::Intrinsic> {
None
}
@@ -552,10 +651,10 @@ impl<R: Register> RegisterStackInfo for UnusedRegisterStackInfo<R> {
type RegType = R;
type RegInfoType = R::InfoType;
- fn storage_regs(&self) -> (Self::RegType, u32) {
+ fn storage_regs(&self) -> (Self::RegType, usize) {
unreachable!()
}
- fn top_relative_regs(&self) -> Option<(Self::RegType, u32)> {
+ fn top_relative_regs(&self) -> Option<(Self::RegType, usize)> {
unreachable!()
}
fn stack_top_reg(&self) -> Self::RegType {
@@ -571,7 +670,7 @@ impl<R: Register> RegisterStack for UnusedRegisterStack<R> {
fn name(&self) -> Cow<str> {
unreachable!()
}
- fn id(&self) -> u32 {
+ fn id(&self) -> RegisterStackId {
unreachable!()
}
fn info(&self) -> Self::InfoType {
@@ -580,7 +679,7 @@ impl<R: Register> RegisterStack for UnusedRegisterStack<R> {
}
/// Type for architrectures that do not use flags. Will panic if accessed as a flag.
-#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct UnusedFlag;
impl Flag for UnusedFlag {
@@ -591,7 +690,7 @@ impl Flag for UnusedFlag {
fn role(&self, _class: Option<Self::FlagClass>) -> FlagRole {
unreachable!()
}
- fn id(&self) -> u32 {
+ fn id(&self) -> FlagId {
unreachable!()
}
}
@@ -605,7 +704,7 @@ impl FlagWrite for UnusedFlag {
fn class(&self) -> Option<Self> {
unreachable!()
}
- fn id(&self) -> u32 {
+ fn id(&self) -> FlagWriteId {
unreachable!()
}
fn flags_written(&self) -> Vec<Self::FlagType> {
@@ -617,7 +716,7 @@ impl FlagClass for UnusedFlag {
fn name(&self) -> Cow<str> {
unreachable!()
}
- fn id(&self) -> u32 {
+ fn id(&self) -> FlagClassId {
unreachable!()
}
}
@@ -628,7 +727,7 @@ impl FlagGroup for UnusedFlag {
fn name(&self) -> Cow<str> {
unreachable!()
}
- fn id(&self) -> u32 {
+ fn id(&self) -> FlagGroupId {
unreachable!()
}
fn flags_required(&self) -> Vec<Self::FlagType> {
@@ -640,17 +739,17 @@ impl FlagGroup for UnusedFlag {
}
/// Type for architrectures that do not use intrinsics. Will panic if accessed as an intrinsic.
-#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct UnusedIntrinsic;
impl Intrinsic for UnusedIntrinsic {
fn name(&self) -> Cow<str> {
unreachable!()
}
- fn id(&self) -> u32 {
+ fn id(&self) -> IntrinsicId {
unreachable!()
}
- fn inputs(&self) -> Vec<Ref<NameAndType>> {
+ fn inputs(&self) -> Vec<NameAndType> {
unreachable!()
}
fn outputs(&self) -> Vec<Conf<Ref<Type>>> {
@@ -658,39 +757,77 @@ impl Intrinsic for UnusedIntrinsic {
}
}
-pub struct CoreRegisterInfo(*mut BNArchitecture, u32, BNRegisterInfo);
+#[derive(Debug, Copy, Clone)]
+pub struct CoreRegisterInfo {
+ arch: CoreArchitecture,
+ id: RegisterId,
+ info: BNRegisterInfo,
+}
+
+impl CoreRegisterInfo {
+ pub fn new(arch: CoreArchitecture, id: RegisterId, info: BNRegisterInfo) -> Self {
+ Self { arch, id, info }
+ }
+}
+
impl RegisterInfo for CoreRegisterInfo {
type RegType = CoreRegister;
fn parent(&self) -> Option<CoreRegister> {
- if self.1 != self.2.fullWidthRegister {
- Some(CoreRegister(self.0, self.2.fullWidthRegister))
+ if self.id != RegisterId::from(self.info.fullWidthRegister) {
+ Some(CoreRegister::new(
+ self.arch,
+ RegisterId::from(self.info.fullWidthRegister),
+ )?)
} else {
None
}
}
fn size(&self) -> usize {
- self.2.size
+ self.info.size
}
fn offset(&self) -> usize {
- self.2.offset
+ self.info.offset
}
fn implicit_extend(&self) -> ImplicitRegisterExtend {
- self.2.extend
+ self.info.extend
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
-pub struct CoreRegister(*mut BNArchitecture, u32);
+pub struct CoreRegister {
+ arch: CoreArchitecture,
+ id: RegisterId,
+}
+
+impl CoreRegister {
+ pub fn new(arch: CoreArchitecture, id: RegisterId) -> Option<Self> {
+ let register = Self { arch, id };
+ register.is_valid().then_some(register)
+ }
+
+ fn is_valid(&self) -> bool {
+ // We check the name to see if the register is actually valid.
+ let name = unsafe { BNGetArchitectureRegisterName(self.arch.handle, self.id.into()) };
+ match name.is_null() {
+ true => false,
+ false => {
+ unsafe { BNFreeString(name) };
+ true
+ }
+ }
+ }
+}
+
impl Register for CoreRegister {
type InfoType = CoreRegisterInfo;
fn name(&self) -> Cow<str> {
unsafe {
- let name = BNGetArchitectureRegisterName(self.0, self.1);
+ let name = BNGetArchitectureRegisterName(self.arch.handle, self.id.into());
// We need to guarantee ownership, as if we're still
// a Borrowed variant we're about to free the underlying
@@ -705,13 +842,21 @@ impl Register for CoreRegister {
}
fn info(&self) -> CoreRegisterInfo {
- CoreRegisterInfo(self.0, self.1, unsafe {
- BNGetArchitectureRegisterInfo(self.0, self.1)
+ CoreRegisterInfo::new(self.arch, self.id, unsafe {
+ BNGetArchitectureRegisterInfo(self.arch.handle, self.id.into())
})
}
- fn id(&self) -> u32 {
- self.1
+ fn id(&self) -> RegisterId {
+ self.id
+ }
+}
+
+impl Debug for CoreRegister {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("CoreRegister")
+ .field("id", &self.id)
+ .finish()
}
}
@@ -725,43 +870,80 @@ 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)
+ Self::new(*context, RegisterId::from(*raw)).expect("Register list contains valid registers")
}
}
-pub struct CoreRegisterStackInfo(*mut BNArchitecture, BNRegisterStackInfo);
+#[derive(Debug, Copy, Clone)]
+pub struct CoreRegisterStackInfo {
+ arch: CoreArchitecture,
+ // TODO: Wrap BNRegisterStackInfo
+ info: BNRegisterStackInfo,
+}
+
+impl CoreRegisterStackInfo {
+ pub fn new(arch: CoreArchitecture, info: BNRegisterStackInfo) -> Self {
+ Self { arch, info }
+ }
+}
impl RegisterStackInfo for CoreRegisterStackInfo {
type RegStackType = CoreRegisterStack;
type RegType = CoreRegister;
type RegInfoType = CoreRegisterInfo;
- fn storage_regs(&self) -> (Self::RegType, u32) {
+ fn storage_regs(&self) -> (Self::RegType, usize) {
(
- CoreRegister(self.0, self.1.firstStorageReg),
- self.1.storageCount,
+ CoreRegister::new(self.arch, RegisterId::from(self.info.firstStorageReg))
+ .expect("Storage register is valid"),
+ self.info.storageCount as usize,
)
}
- fn top_relative_regs(&self) -> Option<(Self::RegType, u32)> {
- if self.1.topRelativeCount == 0 {
+ fn top_relative_regs(&self) -> Option<(Self::RegType, usize)> {
+ if self.info.topRelativeCount == 0 {
None
} else {
Some((
- CoreRegister(self.0, self.1.firstTopRelativeReg),
- self.1.topRelativeCount,
+ CoreRegister::new(self.arch, RegisterId::from(self.info.firstTopRelativeReg))
+ .expect("Top relative register is valid"),
+ self.info.topRelativeCount as usize,
))
}
}
fn stack_top_reg(&self) -> Self::RegType {
- CoreRegister(self.0, self.1.stackTopReg)
+ CoreRegister::new(self.arch, RegisterId::from(self.info.stackTopReg))
+ .expect("Stack top register is valid")
}
}
-#[derive(Copy, Clone, Eq, PartialEq, Hash)]
-pub struct CoreRegisterStack(*mut BNArchitecture, u32);
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+pub struct CoreRegisterStack {
+ arch: CoreArchitecture,
+ id: RegisterStackId,
+}
+
+impl CoreRegisterStack {
+ pub fn new(arch: CoreArchitecture, id: RegisterStackId) -> Option<Self> {
+ let register_stack = Self { arch, id };
+ register_stack.is_valid().then_some(register_stack)
+ }
+
+ fn is_valid(&self) -> bool {
+ // We check the name to see if the stack register is actually valid.
+ let name = unsafe { BNGetArchitectureRegisterStackName(self.arch.handle, self.id.into()) };
+ match name.is_null() {
+ true => false,
+ false => {
+ unsafe { BNFreeString(name) };
+ true
+ }
+ }
+ }
+}
impl RegisterStack for CoreRegisterStack {
type InfoType = CoreRegisterStackInfo;
@@ -770,7 +952,7 @@ impl RegisterStack for CoreRegisterStack {
fn name(&self) -> Cow<str> {
unsafe {
- let name = BNGetArchitectureRegisterStackName(self.0, self.1);
+ let name = BNGetArchitectureRegisterStackName(self.arch.handle, self.id.into());
// We need to guarantee ownership, as if we're still
// a Borrowed variant we're about to free the underlying
@@ -785,24 +967,47 @@ impl RegisterStack for CoreRegisterStack {
}
fn info(&self) -> CoreRegisterStackInfo {
- CoreRegisterStackInfo(self.0, unsafe {
- BNGetArchitectureRegisterStackInfo(self.0, self.1)
+ CoreRegisterStackInfo::new(self.arch, unsafe {
+ BNGetArchitectureRegisterStackInfo(self.arch.handle, self.id.into())
})
}
- fn id(&self) -> u32 {
- self.1
+ fn id(&self) -> RegisterStackId {
+ self.id
+ }
+}
+
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+pub struct CoreFlag {
+ arch: CoreArchitecture,
+ id: FlagId,
+}
+
+impl CoreFlag {
+ pub fn new(arch: CoreArchitecture, id: FlagId) -> Option<Self> {
+ let flag = Self { arch, id };
+ flag.is_valid().then_some(flag)
+ }
+
+ fn is_valid(&self) -> bool {
+ // We check the name to see if the flag is actually valid.
+ let name = unsafe { BNGetArchitectureFlagName(self.arch.handle, self.id.into()) };
+ match name.is_null() {
+ true => false,
+ false => {
+ unsafe { BNFreeString(name) };
+ true
+ }
+ }
}
}
-#[derive(Copy, Clone, Eq, PartialEq, Hash)]
-pub struct CoreFlag(*mut BNArchitecture, u32);
impl Flag for CoreFlag {
type FlagClass = CoreFlagClass;
fn name(&self) -> Cow<str> {
unsafe {
- let name = BNGetArchitectureFlagName(self.0, self.1);
+ let name = BNGetArchitectureFlagName(self.arch.handle, self.id.into());
// We need to guarantee ownership, as if we're still
// a Borrowed variant we're about to free the underlying
@@ -817,28 +1022,52 @@ impl Flag for CoreFlag {
}
fn role(&self, class: Option<CoreFlagClass>) -> FlagRole {
- let class_id = match class {
- Some(class) => class.1,
- _ => 0,
- };
-
- unsafe { BNGetArchitectureFlagRole(self.0, self.1, class_id) }
+ unsafe {
+ BNGetArchitectureFlagRole(
+ self.arch.handle,
+ self.id.into(),
+ class.map(|c| c.id.0).unwrap_or(0),
+ )
+ }
}
- fn id(&self) -> u32 {
- self.1
+ fn id(&self) -> FlagId {
+ self.id
}
}
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
-pub struct CoreFlagWrite(*mut BNArchitecture, u32);
+pub struct CoreFlagWrite {
+ arch: CoreArchitecture,
+ id: FlagWriteId,
+}
+
+impl CoreFlagWrite {
+ pub fn new(arch: CoreArchitecture, id: FlagWriteId) -> Option<Self> {
+ let flag_write = Self { arch, id };
+ flag_write.is_valid().then_some(flag_write)
+ }
+
+ fn is_valid(&self) -> bool {
+ // We check the name to see if the flag write is actually valid.
+ let name = unsafe { BNGetArchitectureFlagWriteTypeName(self.arch.handle, self.id.into()) };
+ match name.is_null() {
+ true => false,
+ false => {
+ unsafe { BNFreeString(name) };
+ true
+ }
+ }
+ }
+}
+
impl FlagWrite for CoreFlagWrite {
type FlagType = CoreFlag;
type FlagClass = CoreFlagClass;
fn name(&self) -> Cow<str> {
unsafe {
- let name = BNGetArchitectureFlagWriteTypeName(self.0, self.1);
+ let name = BNGetArchitectureFlagWriteTypeName(self.arch.handle, self.id.into());
// We need to guarantee ownership, as if we're still
// a Borrowed variant we're about to free the underlying
@@ -852,20 +1081,36 @@ impl FlagWrite for CoreFlagWrite {
}
}
- fn id(&self) -> u32 {
- self.1
+ fn class(&self) -> Option<CoreFlagClass> {
+ let class = unsafe {
+ BNGetArchitectureSemanticClassForFlagWriteType(self.arch.handle, self.id.into())
+ };
+
+ match class {
+ 0 => None,
+ class_id => Some(CoreFlagClass::new(self.arch, class_id.into())?),
+ }
+ }
+
+ fn id(&self) -> FlagWriteId {
+ self.id
}
fn flags_written(&self) -> Vec<CoreFlag> {
let mut count: usize = 0;
let regs: *mut u32 = unsafe {
- BNGetArchitectureFlagsWrittenByFlagWriteType(self.0, self.1, &mut count as *mut _)
+ BNGetArchitectureFlagsWrittenByFlagWriteType(
+ self.arch.handle,
+ self.id.into(),
+ &mut count,
+ )
};
let ret = unsafe {
- slice::from_raw_parts_mut(regs, count)
+ std::slice::from_raw_parts(regs, count)
.iter()
- .map(|reg| CoreFlag(self.0, *reg))
+ .map(|id| FlagId::from(*id))
+ .filter_map(|reg| CoreFlag::new(self.arch, reg))
.collect()
};
@@ -875,23 +1120,38 @@ impl FlagWrite for CoreFlagWrite {
ret
}
+}
- fn class(&self) -> Option<CoreFlagClass> {
- let class = unsafe { BNGetArchitectureSemanticClassForFlagWriteType(self.0, self.1) };
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+pub struct CoreFlagClass {
+ arch: CoreArchitecture,
+ id: FlagClassId,
+}
- match class {
- 0 => None,
- id => Some(CoreFlagClass(self.0, id)),
+impl CoreFlagClass {
+ pub fn new(arch: CoreArchitecture, id: FlagClassId) -> Option<Self> {
+ let flag = Self { arch, id };
+ flag.is_valid().then_some(flag)
+ }
+
+ fn is_valid(&self) -> bool {
+ // We check the name to see if the flag is actually valid.
+ let name =
+ unsafe { BNGetArchitectureSemanticFlagClassName(self.arch.handle, self.id.into()) };
+ match name.is_null() {
+ true => false,
+ false => {
+ unsafe { BNFreeString(name) };
+ true
+ }
}
}
}
-#[derive(Copy, Clone, Eq, PartialEq, Hash)]
-pub struct CoreFlagClass(*mut BNArchitecture, u32);
impl FlagClass for CoreFlagClass {
fn name(&self) -> Cow<str> {
unsafe {
- let name = BNGetArchitectureSemanticFlagClassName(self.0, self.1);
+ let name = BNGetArchitectureSemanticFlagClassName(self.arch.handle, self.id.into());
// We need to guarantee ownership, as if we're still
// a Borrowed variant we're about to free the underlying
@@ -905,20 +1165,44 @@ impl FlagClass for CoreFlagClass {
}
}
- fn id(&self) -> u32 {
- self.1
+ fn id(&self) -> FlagClassId {
+ self.id
+ }
+}
+
+#[derive(Debug, Copy, Clone, Eq, PartialEq)]
+pub struct CoreFlagGroup {
+ arch: CoreArchitecture,
+ id: FlagGroupId,
+}
+
+impl CoreFlagGroup {
+ pub fn new(arch: CoreArchitecture, id: FlagGroupId) -> Option<Self> {
+ let flag_group = Self { arch, id };
+ flag_group.is_valid().then_some(flag_group)
+ }
+
+ fn is_valid(&self) -> bool {
+ // We check the name to see if the flag group is actually valid.
+ let name =
+ unsafe { BNGetArchitectureSemanticFlagGroupName(self.arch.handle, self.id.into()) };
+ match name.is_null() {
+ true => false,
+ false => {
+ unsafe { BNFreeString(name) };
+ true
+ }
+ }
}
}
-#[derive(Copy, Clone, Eq, PartialEq)]
-pub struct CoreFlagGroup(*mut BNArchitecture, u32);
impl FlagGroup for CoreFlagGroup {
type FlagType = CoreFlag;
type FlagClass = CoreFlagClass;
fn name(&self) -> Cow<str> {
unsafe {
- let name = BNGetArchitectureSemanticFlagGroupName(self.0, self.1);
+ let name = BNGetArchitectureSemanticFlagGroupName(self.arch.handle, self.id.into());
// We need to guarantee ownership, as if we're still
// a Borrowed variant we're about to free the underlying
@@ -932,20 +1216,25 @@ impl FlagGroup for CoreFlagGroup {
}
}
- fn id(&self) -> u32 {
- self.1
+ fn id(&self) -> FlagGroupId {
+ self.id
}
fn flags_required(&self) -> Vec<CoreFlag> {
let mut count: usize = 0;
let regs: *mut u32 = unsafe {
- BNGetArchitectureFlagsRequiredForSemanticFlagGroup(self.0, self.1, &mut count as *mut _)
+ BNGetArchitectureFlagsRequiredForSemanticFlagGroup(
+ self.arch.handle,
+ self.id.into(),
+ &mut count,
+ )
};
let ret = unsafe {
- slice::from_raw_parts_mut(regs, count)
+ std::slice::from_raw_parts(regs, count)
.iter()
- .map(|reg| CoreFlag(self.0, *reg))
+ .map(|id| FlagId::from(*id))
+ .filter_map(|reg| CoreFlag::new(self.arch, reg))
.collect()
};
@@ -961,18 +1250,18 @@ impl FlagGroup for CoreFlagGroup {
unsafe {
let flag_conds = BNGetArchitectureFlagConditionsForSemanticFlagGroup(
- self.0,
- self.1,
- &mut count as *mut _,
+ self.arch.handle,
+ self.id.into(),
+ &mut count,
);
- let ret = slice::from_raw_parts_mut(flag_conds, count)
+ let ret = std::slice::from_raw_parts_mut(flag_conds, count)
.iter()
- .map(|class_cond| {
- (
- CoreFlagClass(self.0, class_cond.semanticClass),
+ .filter_map(|class_cond| {
+ Some((
+ CoreFlagClass::new(self.arch, class_cond.semanticClass.into())?,
class_cond.condition,
- )
+ ))
})
.collect();
@@ -984,16 +1273,39 @@ impl FlagGroup for CoreFlagGroup {
}
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
-pub struct CoreIntrinsic(pub(crate) *mut BNArchitecture, pub(crate) u32);
+pub struct CoreIntrinsic {
+ pub arch: CoreArchitecture,
+ pub id: IntrinsicId,
+}
+
+impl CoreIntrinsic {
+ pub fn new(arch: CoreArchitecture, id: IntrinsicId) -> Option<Self> {
+ let intrinsic = Self { arch, id };
+ intrinsic.is_valid().then_some(intrinsic)
+ }
+
+ fn is_valid(&self) -> bool {
+ // We check the name to see if the intrinsic is actually valid.
+ let name = unsafe { BNGetArchitectureIntrinsicName(self.arch.handle, self.id.into()) };
+ match name.is_null() {
+ true => false,
+ false => {
+ unsafe { BNFreeString(name) };
+ true
+ }
+ }
+ }
+}
-impl Intrinsic for crate::architecture::CoreIntrinsic {
+impl Intrinsic for CoreIntrinsic {
fn name(&self) -> Cow<str> {
unsafe {
- let name = BNGetArchitectureIntrinsicName(self.0, self.1);
+ let name = BNGetArchitectureIntrinsicName(self.arch.handle, self.id.into());
// We need to guarantee ownership, as if we're still
// a Borrowed variant we're about to free the underlying
// memory.
+ // TODO: ^ the above assertion nullifies any benefit to passing back Cow tho?
let res = CStr::from_ptr(name);
let res = res.to_string_lossy().into_owned().into();
@@ -1003,19 +1315,23 @@ impl Intrinsic for crate::architecture::CoreIntrinsic {
}
}
- fn id(&self) -> u32 {
- self.1
+ fn id(&self) -> IntrinsicId {
+ self.id
}
- fn inputs(&self) -> Vec<Ref<NameAndType>> {
- let mut count: usize = 0;
+ fn class(&self) -> BNIntrinsicClass {
+ unsafe { BNGetArchitectureIntrinsicClass(self.arch.handle, self.id.into()) }
+ }
+ fn inputs(&self) -> Vec<NameAndType> {
+ let mut count: usize = 0;
unsafe {
- let inputs = BNGetArchitectureIntrinsicInputs(self.0, self.1, &mut count as *mut _);
+ let inputs =
+ BNGetArchitectureIntrinsicInputs(self.arch.handle, self.id.into(), &mut count);
- let ret = slice::from_raw_parts_mut(inputs, count)
+ let ret = std::slice::from_raw_parts_mut(inputs, count)
.iter()
- .map(|x| NameAndType::from_raw(x).to_owned())
+ .map(NameAndType::from_raw)
.collect();
BNFreeNameAndTypeList(inputs, count);
@@ -1026,13 +1342,13 @@ impl Intrinsic for crate::architecture::CoreIntrinsic {
fn outputs(&self) -> Vec<Conf<Ref<Type>>> {
let mut count: usize = 0;
-
unsafe {
- let inputs = BNGetArchitectureIntrinsicOutputs(self.0, self.1, &mut count as *mut _);
+ let inputs =
+ BNGetArchitectureIntrinsicOutputs(self.arch.handle, self.id.into(), &mut count);
- let ret = slice::from_raw_parts_mut(inputs, count)
+ let ret = std::slice::from_raw_parts_mut(inputs, count)
.iter()
- .map(|input| (*input).into())
+ .map(Conf::<Ref<Type>>::from_raw)
.collect();
BNFreeOutputTypeList(inputs, count);
@@ -1042,12 +1358,14 @@ impl Intrinsic for crate::architecture::CoreIntrinsic {
}
}
+// TODO: WTF?!?!?!?
pub struct CoreArchitectureList(*mut *mut BNArchitecture, usize);
-impl ops::Deref for CoreArchitectureList {
+
+impl Deref for CoreArchitectureList {
type Target = [CoreArchitecture];
fn deref(&self) -> &Self::Target {
- unsafe { slice::from_raw_parts_mut(self.0 as *mut CoreArchitecture, self.1) }
+ unsafe { std::slice::from_raw_parts_mut(self.0 as *mut CoreArchitecture, self.1) }
}
}
@@ -1060,37 +1378,41 @@ impl Drop for CoreArchitectureList {
}
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
-pub struct CoreArchitecture(pub(crate) *mut BNArchitecture);
-
-unsafe impl Send for CoreArchitecture {}
-unsafe impl Sync for CoreArchitecture {}
+pub struct CoreArchitecture {
+ pub(crate) handle: *mut BNArchitecture,
+}
impl CoreArchitecture {
- pub(crate) unsafe fn from_raw(raw: *mut BNArchitecture) -> Self {
- CoreArchitecture(raw)
+ // TODO: Leave a note on architecture lifetimes. Specifically that they are never freed.
+ pub(crate) unsafe fn from_raw(handle: *mut BNArchitecture) -> Self {
+ debug_assert!(!handle.is_null());
+ CoreArchitecture { handle }
}
pub fn list_all() -> CoreArchitectureList {
let mut count: usize = 0;
- let archs = unsafe { BNGetArchitectureList(&mut count as *mut _) };
+ let archs = unsafe { BNGetArchitectureList(&mut count) };
CoreArchitectureList(archs, count)
}
pub fn by_name(name: &str) -> Option<Self> {
- let res = unsafe { BNGetArchitectureByName(name.into_bytes_with_nul().as_ptr() as *mut _) };
-
- match res.is_null() {
- false => Some(CoreArchitecture(res)),
+ let handle =
+ unsafe { BNGetArchitectureByName(name.into_bytes_with_nul().as_ptr() as *mut _) };
+ match handle.is_null() {
+ false => Some(CoreArchitecture { handle }),
true => None,
}
}
pub fn name(&self) -> BnString {
- unsafe { BnString::from_raw(BNGetArchitectureName(self.0)) }
+ unsafe { BnString::from_raw(BNGetArchitectureName(self.handle)) }
}
}
+unsafe impl Send for CoreArchitecture {}
+unsafe impl Sync for CoreArchitecture {}
+
impl AsRef<CoreArchitecture> for CoreArchitecture {
fn as_ref(&self) -> &Self {
self
@@ -1111,49 +1433,39 @@ impl Architecture for CoreArchitecture {
type Intrinsic = CoreIntrinsic;
fn endianness(&self) -> Endianness {
- unsafe { BNGetArchitectureEndianness(self.0) }
+ unsafe { BNGetArchitectureEndianness(self.handle) }
}
fn address_size(&self) -> usize {
- unsafe { BNGetArchitectureAddressSize(self.0) }
+ unsafe { BNGetArchitectureAddressSize(self.handle) }
}
fn default_integer_size(&self) -> usize {
- unsafe { BNGetArchitectureDefaultIntegerSize(self.0) }
+ unsafe { BNGetArchitectureDefaultIntegerSize(self.handle) }
}
fn instruction_alignment(&self) -> usize {
- unsafe { BNGetArchitectureInstructionAlignment(self.0) }
+ unsafe { BNGetArchitectureInstructionAlignment(self.handle) }
}
fn max_instr_len(&self) -> usize {
- unsafe { BNGetArchitectureMaxInstructionLength(self.0) }
+ unsafe { BNGetArchitectureMaxInstructionLength(self.handle) }
}
fn opcode_display_len(&self) -> usize {
- unsafe { BNGetArchitectureOpcodeDisplayLength(self.0) }
+ unsafe { BNGetArchitectureOpcodeDisplayLength(self.handle) }
}
- fn associated_arch_by_addr(&self, addr: &mut u64) -> CoreArchitecture {
- let arch = unsafe { BNGetAssociatedArchitectureByAddress(self.0, addr as *mut _) };
-
- CoreArchitecture(arch)
+ fn associated_arch_by_addr(&self, addr: u64) -> CoreArchitecture {
+ let handle = unsafe { BNGetAssociatedArchitectureByAddress(self.handle, addr as *mut _) };
+ CoreArchitecture { handle }
}
fn instruction_info(&self, data: &[u8], addr: u64) -> Option<InstructionInfo> {
- let mut info = unsafe { zeroed::<InstructionInfo>() };
- let success = unsafe {
- BNGetInstructionInfo(
- self.0,
- data.as_ptr(),
- addr,
- data.len(),
- &mut (info.0) as *mut _,
- )
- };
-
- if success {
- Some(info)
+ let mut info = BNInstructionInfo::default();
+ if unsafe { BNGetInstructionInfo(self.handle, data.as_ptr(), addr, data.len(), &mut info) }
+ {
+ Some(info.into())
} else {
None
}
@@ -1166,23 +1478,23 @@ impl Architecture for CoreArchitecture {
) -> Option<(usize, Vec<InstructionTextToken>)> {
let mut consumed = data.len();
let mut count: usize = 0;
- let mut result: *mut BNInstructionTextToken = ptr::null_mut();
+ let mut result: *mut BNInstructionTextToken = std::ptr::null_mut();
unsafe {
if BNGetInstructionText(
- self.0,
+ self.handle,
data.as_ptr(),
addr,
- &mut consumed as *mut _,
- &mut result as *mut _,
- &mut count as *mut _,
+ &mut consumed,
+ &mut result,
+ &mut count,
) {
- let vec = slice::from_raw_parts(result, count)
+ let instr_text_tokens = std::slice::from_raw_parts(result, count)
.iter()
- .map(|x| InstructionTextToken::from_raw(x).to_owned())
+ .map(InstructionTextToken::from_raw)
.collect();
BNFreeInstructionText(result, count);
- Some((consumed, vec))
+ Some((consumed, instr_text_tokens))
} else {
None
}
@@ -1193,11 +1505,17 @@ impl Architecture for CoreArchitecture {
&self,
data: &[u8],
addr: u64,
- il: &mut Lifter<Self>,
+ il: &mut MutableLiftedILFunction<Self>,
) -> Option<(usize, bool)> {
let mut size = data.len();
let success = unsafe {
- BNGetInstructionLowLevelIL(self.0, data.as_ptr(), addr, &mut size as *mut _, il.handle)
+ BNGetInstructionLowLevelIL(
+ self.handle,
+ data.as_ptr(),
+ addr,
+ &mut size as *mut _,
+ il.handle,
+ )
};
if !success {
@@ -1211,40 +1529,69 @@ impl Architecture for CoreArchitecture {
&self,
_flag: Self::Flag,
_flag_write: Self::FlagWrite,
- _op: FlagWriteOp<Self::Register>,
- _il: &'a mut Lifter<Self>,
- ) -> Option<LiftedExpr<'a, Self>> {
+ _op: LowLevelILFlagWriteOp<Self::Register>,
+ _il: &'a mut MutableLiftedILFunction<Self>,
+ ) -> Option<MutableLiftedILExpr<'a, Self, ValueExpr>> {
None
}
+ fn flags_required_for_flag_condition(
+ &self,
+ condition: FlagCondition,
+ class: Option<Self::FlagClass>,
+ ) -> Vec<Self::Flag> {
+ let class_id_raw = class.map(|c| c.id().0).unwrap_or(0);
+
+ unsafe {
+ let mut count: usize = 0;
+ let flags = BNGetArchitectureFlagsRequiredForFlagCondition(
+ self.handle,
+ condition,
+ class_id_raw,
+ &mut count,
+ );
+
+ let ret = std::slice::from_raw_parts(flags, count)
+ .iter()
+ .map(|&id| FlagId::from(id))
+ .filter_map(|flag| CoreFlag::new(*self, flag))
+ .collect();
+
+ BNFreeRegisterList(flags);
+
+ ret
+ }
+ }
+
fn flag_cond_llil<'a>(
&self,
_cond: FlagCondition,
_class: Option<Self::FlagClass>,
- _il: &'a mut Lifter<Self>,
- ) -> Option<LiftedExpr<'a, Self>> {
+ _il: &'a mut MutableLiftedILFunction<Self>,
+ ) -> Option<MutableLiftedILExpr<'a, Self, ValueExpr>> {
None
}
fn flag_group_llil<'a>(
&self,
_group: Self::FlagGroup,
- _il: &'a mut Lifter<Self>,
- ) -> Option<LiftedExpr<'a, Self>> {
+ _il: &'a mut MutableLiftedILFunction<Self>,
+ ) -> Option<MutableLiftedILExpr<'a, Self, ValueExpr>> {
None
}
fn registers_all(&self) -> Vec<CoreRegister> {
unsafe {
let mut count: usize = 0;
- let regs = BNGetAllArchitectureRegisters(self.0, &mut count as *mut _);
+ let registers_raw = BNGetAllArchitectureRegisters(self.handle, &mut count);
- let ret = slice::from_raw_parts_mut(regs, count)
+ let ret = std::slice::from_raw_parts(registers_raw, count)
.iter()
- .map(|reg| CoreRegister(self.0, *reg))
+ .map(|&id| RegisterId::from(id))
+ .filter_map(|reg| CoreRegister::new(*self, reg))
.collect();
- BNFreeRegisterList(regs);
+ BNFreeRegisterList(registers_raw);
ret
}
@@ -1253,14 +1600,15 @@ impl Architecture for CoreArchitecture {
fn registers_full_width(&self) -> Vec<CoreRegister> {
unsafe {
let mut count: usize = 0;
- let regs = BNGetFullWidthArchitectureRegisters(self.0, &mut count as *mut _);
+ let registers_raw = BNGetFullWidthArchitectureRegisters(self.handle, &mut count);
- let ret = slice::from_raw_parts_mut(regs, count)
+ let ret = std::slice::from_raw_parts(registers_raw, count)
.iter()
- .map(|reg| CoreRegister(self.0, *reg))
+ .map(|&id| RegisterId::from(id))
+ .filter_map(|reg| CoreRegister::new(*self, reg))
.collect();
- BNFreeRegisterList(regs);
+ BNFreeRegisterList(registers_raw);
ret
}
@@ -1269,14 +1617,15 @@ impl Architecture for CoreArchitecture {
fn registers_global(&self) -> Vec<CoreRegister> {
unsafe {
let mut count: usize = 0;
- let regs = BNGetArchitectureGlobalRegisters(self.0, &mut count as *mut _);
+ let registers_raw = BNGetArchitectureGlobalRegisters(self.handle, &mut count);
- let ret = slice::from_raw_parts_mut(regs, count)
+ let ret = std::slice::from_raw_parts(registers_raw, count)
.iter()
- .map(|reg| CoreRegister(self.0, *reg))
+ .map(|&id| RegisterId::from(id))
+ .filter_map(|reg| CoreRegister::new(*self, reg))
.collect();
- BNFreeRegisterList(regs);
+ BNFreeRegisterList(registers_raw);
ret
}
@@ -1285,14 +1634,15 @@ impl Architecture for CoreArchitecture {
fn registers_system(&self) -> Vec<CoreRegister> {
unsafe {
let mut count: usize = 0;
- let regs = BNGetArchitectureSystemRegisters(self.0, &mut count as *mut _);
+ let registers_raw = BNGetArchitectureSystemRegisters(self.handle, &mut count);
- let ret = slice::from_raw_parts_mut(regs, count)
+ let ret = std::slice::from_raw_parts(registers_raw, count)
.iter()
- .map(|reg| CoreRegister(self.0, *reg))
+ .map(|&id| RegisterId::from(id))
+ .filter_map(|reg| CoreRegister::new(*self, reg))
.collect();
- BNFreeRegisterList(regs);
+ BNFreeRegisterList(registers_raw);
ret
}
@@ -1301,14 +1651,15 @@ impl Architecture for CoreArchitecture {
fn register_stacks(&self) -> Vec<CoreRegisterStack> {
unsafe {
let mut count: usize = 0;
- let regs = BNGetAllArchitectureRegisterStacks(self.0, &mut count as *mut _);
+ let reg_stacks_raw = BNGetAllArchitectureRegisterStacks(self.handle, &mut count);
- let ret = slice::from_raw_parts_mut(regs, count)
+ let ret = std::slice::from_raw_parts(reg_stacks_raw, count)
.iter()
- .map(|reg| CoreRegisterStack(self.0, *reg))
+ .map(|&id| RegisterStackId::from(id))
+ .filter_map(|reg_stack| CoreRegisterStack::new(*self, reg_stack))
.collect();
- BNFreeRegisterList(regs);
+ BNFreeRegisterList(reg_stacks_raw);
ret
}
@@ -1317,14 +1668,15 @@ impl Architecture for CoreArchitecture {
fn flags(&self) -> Vec<CoreFlag> {
unsafe {
let mut count: usize = 0;
- let regs = BNGetAllArchitectureFlags(self.0, &mut count as *mut _);
+ let flags_raw = BNGetAllArchitectureFlags(self.handle, &mut count);
- let ret = slice::from_raw_parts_mut(regs, count)
+ let ret = std::slice::from_raw_parts(flags_raw, count)
.iter()
- .map(|reg| CoreFlag(self.0, *reg))
+ .map(|&id| FlagId::from(id))
+ .filter_map(|flag| CoreFlag::new(*self, flag))
.collect();
- BNFreeRegisterList(regs);
+ BNFreeRegisterList(flags_raw);
ret
}
@@ -1333,14 +1685,15 @@ impl Architecture for CoreArchitecture {
fn flag_write_types(&self) -> Vec<CoreFlagWrite> {
unsafe {
let mut count: usize = 0;
- let regs = BNGetAllArchitectureFlagWriteTypes(self.0, &mut count as *mut _);
+ let flag_writes_raw = BNGetAllArchitectureFlagWriteTypes(self.handle, &mut count);
- let ret = slice::from_raw_parts_mut(regs, count)
+ let ret = std::slice::from_raw_parts(flag_writes_raw, count)
.iter()
- .map(|reg| CoreFlagWrite(self.0, *reg))
+ .map(|&id| FlagWriteId::from(id))
+ .filter_map(|flag_write| CoreFlagWrite::new(*self, flag_write))
.collect();
- BNFreeRegisterList(regs);
+ BNFreeRegisterList(flag_writes_raw);
ret
}
@@ -1349,14 +1702,15 @@ impl Architecture for CoreArchitecture {
fn flag_classes(&self) -> Vec<CoreFlagClass> {
unsafe {
let mut count: usize = 0;
- let regs = BNGetAllArchitectureSemanticFlagClasses(self.0, &mut count as *mut _);
+ let flag_classes_raw = BNGetAllArchitectureSemanticFlagClasses(self.handle, &mut count);
- let ret = slice::from_raw_parts_mut(regs, count)
+ let ret = std::slice::from_raw_parts(flag_classes_raw, count)
.iter()
- .map(|reg| CoreFlagClass(self.0, *reg))
+ .map(|&id| FlagClassId::from(id))
+ .filter_map(|flag_class| CoreFlagClass::new(*self, flag_class))
.collect();
- BNFreeRegisterList(regs);
+ BNFreeRegisterList(flag_classes_raw);
ret
}
@@ -1365,117 +1719,81 @@ impl Architecture for CoreArchitecture {
fn flag_groups(&self) -> Vec<CoreFlagGroup> {
unsafe {
let mut count: usize = 0;
- let regs = BNGetAllArchitectureSemanticFlagGroups(self.0, &mut count as *mut _);
+ let flag_groups_raw = BNGetAllArchitectureSemanticFlagGroups(self.handle, &mut count);
- let ret = slice::from_raw_parts_mut(regs, count)
+ let ret = std::slice::from_raw_parts(flag_groups_raw, count)
.iter()
- .map(|reg| CoreFlagGroup(self.0, *reg))
+ .map(|&id| FlagGroupId::from(id))
+ .filter_map(|flag_group| CoreFlagGroup::new(*self, flag_group))
.collect();
- BNFreeRegisterList(regs);
-
- ret
- }
- }
-
- fn flags_required_for_flag_condition(
- &self,
- condition: FlagCondition,
- class: Option<Self::FlagClass>,
- ) -> Vec<Self::Flag> {
- let class_id = class.map(|c| c.id()).unwrap_or(0);
-
- unsafe {
- let mut count: usize = 0;
- let flags = BNGetArchitectureFlagsRequiredForFlagCondition(
- self.0,
- condition,
- class_id,
- &mut count as *mut _,
- );
-
- let ret = slice::from_raw_parts_mut(flags, count)
- .iter()
- .map(|flag| CoreFlag(self.0, *flag))
- .collect();
-
- BNFreeRegisterList(flags);
+ BNFreeRegisterList(flag_groups_raw);
ret
}
}
fn stack_pointer_reg(&self) -> Option<CoreRegister> {
- match unsafe { BNGetArchitectureStackPointerRegister(self.0) } {
+ match unsafe { BNGetArchitectureStackPointerRegister(self.handle) } {
0xffff_ffff => None,
- reg => Some(CoreRegister(self.0, reg)),
+ reg => Some(CoreRegister::new(*self, reg.into())?),
}
}
fn link_reg(&self) -> Option<CoreRegister> {
- match unsafe { BNGetArchitectureLinkRegister(self.0) } {
+ match unsafe { BNGetArchitectureLinkRegister(self.handle) } {
0xffff_ffff => None,
- reg => Some(CoreRegister(self.0, reg)),
+ reg => Some(CoreRegister::new(*self, reg.into())?),
}
}
- fn register_from_id(&self, id: u32) -> Option<CoreRegister> {
- // TODO validate in debug builds
- Some(CoreRegister(self.0, id))
+ fn register_from_id(&self, id: RegisterId) -> Option<CoreRegister> {
+ CoreRegister::new(*self, id)
}
- fn register_stack_from_id(&self, id: u32) -> Option<CoreRegisterStack> {
- // TODO validate in debug builds
- Some(CoreRegisterStack(self.0, id))
+ fn register_stack_from_id(&self, id: RegisterStackId) -> Option<CoreRegisterStack> {
+ CoreRegisterStack::new(*self, id)
}
- fn flag_from_id(&self, id: u32) -> Option<CoreFlag> {
- // TODO validate in debug builds
- Some(CoreFlag(self.0, id))
+ fn flag_from_id(&self, id: FlagId) -> Option<CoreFlag> {
+ CoreFlag::new(*self, id)
}
- fn flag_write_from_id(&self, id: u32) -> Option<CoreFlagWrite> {
- // TODO validate in debug builds
- Some(CoreFlagWrite(self.0, id))
+ fn flag_write_from_id(&self, id: FlagWriteId) -> Option<CoreFlagWrite> {
+ CoreFlagWrite::new(*self, id)
}
- fn flag_class_from_id(&self, id: u32) -> Option<CoreFlagClass> {
- // TODO validate in debug builds
- Some(CoreFlagClass(self.0, id))
+ fn flag_class_from_id(&self, id: FlagClassId) -> Option<CoreFlagClass> {
+ CoreFlagClass::new(*self, id)
}
- fn flag_group_from_id(&self, id: u32) -> Option<CoreFlagGroup> {
- // TODO validate in debug builds
- Some(CoreFlagGroup(self.0, id))
+ fn flag_group_from_id(&self, id: FlagGroupId) -> Option<CoreFlagGroup> {
+ CoreFlagGroup::new(*self, id)
}
fn intrinsics(&self) -> Vec<CoreIntrinsic> {
unsafe {
let mut count: usize = 0;
- let intrinsics = BNGetAllArchitectureIntrinsics(self.0, &mut count as *mut _);
+ let intrinsics_raw = BNGetAllArchitectureIntrinsics(self.handle, &mut count);
- let ret = slice::from_raw_parts_mut(intrinsics, count)
+ let intrinsics = std::slice::from_raw_parts_mut(intrinsics_raw, count)
.iter()
- .map(|reg| CoreIntrinsic(self.0, *reg))
+ .map(|&id| IntrinsicId::from(id))
+ .filter_map(|intrinsic| CoreIntrinsic::new(*self, intrinsic))
.collect();
- BNFreeRegisterList(intrinsics);
+ BNFreeRegisterList(intrinsics_raw);
- ret
+ intrinsics
}
}
- fn intrinsic_class(&self, id: u32) -> binaryninjacore_sys::BNIntrinsicClass {
- unsafe { BNGetArchitectureIntrinsicClass(self.0, id) }
- }
-
- fn intrinsic_from_id(&self, id: u32) -> Option<CoreIntrinsic> {
- // TODO validate in debug builds
- Some(CoreIntrinsic(self.0, id))
+ fn intrinsic_from_id(&self, id: IntrinsicId) -> Option<CoreIntrinsic> {
+ CoreIntrinsic::new(*self, id)
}
fn can_assemble(&self) -> bool {
- unsafe { BNCanArchitectureAssemble(self.0) }
+ unsafe { BNCanArchitectureAssemble(self.handle) }
}
fn assemble(&self, code: &str, addr: u64) -> Result<Vec<u8>, String> {
@@ -1485,10 +1803,10 @@ impl Architecture for CoreArchitecture {
Ok(result) => result,
Err(_) => return Err("Result buffer allocation failed".to_string()),
};
- let mut error_raw: *mut c_char = ptr::null_mut();
+ let mut error_raw: *mut c_char = std::ptr::null_mut();
let res = unsafe {
BNAssemble(
- self.0,
+ self.handle,
code.as_ptr(),
addr,
result.as_raw(),
@@ -1510,32 +1828,37 @@ impl Architecture for CoreArchitecture {
fn is_never_branch_patch_available(&self, data: &[u8], addr: u64) -> bool {
unsafe {
- BNIsArchitectureNeverBranchPatchAvailable(self.0, data.as_ptr(), addr, data.len())
+ BNIsArchitectureNeverBranchPatchAvailable(self.handle, data.as_ptr(), addr, data.len())
}
}
fn is_always_branch_patch_available(&self, data: &[u8], addr: u64) -> bool {
unsafe {
- BNIsArchitectureAlwaysBranchPatchAvailable(self.0, data.as_ptr(), addr, data.len())
+ BNIsArchitectureAlwaysBranchPatchAvailable(self.handle, data.as_ptr(), addr, data.len())
}
}
fn is_invert_branch_patch_available(&self, data: &[u8], addr: u64) -> bool {
unsafe {
- BNIsArchitectureInvertBranchPatchAvailable(self.0, data.as_ptr(), addr, data.len())
+ BNIsArchitectureInvertBranchPatchAvailable(self.handle, data.as_ptr(), addr, data.len())
}
}
fn is_skip_and_return_zero_patch_available(&self, data: &[u8], addr: u64) -> bool {
unsafe {
- BNIsArchitectureSkipAndReturnZeroPatchAvailable(self.0, data.as_ptr(), addr, data.len())
+ BNIsArchitectureSkipAndReturnZeroPatchAvailable(
+ self.handle,
+ data.as_ptr(),
+ addr,
+ data.len(),
+ )
}
}
fn is_skip_and_return_value_patch_available(&self, data: &[u8], addr: u64) -> bool {
unsafe {
BNIsArchitectureSkipAndReturnValuePatchAvailable(
- self.0,
+ self.handle,
data.as_ptr(),
addr,
data.len(),
@@ -1544,20 +1867,26 @@ impl Architecture for CoreArchitecture {
}
fn convert_to_nop(&self, data: &mut [u8], addr: u64) -> bool {
- unsafe { BNArchitectureConvertToNop(self.0, data.as_mut_ptr(), addr, data.len()) }
+ unsafe { BNArchitectureConvertToNop(self.handle, data.as_mut_ptr(), addr, data.len()) }
}
fn always_branch(&self, data: &mut [u8], addr: u64) -> bool {
- unsafe { BNArchitectureAlwaysBranch(self.0, data.as_mut_ptr(), addr, data.len()) }
+ unsafe { BNArchitectureAlwaysBranch(self.handle, data.as_mut_ptr(), addr, data.len()) }
}
fn invert_branch(&self, data: &mut [u8], addr: u64) -> bool {
- unsafe { BNArchitectureInvertBranch(self.0, data.as_mut_ptr(), addr, data.len()) }
+ unsafe { BNArchitectureInvertBranch(self.handle, data.as_mut_ptr(), addr, data.len()) }
}
fn skip_and_return_value(&self, data: &mut [u8], addr: u64, value: u64) -> bool {
unsafe {
- BNArchitectureSkipAndReturnValue(self.0, data.as_mut_ptr(), addr, data.len(), value)
+ BNArchitectureSkipAndReturnValue(
+ self.handle,
+ data.as_mut_ptr(),
+ addr,
+ data.len(),
+ value,
+ )
}
}
@@ -1568,30 +1897,33 @@ impl Architecture for CoreArchitecture {
macro_rules! cc_func {
($get_name:ident, $get_api:ident, $set_name:ident, $set_api:ident) => {
- fn $get_name(&self) -> Option<Ref<CallingConvention<Self>>> {
- let handle = self.as_ref();
+ fn $get_name(&self) -> Option<Ref<CoreCallingConvention>> {
+ let arch = self.as_ref();
unsafe {
- let cc = $get_api(handle.0);
+ let cc = $get_api(arch.handle);
if cc.is_null() {
None
} else {
- Some(CallingConvention::ref_from_raw(cc, self.handle()))
+ Some(CoreCallingConvention::ref_from_raw(
+ cc,
+ self.as_ref().handle(),
+ ))
}
}
}
- fn $set_name(&self, cc: &CallingConvention<Self>) {
- let handle = self.as_ref();
+ fn $set_name(&self, cc: &CoreCallingConvention) {
+ let arch = self.as_ref();
assert!(
- cc.arch_handle.borrow().as_ref().0 == handle.0,
+ cc.arch_handle.borrow().as_ref().handle == arch.handle,
"use of calling convention with non-matching architecture!"
);
unsafe {
- $set_api(handle.0, cc.handle);
+ $set_api(arch.handle, cc.handle);
}
}
};
@@ -1603,18 +1935,19 @@ pub trait ArchitectureExt: Architecture {
let name = name.into_bytes_with_nul();
match unsafe {
- BNGetArchitectureRegisterByName(self.as_ref().0, name.as_ref().as_ptr() as *mut _)
+ BNGetArchitectureRegisterByName(self.as_ref().handle, name.as_ref().as_ptr() as *mut _)
} {
0xffff_ffff => None,
- reg => self.register_from_id(reg),
+ reg => self.register_from_id(reg.into()),
}
}
- fn calling_conventions(&self) -> Array<CallingConvention<Self>> {
+ fn calling_conventions(&self) -> Array<CoreCallingConvention> {
unsafe {
let mut count = 0;
- let calling_convs = BNGetArchitectureCallingConventions(self.as_ref().0, &mut count);
- Array::new(calling_convs, count, self.handle())
+ let calling_convs =
+ BNGetArchitectureCallingConventions(self.as_ref().handle, &mut count);
+ Array::new(calling_convs, count, self.as_ref().handle())
}
}
@@ -1648,7 +1981,7 @@ pub trait ArchitectureExt: Architecture {
fn standalone_platform(&self) -> Option<Ref<Platform>> {
unsafe {
- let handle = BNGetArchitectureStandalonePlatform(self.as_ref().0);
+ let handle = BNGetArchitectureStandalonePlatform(self.as_ref().handle);
if handle.is_null() {
return None;
@@ -1665,7 +1998,8 @@ pub trait ArchitectureExt: Architecture {
};
unsafe {
- let handle = BNArchitectureGetRelocationHandler(self.as_ref().0, view_name.as_ptr());
+ let handle =
+ BNArchitectureGetRelocationHandler(self.as_ref().handle, view_name.as_ptr());
if handle.is_null() {
return None;
@@ -1692,7 +2026,7 @@ pub trait ArchitectureExt: Architecture {
where
R: 'static + FunctionRecognizer + Send + Sync + Sized,
{
- crate::functionrecognizer::register_arch_function_recognizer(self.as_ref(), recognizer);
+ crate::function_recognizer::register_arch_function_recognizer(self.as_ref(), recognizer);
}
}
@@ -1704,9 +2038,6 @@ where
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync + Sized,
F: FnOnce(CustomArchitectureHandle<A>, CoreArchitecture) -> A,
{
- use std::mem;
- use std::os::raw::{c_char, c_void};
-
#[repr(C)]
struct ArchitectureBuilder<A, F>
where
@@ -1731,7 +2062,7 @@ where
let create = custom_arch.func.take().unwrap();
custom_arch
.arch
- .write(create(custom_arch_handle, CoreArchitecture(obj)));
+ .write(create(custom_arch_handle, CoreArchitecture::from_raw(obj)));
}
}
@@ -1791,9 +2122,9 @@ where
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync,
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- let addr = unsafe { &mut *(addr) };
+ let addr = unsafe { *(addr) };
- custom_arch.associated_arch_by_addr(addr).0
+ custom_arch.associated_arch_by_addr(addr).handle
}
extern "C" fn cb_instruction_info<A>(
@@ -1807,12 +2138,12 @@ where
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync,
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- let data = unsafe { slice::from_raw_parts(data, len) };
- let result = unsafe { &mut *(result as *mut InstructionInfo) };
+ let data = unsafe { std::slice::from_raw_parts(data, len) };
match custom_arch.instruction_info(data, addr) {
Some(info) => {
- result.0 = info.0;
+ // SAFETY: Passed in to be written to
+ unsafe { *result = info.into() };
true
}
None => false,
@@ -1831,28 +2162,35 @@ where
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync,
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- let data = unsafe { slice::from_raw_parts(data, *len) };
+ let data = unsafe { std::slice::from_raw_parts(data, *len) };
let result = unsafe { &mut *result };
let Some((res_size, res_tokens)) = custom_arch.instruction_text(data, addr) else {
return false;
};
- let res_tokens: Box<[_]> = res_tokens.into_boxed_slice();
+ let res_tokens: Box<[BNInstructionTextToken]> = res_tokens
+ .into_iter()
+ .map(InstructionTextToken::into_raw)
+ .collect();
unsafe {
+ // NOTE: Freed with `cb_free_instruction_text`
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;
+ *result = res_tokens.as_mut_ptr();
+ *count = res_tokens.len();
*len = res_size;
}
true
}
extern "C" fn cb_free_instruction_text(tokens: *mut BNInstructionTextToken, count: usize) {
- let _tokens = unsafe { Box::from_raw(ptr::slice_from_raw_parts_mut(tokens, count)) };
+ unsafe {
+ let raw_tokens = std::slice::from_raw_parts_mut(tokens, count);
+ let boxed_tokens = Box::from_raw(raw_tokens);
+ for token in boxed_tokens {
+ InstructionTextToken::free_raw(token);
+ }
+ }
}
extern "C" fn cb_instruction_llil<A>(
@@ -1870,8 +2208,8 @@ where
handle: ctxt as *mut A,
};
- let data = unsafe { slice::from_raw_parts(data, *len) };
- let mut lifter = unsafe { Lifter::from_raw(custom_arch_handle, il) };
+ let data = unsafe { std::slice::from_raw_parts(data, *len) };
+ let mut lifter = unsafe { MutableLiftedILFunction::from_raw(custom_arch_handle, il) };
match custom_arch.instruction_llil(data, addr, &mut lifter) {
Some((res_len, res_value)) => {
@@ -1888,9 +2226,9 @@ where
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- match custom_arch.register_from_id(reg) {
- Some(reg) => BnString::new(reg.name().as_ref()).into_raw(),
- None => BnString::new("invalid_reg").into_raw(),
+ match custom_arch.register_from_id(reg.into()) {
+ Some(reg) => BnString::into_raw(BnString::new(reg.name().as_ref())),
+ None => BnString::into_raw(BnString::new("invalid_reg")),
}
}
@@ -1900,9 +2238,9 @@ where
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- match custom_arch.flag_from_id(flag) {
- Some(flag) => BnString::new(flag.name().as_ref()).into_raw(),
- None => BnString::new("invalid_flag").into_raw(),
+ match custom_arch.flag_from_id(flag.into()) {
+ Some(flag) => BnString::into_raw(BnString::new(flag.name().as_ref())),
+ None => BnString::into_raw(BnString::new("invalid_flag")),
}
}
@@ -1912,9 +2250,9 @@ where
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- match custom_arch.flag_write_from_id(flag_write) {
- Some(flag_write) => BnString::new(flag_write.name().as_ref()).into_raw(),
- None => BnString::new("invalid_flag_write").into_raw(),
+ match custom_arch.flag_write_from_id(flag_write.into()) {
+ Some(flag_write) => BnString::into_raw(BnString::new(flag_write.name().as_ref())),
+ None => BnString::into_raw(BnString::new("invalid_flag_write")),
}
}
@@ -1924,9 +2262,9 @@ where
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- match custom_arch.flag_class_from_id(class) {
- Some(class) => BnString::new(class.name().as_ref()).into_raw(),
- None => BnString::new("invalid_flag_class").into_raw(),
+ match custom_arch.flag_class_from_id(class.into()) {
+ Some(class) => BnString::into_raw(BnString::new(class.name().as_ref())),
+ None => BnString::into_raw(BnString::new("invalid_flag_class")),
}
}
@@ -1936,9 +2274,9 @@ where
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- match custom_arch.flag_group_from_id(group) {
- Some(group) => BnString::new(group.name().as_ref()).into_raw(),
- None => BnString::new("invalid_flag_group").into_raw(),
+ match custom_arch.flag_group_from_id(group.into()) {
+ Some(group) => BnString::into_raw(BnString::new(group.name().as_ref())),
+ None => BnString::into_raw(BnString::new("invalid_flag_group")),
}
}
@@ -1947,16 +2285,16 @@ where
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync,
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- let mut regs: Vec<_> = custom_arch
+ let mut regs: Box<[_]> = custom_arch
.registers_full_width()
.iter()
- .map(|r| r.id())
+ .map(|r| r.id().0)
.collect();
// SAFETY: `count` is an out parameter
unsafe { *count = regs.len() };
let regs_ptr = regs.as_mut_ptr();
- mem::forget(regs);
+ std::mem::forget(regs);
regs_ptr
}
@@ -1965,12 +2303,16 @@ where
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync,
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- let mut regs: Vec<_> = custom_arch.registers_all().iter().map(|r| r.id()).collect();
+ let mut regs: Box<[_]> = custom_arch
+ .registers_all()
+ .iter()
+ .map(|r| r.id().0)
+ .collect();
// SAFETY: `count` is an out parameter
unsafe { *count = regs.len() };
let regs_ptr = regs.as_mut_ptr();
- mem::forget(regs);
+ std::mem::forget(regs);
regs_ptr
}
@@ -1979,16 +2321,16 @@ where
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync,
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- let mut regs: Vec<_> = custom_arch
+ let mut regs: Box<[_]> = custom_arch
.registers_global()
.iter()
- .map(|r| r.id())
+ .map(|r| r.id().0)
.collect();
// SAFETY: `count` is an out parameter
unsafe { *count = regs.len() };
let regs_ptr = regs.as_mut_ptr();
- mem::forget(regs);
+ std::mem::forget(regs);
regs_ptr
}
@@ -1997,16 +2339,16 @@ where
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync,
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- let mut regs: Vec<_> = custom_arch
+ let mut regs: Box<[_]> = custom_arch
.registers_system()
.iter()
- .map(|r| r.id())
+ .map(|r| r.id().0)
.collect();
// SAFETY: `count` is an out parameter
unsafe { *count = regs.len() };
let regs_ptr = regs.as_mut_ptr();
- mem::forget(regs);
+ std::mem::forget(regs);
regs_ptr
}
@@ -2015,12 +2357,12 @@ where
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync,
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- let mut flags: Vec<_> = custom_arch.flags().iter().map(|f| f.id()).collect();
+ let mut flags: Box<[_]> = custom_arch.flags().iter().map(|f| f.id().0).collect();
// SAFETY: `count` is an out parameter
unsafe { *count = flags.len() };
let flags_ptr = flags.as_mut_ptr();
- mem::forget(flags);
+ std::mem::forget(flags);
flags_ptr
}
@@ -2029,16 +2371,16 @@ where
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync,
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- let mut flag_writes: Vec<_> = custom_arch
+ let mut flag_writes: Box<[_]> = custom_arch
.flag_write_types()
.iter()
- .map(|f| f.id())
+ .map(|f| f.id().0)
.collect();
// SAFETY: `count` is an out parameter
unsafe { *count = flag_writes.len() };
let flags_ptr = flag_writes.as_mut_ptr();
- mem::forget(flag_writes);
+ std::mem::forget(flag_writes);
flags_ptr
}
@@ -2047,12 +2389,16 @@ where
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync,
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- let mut flag_classes: Vec<_> = custom_arch.flag_classes().iter().map(|f| f.id()).collect();
+ let mut flag_classes: Box<[_]> = custom_arch
+ .flag_classes()
+ .iter()
+ .map(|f| f.id().0)
+ .collect();
// SAFETY: `count` is an out parameter
unsafe { *count = flag_classes.len() };
let flags_ptr = flag_classes.as_mut_ptr();
- mem::forget(flag_classes);
+ std::mem::forget(flag_classes);
flags_ptr
}
@@ -2061,12 +2407,13 @@ where
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync,
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- let mut flag_groups: Vec<_> = custom_arch.flag_groups().iter().map(|f| f.id()).collect();
+ let mut flag_groups: Box<[_]> =
+ custom_arch.flag_groups().iter().map(|f| f.id().0).collect();
// SAFETY: `count` is an out parameter
unsafe { *count = flag_groups.len() };
let flags_ptr = flag_groups.as_mut_ptr();
- mem::forget(flag_groups);
+ std::mem::forget(flag_groups);
flags_ptr
}
@@ -2077,8 +2424,8 @@ where
let custom_arch = unsafe { &*(ctxt as *mut A) };
if let (Some(flag), class) = (
- custom_arch.flag_from_id(flag),
- custom_arch.flag_class_from_id(class),
+ custom_arch.flag_from_id(FlagId(flag)),
+ custom_arch.flag_class_from_id(FlagClassId(class)),
) {
flag.role(class)
} else {
@@ -2096,17 +2443,17 @@ where
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync,
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- let class = custom_arch.flag_class_from_id(class);
- let mut flags: Vec<_> = custom_arch
+ let class = custom_arch.flag_class_from_id(FlagClassId(class));
+ let mut flags: Box<[_]> = custom_arch
.flags_required_for_flag_condition(cond, class)
.iter()
- .map(|f| f.id())
+ .map(|f| f.id().0)
.collect();
// SAFETY: `count` is an out parameter
unsafe { *count = flags.len() };
let flags_ptr = flags.as_mut_ptr();
- mem::forget(flags);
+ std::mem::forget(flags);
flags_ptr
}
@@ -2120,19 +2467,19 @@ where
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- if let Some(group) = custom_arch.flag_group_from_id(group) {
- let mut flags: Vec<_> = group.flags_required().iter().map(|f| f.id()).collect();
+ if let Some(group) = custom_arch.flag_group_from_id(FlagGroupId(group)) {
+ let mut flags: Box<[_]> = group.flags_required().iter().map(|f| f.id().0).collect();
// SAFETY: `count` is an out parameter
unsafe { *count = flags.len() };
let flags_ptr = flags.as_mut_ptr();
- mem::forget(flags);
+ std::mem::forget(flags);
flags_ptr
} else {
unsafe {
*count = 0;
}
- ptr::null_mut()
+ std::ptr::null_mut()
}
}
@@ -2146,26 +2493,26 @@ where
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- if let Some(group) = custom_arch.flag_group_from_id(group) {
+ if let Some(group) = custom_arch.flag_group_from_id(FlagGroupId(group)) {
let flag_conditions = group.flag_conditions();
- let mut flags = flag_conditions
+ let mut flags: Box<[_]> = flag_conditions
.iter()
.map(|(&class, &condition)| BNFlagConditionForSemanticClass {
- semanticClass: class.id(),
+ semanticClass: class.id().0,
condition,
})
- .collect::<Vec<_>>();
+ .collect();
// SAFETY: `count` is an out parameter
unsafe { *count = flags.len() };
let flags_ptr = flags.as_mut_ptr();
- mem::forget(flags);
+ std::mem::forget(flags);
flags_ptr
} else {
unsafe {
*count = 0;
}
- ptr::null_mut()
+ std::ptr::null_mut()
}
}
@@ -2181,7 +2528,7 @@ where
}
unsafe {
- let flags_ptr = ptr::slice_from_raw_parts_mut(conds, count);
+ let flags_ptr = std::ptr::slice_from_raw_parts_mut(conds, count);
let _flags = Box::from_raw(flags_ptr);
}
}
@@ -2196,20 +2543,23 @@ where
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- if let Some(write_type) = custom_arch.flag_write_from_id(write_type) {
- let mut flags_written: Vec<_> =
- write_type.flags_written().iter().map(|f| f.id()).collect();
+ if let Some(write_type) = custom_arch.flag_write_from_id(FlagWriteId(write_type)) {
+ let mut flags_written: Box<[_]> = write_type
+ .flags_written()
+ .iter()
+ .map(|f| f.id().0)
+ .collect();
// SAFETY: `count` is an out parameter
unsafe { *count = flags_written.len() };
let flags_ptr = flags_written.as_mut_ptr();
- mem::forget(flags_written);
+ std::mem::forget(flags_written);
flags_ptr
} else {
unsafe {
*count = 0;
}
- ptr::null_mut()
+ std::ptr::null_mut()
}
}
@@ -2222,9 +2572,9 @@ where
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
custom_arch
- .flag_write_from_id(write_type)
+ .flag_write_from_id(FlagWriteId(write_type))
.map(|w| w.class())
- .and_then(|c| c.map(|c| c.id()))
+ .and_then(|c| c.map(|c| c.id().0))
.unwrap_or(0)
}
@@ -2246,19 +2596,19 @@ where
handle: ctxt as *mut A,
};
- let flag_write = custom_arch.flag_write_from_id(flag_write);
- let flag = custom_arch.flag_from_id(flag);
- let operands = unsafe { slice::from_raw_parts(operands_raw, operand_count) };
- let mut lifter = unsafe { Lifter::from_raw(custom_arch_handle, il) };
+ let flag_write = custom_arch.flag_write_from_id(FlagWriteId(flag_write));
+ let flag = custom_arch.flag_from_id(FlagId(flag));
+ let operands = unsafe { std::slice::from_raw_parts(operands_raw, operand_count) };
+ let mut lifter = unsafe { MutableLiftedILFunction::from_raw(custom_arch_handle, il) };
if let (Some(flag_write), Some(flag)) = (flag_write, flag) {
- if let Some(op) = FlagWriteOp::from_op(custom_arch, size, op, operands) {
+ if let Some(op) = LowLevelILFlagWriteOp::from_op(custom_arch, size, op, operands) {
if let Some(expr) = custom_arch.flag_write_llil(flag, flag_write, op, &mut lifter) {
// TODO verify that returned expr is a bool value
- return expr.expr_idx;
+ return expr.index.0;
}
} else {
- warn!(
+ log::warn!(
"unable to unpack flag write op: {:?} with {} operands",
op,
operands.len()
@@ -2269,7 +2619,7 @@ where
unsafe {
BNGetDefaultArchitectureFlagWriteLowLevelIL(
- custom_arch.as_ref().0,
+ custom_arch.as_ref().handle,
op,
size,
role,
@@ -2281,7 +2631,7 @@ where
} else {
// TODO this should be impossible; requires bad flag/flag_write ids passed in;
// explode more violently
- lifter.unimplemented().expr_idx
+ lifter.unimplemented().index.0
}
}
@@ -2299,15 +2649,15 @@ where
handle: ctxt as *mut A,
};
- let class = custom_arch.flag_class_from_id(class);
+ let class = custom_arch.flag_class_from_id(FlagClassId(class));
- let mut lifter = unsafe { Lifter::from_raw(custom_arch_handle, il) };
+ let mut lifter = unsafe { MutableLiftedILFunction::from_raw(custom_arch_handle, il) };
if let Some(expr) = custom_arch.flag_cond_llil(cond, class, &mut lifter) {
// TODO verify that returned expr is a bool value
- return expr.expr_idx;
+ return expr.index.0;
}
- lifter.unimplemented().expr_idx
+ lifter.unimplemented().index.0
}
extern "C" fn cb_flag_group_llil<A>(
@@ -2323,16 +2673,16 @@ where
handle: ctxt as *mut A,
};
- let mut lifter = unsafe { Lifter::from_raw(custom_arch_handle, il) };
+ let mut lifter = unsafe { MutableLiftedILFunction::from_raw(custom_arch_handle, il) };
- if let Some(group) = custom_arch.flag_group_from_id(group) {
+ if let Some(group) = custom_arch.flag_group_from_id(FlagGroupId(group)) {
if let Some(expr) = custom_arch.flag_group_llil(group, &mut lifter) {
// TODO verify that returned expr is a bool value
- return expr.expr_idx;
+ return expr.index.0;
}
}
- lifter.unimplemented().expr_idx
+ lifter.unimplemented().index.0
}
extern "C" fn cb_free_register_list(_ctxt: *mut c_void, regs: *mut u32, count: usize) {
@@ -2341,7 +2691,7 @@ where
}
unsafe {
- let regs_ptr = ptr::slice_from_raw_parts_mut(regs, count);
+ let regs_ptr = std::ptr::slice_from_raw_parts_mut(regs, count);
let _regs = Box::from_raw(regs_ptr);
}
}
@@ -2353,12 +2703,12 @@ where
let custom_arch = unsafe { &*(ctxt as *mut A) };
let result = unsafe { &mut *result };
- if let Some(reg) = custom_arch.register_from_id(reg) {
+ if let Some(reg) = custom_arch.register_from_id(RegisterId(reg)) {
let info = reg.info();
result.fullWidthRegister = match info.parent() {
- Some(p) => p.id(),
- None => reg.id(),
+ Some(p) => p.id().0,
+ None => reg.id().0,
};
result.offset = info.offset();
@@ -2374,7 +2724,7 @@ where
let custom_arch = unsafe { &*(ctxt as *mut A) };
if let Some(reg) = custom_arch.stack_pointer_reg() {
- reg.id()
+ reg.id().0
} else {
0xffff_ffff
}
@@ -2387,7 +2737,7 @@ where
let custom_arch = unsafe { &*(ctxt as *mut A) };
if let Some(reg) = custom_arch.link_reg() {
- reg.id()
+ reg.id().0
} else {
0xffff_ffff
}
@@ -2399,9 +2749,9 @@ where
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- match custom_arch.register_stack_from_id(stack) {
- Some(stack) => BnString::new(stack.name().as_ref()).into_raw(),
- None => BnString::new("invalid_reg_stack").into_raw(),
+ match custom_arch.register_stack_from_id(RegisterStackId(stack)) {
+ Some(stack) => BnString::into_raw(BnString::new(stack.name().as_ref())),
+ None => BnString::into_raw(BnString::new("invalid_reg_stack")),
}
}
@@ -2410,16 +2760,16 @@ where
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync,
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- let mut regs: Vec<_> = custom_arch
+ let mut regs: Box<[_]> = custom_arch
.register_stacks()
.iter()
- .map(|r| r.id())
+ .map(|r| r.id().0)
.collect();
// SAFETY: Passed in to be written
unsafe { *count = regs.len() };
let regs_ptr = regs.as_mut_ptr();
- mem::forget(regs);
+ std::mem::forget(regs);
regs_ptr
}
@@ -2433,22 +2783,22 @@ where
let custom_arch = unsafe { &*(ctxt as *mut A) };
let result = unsafe { &mut *result };
- if let Some(stack) = custom_arch.register_stack_from_id(stack) {
+ if let Some(stack) = custom_arch.register_stack_from_id(RegisterStackId(stack)) {
let info = stack.info();
let (reg, count) = info.storage_regs();
- result.firstStorageReg = reg.id();
- result.storageCount = count;
+ result.firstStorageReg = reg.id().0;
+ result.storageCount = count as u32;
if let Some((reg, count)) = info.top_relative_regs() {
- result.firstTopRelativeReg = reg.id();
- result.topRelativeCount = count;
+ result.firstTopRelativeReg = reg.id().0;
+ result.topRelativeCount = count as u32;
} else {
result.firstTopRelativeReg = 0xffff_ffff;
result.topRelativeCount = 0;
}
- result.stackTopReg = info.stack_top_reg().id();
+ result.stackTopReg = info.stack_top_reg().id().0;
}
}
@@ -2457,7 +2807,11 @@ where
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync,
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- custom_arch.intrinsic_class(intrinsic)
+ match custom_arch.intrinsic_from_id(IntrinsicId(intrinsic)) {
+ Some(intrinsic) => intrinsic.class(),
+ // TODO: Make this unreachable?
+ None => BNIntrinsicClass::GeneralIntrinsicClass,
+ }
}
extern "C" fn cb_intrinsic_name<A>(ctxt: *mut c_void, intrinsic: u32) -> *mut c_char
@@ -2465,9 +2819,9 @@ where
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync,
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- match custom_arch.intrinsic_from_id(intrinsic) {
- Some(intrinsic) => BnString::new(intrinsic.name().as_ref()).into_raw(),
- None => BnString::new("invalid_intrinsic").into_raw(),
+ match custom_arch.intrinsic_from_id(IntrinsicId(intrinsic)) {
+ Some(intrinsic) => BnString::into_raw(BnString::new(intrinsic.name())),
+ None => BnString::into_raw(BnString::new("invalid_intrinsic")),
}
}
@@ -2476,12 +2830,12 @@ where
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync,
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- let mut intrinsics: Vec<_> = custom_arch.intrinsics().iter().map(|i| i.id()).collect();
+ let mut intrinsics: Box<[_]> = custom_arch.intrinsics().iter().map(|i| i.id().0).collect();
// SAFETY: Passed in to be written
unsafe { *count = intrinsics.len() };
let intrinsics_ptr = intrinsics.as_mut_ptr();
- mem::forget(intrinsics);
+ std::mem::forget(intrinsics);
intrinsics_ptr
}
@@ -2495,44 +2849,48 @@ where
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- let Some(intrinsic) = custom_arch.intrinsic_from_id(intrinsic) else {
+ let Some(intrinsic) = custom_arch.intrinsic_from_id(IntrinsicId(intrinsic)) else {
+ // SAFETY: Passed in to be written
unsafe {
*count = 0;
}
- return ptr::null_mut();
+ return std::ptr::null_mut();
};
let inputs = intrinsic.inputs();
- let mut res: Box<[_]> = inputs
- .into_iter()
- .map(|input| unsafe { Ref::into_raw(input) }.0)
- .collect();
+ // NOTE: The into_raw will leak and be freed later by `cb_free_name_and_types`.
+ let raw_inputs: Box<[_]> = inputs.into_iter().map(NameAndType::into_raw).collect();
+ // SAFETY: Passed in to be written
unsafe {
- *count = res.len();
- if res.is_empty() {
- ptr::null_mut()
- } else {
- let raw = res.as_mut_ptr();
- mem::forget(res);
- raw
- }
+ *count = raw_inputs.len();
+ }
+
+ if raw_inputs.is_empty() {
+ std::ptr::null_mut()
+ } else {
+ // Core is responsible for calling back to `cb_free_name_and_types`.
+ Box::leak(raw_inputs).as_mut_ptr()
}
}
- extern "C" fn cb_free_name_and_types<A>(ctxt: *mut c_void, nt: *mut BNNameAndType, count: usize)
- where
+ extern "C" fn cb_free_name_and_types<A>(
+ _ctxt: *mut c_void,
+ nt: *mut BNNameAndType,
+ count: usize,
+ ) where
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync,
{
- let _custom_arch = unsafe { &*(ctxt as *mut A) };
+ if nt.is_null() {
+ return;
+ }
- if !nt.is_null() {
- unsafe {
- 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));
- }
- }
+ // Reconstruct the box and drop.
+ let nt_ptr = std::ptr::slice_from_raw_parts_mut(nt, count);
+ // SAFETY: nt_ptr is a pointer to a Box.
+ let boxed_name_and_types = unsafe { Box::from_raw(nt_ptr) };
+ for nt in boxed_name_and_types {
+ NameAndType::free_raw(nt);
}
}
@@ -2546,25 +2904,31 @@ where
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- if let Some(intrinsic) = custom_arch.intrinsic_from_id(intrinsic) {
- let inputs = intrinsic.outputs();
- let mut res: Box<[_]> = inputs.iter().map(|input| input.as_ref().into()).collect();
-
- 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(IntrinsicId(intrinsic)) else {
+ // SAFETY: Passed in to be written
unsafe {
*count = 0;
}
- ptr::null_mut()
+ return std::ptr::null_mut();
+ };
+
+ let outputs = intrinsic.outputs();
+ let raw_outputs: Box<[BNTypeWithConfidence]> = outputs
+ .into_iter()
+ // Leaked to be freed later by `cb_free_type_list`.
+ .map(Conf::<Ref<Type>>::into_raw)
+ .collect();
+
+ // SAFETY: Passed in to be written
+ unsafe {
+ *count = raw_outputs.len();
+ }
+
+ if raw_outputs.is_empty() {
+ std::ptr::null_mut()
+ } else {
+ // Core is responsible for calling back to `cb_free_type_list`.
+ Box::leak(raw_outputs).as_mut_ptr()
}
}
@@ -2576,8 +2940,9 @@ where
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync,
{
let _custom_arch = unsafe { &*(ctxt as *mut A) };
- if !tl.is_null() {
- let _type_list = unsafe { Box::from_raw(ptr::slice_from_raw_parts_mut(tl, count)) };
+ let boxed_types = unsafe { Box::from_raw(std::ptr::slice_from_raw_parts_mut(tl, count)) };
+ for ty in boxed_types {
+ Conf::<Ref<Type>>::free_raw(ty);
}
}
@@ -2607,20 +2972,20 @@ where
Ok(result) => {
buffer.set_data(&result);
unsafe {
- *errors = BnString::new("").into_raw();
+ *errors = BnString::into_raw(BnString::new(""));
}
true
}
Err(result) => {
unsafe {
- *errors = BnString::new(result).into_raw();
+ *errors = BnString::into_raw(BnString::new(result));
}
false
}
};
// Caller owns the data buffer, don't free it
- mem::forget(buffer);
+ std::mem::forget(buffer);
result
}
@@ -2635,7 +3000,7 @@ where
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync,
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- let data = unsafe { slice::from_raw_parts(data, len) };
+ let data = unsafe { std::slice::from_raw_parts(data, len) };
custom_arch.is_never_branch_patch_available(data, addr)
}
@@ -2649,7 +3014,7 @@ where
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync,
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- let data = unsafe { slice::from_raw_parts(data, len) };
+ let data = unsafe { std::slice::from_raw_parts(data, len) };
custom_arch.is_always_branch_patch_available(data, addr)
}
@@ -2663,7 +3028,7 @@ where
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync,
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- let data = unsafe { slice::from_raw_parts(data, len) };
+ let data = unsafe { std::slice::from_raw_parts(data, len) };
custom_arch.is_invert_branch_patch_available(data, addr)
}
@@ -2677,7 +3042,7 @@ where
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync,
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- let data = unsafe { slice::from_raw_parts(data, len) };
+ let data = unsafe { std::slice::from_raw_parts(data, len) };
custom_arch.is_skip_and_return_zero_patch_available(data, addr)
}
@@ -2691,7 +3056,7 @@ where
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync,
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- let data = unsafe { slice::from_raw_parts(data, len) };
+ let data = unsafe { std::slice::from_raw_parts(data, len) };
custom_arch.is_skip_and_return_value_patch_available(data, addr)
}
@@ -2705,7 +3070,7 @@ where
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync,
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- let data = unsafe { slice::from_raw_parts_mut(data, len) };
+ let data = unsafe { std::slice::from_raw_parts_mut(data, len) };
custom_arch.convert_to_nop(data, addr)
}
@@ -2719,7 +3084,7 @@ where
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync,
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- let data = unsafe { slice::from_raw_parts_mut(data, len) };
+ let data = unsafe { std::slice::from_raw_parts_mut(data, len) };
custom_arch.always_branch(data, addr)
}
@@ -2733,7 +3098,7 @@ where
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync,
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- let data = unsafe { slice::from_raw_parts_mut(data, len) };
+ let data = unsafe { std::slice::from_raw_parts_mut(data, len) };
custom_arch.invert_branch(data, addr)
}
@@ -2748,7 +3113,7 @@ where
A: 'static + Architecture<Handle = CustomArchitectureHandle<A>> + Send + Sync,
{
let custom_arch = unsafe { &*(ctxt as *mut A) };
- let data = unsafe { slice::from_raw_parts_mut(data, len) };
+ let data = unsafe { std::slice::from_raw_parts_mut(data, len) };
custom_arch.skip_and_return_value(data, addr, val)
}
@@ -2767,7 +3132,9 @@ where
getAddressSize: Some(cb_address_size::<A>),
getDefaultIntegerSize: Some(cb_default_integer_size::<A>),
getInstructionAlignment: Some(cb_instruction_alignment::<A>),
+ // TODO: Make getOpcodeDisplayLength optional.
getMaxInstructionLength: Some(cb_max_instr_len::<A>),
+ // TODO: Make getOpcodeDisplayLength optional.
getOpcodeDisplayLength: Some(cb_opcode_display_len::<A>),
getAssociatedArchitectureByAddress: Some(cb_associated_arch_by_addr::<A>),
getInstructionInfo: Some(cb_instruction_info::<A>),
@@ -2923,9 +3290,9 @@ pub fn llvm_assemble(
let code = CString::new(code).map_err(|_| "Invalid encoding in code string".to_string())?;
let arch_triple = CString::new(arch_triple)
.map_err(|_| "Invalid encoding in architecture triple string".to_string())?;
- let mut out_bytes: *mut c_char = ptr::null_mut();
+ let mut out_bytes: *mut c_char = std::ptr::null_mut();
let mut out_bytes_len: c_int = 0;
- let mut err_bytes: *mut c_char = ptr::null_mut();
+ let mut err_bytes: *mut c_char = std::ptr::null_mut();
let mut err_len: c_int = 0;
unsafe {
@@ -2950,7 +3317,7 @@ pub fn llvm_assemble(
Vec::new()
} else {
unsafe {
- slice::from_raw_parts(
+ std::slice::from_raw_parts(
out_bytes as *const c_char as *const u8,
out_bytes_len as usize,
)
@@ -2962,7 +3329,7 @@ pub fn llvm_assemble(
"".into()
} else {
String::from_utf8_lossy(unsafe {
- slice::from_raw_parts(err_bytes as *const c_char as *const u8, err_len as usize)
+ std::slice::from_raw_parts(err_bytes as *const c_char as *const u8, err_len as usize)
})
.into_owned()
};
diff --git a/rust/src/backgroundtask.rs b/rust/src/background_task.rs
index e5fa6e77..0420afbf 100644
--- a/rust/src/backgroundtask.rs
+++ b/rust/src/background_task.rs
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-//! Background tasks provide plugins the ability to run tasks in the background so they don't hand the UI
+//! Background tasks provide plugins the ability to inform the core of long-running background tasks.
use binaryninjacore_sys::*;
@@ -23,6 +23,14 @@ use crate::string::*;
pub type Result<R> = result::Result<R, ()>;
+/// A [`BackgroundTask`] does not actually execute any code, only act as a handler, primarily to query
+/// the status of the task, and to cancel the task.
+///
+/// If you are looking to execute code in the background consider using rusts threading API, or if you
+/// want the core to execute the task on a worker thread, use the [`crate::worker_thread`] API.
+///
+/// NOTE: If you do not call [`BackgroundTask::finish`] or [`BackgroundTask::cancel`] the task will
+/// persist even _after_ it has been dropped.
#[derive(PartialEq, Eq, Hash)]
pub struct BackgroundTask {
pub(crate) handle: *mut BNBackgroundTask,
@@ -35,16 +43,12 @@ impl BackgroundTask {
Self { handle }
}
- pub fn new<S: BnStrCompatible>(initial_text: S, can_cancel: bool) -> Result<Ref<Self>> {
+ pub fn new<S: BnStrCompatible>(initial_text: S, can_cancel: bool) -> Ref<Self> {
let text = initial_text.into_bytes_with_nul();
-
let handle = unsafe { BNBeginBackgroundTask(text.as_ref().as_ptr() as *mut _, can_cancel) };
-
- if handle.is_null() {
- return Err(());
- }
-
- unsafe { Ok(Ref::new(Self { handle })) }
+ // We should always be returned a valid task.
+ assert!(!handle.is_null());
+ unsafe { Ref::new(Self { handle }) }
}
pub fn can_cancel(&self) -> bool {
@@ -55,25 +59,24 @@ impl BackgroundTask {
unsafe { BNIsBackgroundTaskCancelled(self.handle) }
}
- pub fn is_finished(&self) -> bool {
- unsafe { BNIsBackgroundTaskFinished(self.handle) }
- }
-
- pub fn get_progress_text(&self) -> BnString {
- unsafe { BnString::from_raw(BNGetBackgroundTaskProgressText(self.handle)) }
- }
-
pub fn cancel(&self) {
unsafe { BNCancelBackgroundTask(self.handle) }
}
+ pub fn is_finished(&self) -> bool {
+ unsafe { BNIsBackgroundTaskFinished(self.handle) }
+ }
+
pub fn finish(&self) {
unsafe { BNFinishBackgroundTask(self.handle) }
}
+ pub fn progress_text(&self) -> BnString {
+ unsafe { BnString::from_raw(BNGetBackgroundTaskProgressText(self.handle)) }
+ }
+
pub fn set_progress_text<S: BnStrCompatible>(&self, text: S) {
let progress_text = text.into_bytes_with_nul();
-
unsafe {
BNSetBackgroundTaskProgressText(self.handle, progress_text.as_ref().as_ptr() as *mut _)
}
@@ -83,7 +86,6 @@ impl BackgroundTask {
unsafe {
let mut count = 0;
let handles = BNGetRunningBackgroundTasks(&mut count);
-
Array::new(handles, count, ())
}
}
diff --git a/rust/src/basicblock.rs b/rust/src/basic_block.rs
index a6ac8f74..b880d28f 100644
--- a/rust/src/basicblock.rs
+++ b/rust/src/basic_block.rs
@@ -12,13 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use std::fmt;
-
use crate::architecture::CoreArchitecture;
use crate::function::Function;
-use binaryninjacore_sys::*;
-
use crate::rc::*;
+use crate::BranchType;
+use binaryninjacore_sys::*;
+use std::fmt;
+use std::fmt::Debug;
enum EdgeDirection {
Incoming,
@@ -26,30 +26,12 @@ enum EdgeDirection {
}
pub struct Edge<'a, C: 'a + BlockContext> {
- branch: super::BranchType,
- back_edge: bool,
- source: Guard<'a, BasicBlock<C>>,
+ pub branch: BranchType,
+ pub back_edge: bool,
+ pub source: Guard<'a, BasicBlock<C>>,
target: Guard<'a, BasicBlock<C>>,
}
-impl<'a, C: 'a + BlockContext> Edge<'a, C> {
- pub fn branch_type(&self) -> super::BranchType {
- self.branch
- }
-
- pub fn back_edge(&self) -> bool {
- self.back_edge
- }
-
- pub fn source(&self) -> &BasicBlock<C> {
- &self.source
- }
-
- pub fn target(&self) -> &BasicBlock<C> {
- &self.target
- }
-}
-
impl<'a, C: 'a + fmt::Debug + BlockContext> fmt::Debug for Edge<'a, C> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(
@@ -68,13 +50,17 @@ 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;
+ type Wrapped<'b>
+ = Edge<'b, C>
+ where
+ 'a: 'b;
}
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 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()),
@@ -104,6 +90,7 @@ unsafe impl<'a, C: 'a + BlockContext> CoreArrayProviderInner for Edge<'a, C> {
pub trait BlockContext: Clone + Sync + Send + Sized {
type Instruction;
+ type InstructionIndex: Debug + From<u64>;
type Iter: Iterator<Item = Self::Instruction>;
fn start(&self, block: &BasicBlock<Self>) -> Self::Instruction;
@@ -116,19 +103,20 @@ pub struct BasicBlock<C: BlockContext> {
context: C,
}
-unsafe impl<C: BlockContext> Send for BasicBlock<C> {}
-unsafe impl<C: BlockContext> Sync for BasicBlock<C> {}
-
impl<C: BlockContext> BasicBlock<C> {
pub(crate) unsafe fn from_raw(handle: *mut BNBasicBlock, context: C) -> Self {
Self { handle, context }
}
+ pub(crate) unsafe fn ref_from_raw(handle: *mut BNBasicBlock, context: C) -> Ref<Self> {
+ Ref::new(Self::from_raw(handle, context))
+ }
+
// TODO native bb vs il bbs
pub fn function(&self) -> Ref<Function> {
unsafe {
let func = BNGetBasicBlockFunction(self.handle);
- Function::from_raw(func)
+ Function::ref_from_raw(func)
}
}
@@ -143,12 +131,12 @@ impl<C: BlockContext> BasicBlock<C> {
self.context.iter(self)
}
- pub fn raw_start(&self) -> u64 {
- unsafe { BNGetBasicBlockStart(self.handle) }
+ pub fn start_index(&self) -> C::InstructionIndex {
+ C::InstructionIndex::from(unsafe { BNGetBasicBlockStart(self.handle) })
}
- pub fn raw_end(&self) -> u64 {
- unsafe { BNGetBasicBlockEnd(self.handle) }
+ pub fn end_index(&self) -> C::InstructionIndex {
+ C::InstructionIndex::from(unsafe { BNGetBasicBlockEnd(self.handle) })
}
pub fn raw_length(&self) -> u64 {
@@ -159,7 +147,6 @@ impl<C: BlockContext> BasicBlock<C> {
unsafe {
let mut count = 0;
let edges = BNGetBasicBlockIncomingEdges(self.handle, &mut count);
-
Array::new(
edges,
count,
@@ -175,7 +162,6 @@ impl<C: BlockContext> BasicBlock<C> {
unsafe {
let mut count = 0;
let edges = BNGetBasicBlockOutgoingEdges(self.handle, &mut count);
-
Array::new(
edges,
count,
@@ -187,7 +173,7 @@ impl<C: BlockContext> BasicBlock<C> {
}
}
- // is this valid for il blocks?
+ // is this valid for il blocks? (it looks like up to MLIL it is)
pub fn has_undetermined_outgoing_edges(&self) -> bool {
unsafe { BNBasicBlockHasUndeterminedOutgoingEdges(self.handle) }
}
@@ -196,18 +182,18 @@ impl<C: BlockContext> BasicBlock<C> {
unsafe { BNBasicBlockCanExit(self.handle) }
}
+ // TODO: Should we new type this? I just cant tell where the consumers of this are.
pub fn index(&self) -> usize {
unsafe { BNGetBasicBlockIndex(self.handle) }
}
pub fn immediate_dominator(&self) -> Option<Ref<Self>> {
unsafe {
+ // TODO: We don't allow the user to calculate post dominators
let block = BNGetBasicBlockImmediateDominator(self.handle, false);
-
if block.is_null() {
return None;
}
-
Some(Ref::new(BasicBlock::from_raw(block, self.context.clone())))
}
}
@@ -215,8 +201,8 @@ impl<C: BlockContext> BasicBlock<C> {
pub fn dominators(&self) -> Array<BasicBlock<C>> {
unsafe {
let mut count = 0;
+ // TODO: We don't allow the user to calculate post dominators
let blocks = BNGetBasicBlockDominators(self.handle, &mut count, false);
-
Array::new(blocks, count, self.context.clone())
}
}
@@ -224,8 +210,8 @@ impl<C: BlockContext> BasicBlock<C> {
pub fn strict_dominators(&self) -> Array<BasicBlock<C>> {
unsafe {
let mut count = 0;
+ // TODO: We don't allow the user to calculate post dominators
let blocks = BNGetBasicBlockStrictDominators(self.handle, &mut count, false);
-
Array::new(blocks, count, self.context.clone())
}
}
@@ -233,8 +219,8 @@ impl<C: BlockContext> BasicBlock<C> {
pub fn dominator_tree_children(&self) -> Array<BasicBlock<C>> {
unsafe {
let mut count = 0;
+ // TODO: We don't allow the user to calculate post dominators
let blocks = BNGetBasicBlockDominatorTreeChildren(self.handle, &mut count, false);
-
Array::new(blocks, count, self.context.clone())
}
}
@@ -242,8 +228,8 @@ impl<C: BlockContext> BasicBlock<C> {
pub fn dominance_frontier(&self) -> Array<BasicBlock<C>> {
unsafe {
let mut count = 0;
+ // TODO: We don't allow the user to calculate post dominators
let blocks = BNGetBasicBlockDominanceFrontier(self.handle, &mut count, false);
-
Array::new(blocks, count, self.context.clone())
}
}
@@ -251,7 +237,16 @@ impl<C: BlockContext> BasicBlock<C> {
// TODO iterated dominance frontier
}
-impl<'a, C: BlockContext> IntoIterator for &'a BasicBlock<C> {
+impl<C: BlockContext> IntoIterator for &BasicBlock<C> {
+ type Item = C::Instruction;
+ type IntoIter = C::Iter;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.iter()
+ }
+}
+
+impl<C: BlockContext> IntoIterator for BasicBlock<C> {
type Item = C::Instruction;
type IntoIter = C::Iter;
@@ -262,14 +257,12 @@ impl<'a, C: BlockContext> IntoIterator for &'a BasicBlock<C> {
impl<C: fmt::Debug + BlockContext> fmt::Debug for BasicBlock<C> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(
- f,
- "<bb handle {:p} context {:?} contents: {} -> {}>",
- self.handle,
- &self.context,
- self.raw_start(),
- self.raw_end()
- )
+ f.debug_struct("BasicBlock")
+ .field("context", &self.context)
+ .field("start_index", &self.start_index())
+ .field("end_index", &self.end_index())
+ .field("raw_length", &self.raw_length())
+ .finish()
}
}
@@ -297,14 +290,21 @@ 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;
+ type Wrapped<'a>
+ = Guard<'a, BasicBlock<C>>
+ where
+ C: 'a;
}
unsafe impl<C: BlockContext> CoreArrayProviderInner for BasicBlock<C> {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeBasicBlockList(raw, count);
}
+
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)
}
}
+
+unsafe impl<C: BlockContext> Send for BasicBlock<C> {}
+unsafe impl<C: BlockContext> Sync for BasicBlock<C> {}
diff --git a/rust/src/binary_reader.rs b/rust/src/binary_reader.rs
new file mode 100644
index 00000000..da29b515
--- /dev/null
+++ b/rust/src/binary_reader.rs
@@ -0,0 +1,173 @@
+// Copyright 2022-2024 Vector 35 Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! A convenience class for reading binary data
+
+use binaryninjacore_sys::*;
+use std::fmt::Debug;
+
+use crate::binary_view::{BinaryView, BinaryViewBase};
+use crate::Endianness;
+
+use crate::rc::Ref;
+use std::io::{ErrorKind, Read, Seek, SeekFrom};
+
+pub struct BinaryReader {
+ view: Ref<BinaryView>,
+ handle: *mut BNBinaryReader,
+}
+
+impl BinaryReader {
+ pub fn new(view: &BinaryView) -> Self {
+ let handle = unsafe { BNCreateBinaryReader(view.handle) };
+ Self {
+ view: view.to_owned(),
+ handle,
+ }
+ }
+
+ pub fn new_with_opts(view: &BinaryView, options: &BinaryReaderOptions) -> Self {
+ let mut reader = Self::new(view);
+ if let Some(endianness) = options.endianness {
+ reader.set_endianness(endianness);
+ }
+ // Set the virtual base before we seek.
+ if let Some(virtual_base) = options.virtual_base {
+ reader.set_virtual_base(virtual_base);
+ }
+ if let Some(address) = options.address {
+ reader.seek_to_offset(address);
+ }
+ reader
+ }
+
+ pub fn endianness(&self) -> Endianness {
+ unsafe { BNGetBinaryReaderEndianness(self.handle) }
+ }
+
+ pub fn set_endianness(&mut self, endianness: Endianness) {
+ unsafe { BNSetBinaryReaderEndianness(self.handle, endianness) }
+ }
+
+ pub fn virtual_base(&self) -> u64 {
+ unsafe { BNGetBinaryReaderVirtualBase(self.handle) }
+ }
+
+ pub fn set_virtual_base(&mut self, virtual_base_addr: u64) {
+ unsafe { BNSetBinaryReaderVirtualBase(self.handle, virtual_base_addr) }
+ }
+
+ /// Prefer using [crate::binary_reader::BinaryReader::seek] over this.
+ pub fn seek_to_offset(&mut self, offset: u64) {
+ unsafe { BNSeekBinaryReader(self.handle, offset) }
+ }
+
+ /// Prefer using [crate::binary_reader::BinaryReader::seek] over this.
+ pub fn seek_to_relative_offset(&mut self, offset: i64) {
+ unsafe { BNSeekBinaryReaderRelative(self.handle, offset) }
+ }
+
+ pub fn offset(&self) -> u64 {
+ unsafe { BNGetReaderPosition(self.handle) }
+ }
+
+ /// Are we at the end of the file?
+ pub fn is_eof(&self) -> bool {
+ unsafe { BNIsEndOfFile(self.handle) }
+ }
+}
+
+impl Debug for BinaryReader {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("BinaryReader")
+ .field("offset", &self.offset())
+ .field("virtual_base", &self.virtual_base())
+ .field("endianness", &self.endianness())
+ .finish()
+ }
+}
+
+impl Seek for BinaryReader {
+ /// Seek to the specified position.
+ fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
+ match pos {
+ SeekFrom::Current(offset) => self.seek_to_relative_offset(offset),
+ SeekFrom::Start(offset) => self.seek_to_offset(offset),
+ SeekFrom::End(end_offset) => {
+ let offset =
+ self.view
+ .len()
+ .checked_add_signed(end_offset)
+ .ok_or(std::io::Error::new(
+ ErrorKind::Other,
+ "Seeking from end overflowed",
+ ))?;
+ self.seek_to_offset(offset);
+ }
+ };
+
+ Ok(self.offset())
+ }
+}
+
+impl Read for BinaryReader {
+ fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
+ let len = buf.len();
+
+ let result = unsafe { BNReadData(self.handle, buf.as_mut_ptr() as *mut _, len) };
+
+ if !result {
+ Err(std::io::Error::new(ErrorKind::Other, "Read out of bounds"))
+ } else {
+ Ok(len)
+ }
+ }
+}
+
+impl Drop for BinaryReader {
+ fn drop(&mut self) {
+ unsafe { BNFreeBinaryReader(self.handle) }
+ }
+}
+
+unsafe impl Sync for BinaryReader {}
+unsafe impl Send for BinaryReader {}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
+pub struct BinaryReaderOptions {
+ endianness: Option<Endianness>,
+ virtual_base: Option<u64>,
+ address: Option<u64>,
+}
+
+impl BinaryReaderOptions {
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ pub fn with_endianness(mut self, endian: Endianness) -> Self {
+ self.endianness = Some(endian);
+ self
+ }
+
+ pub fn with_virtual_base(mut self, virtual_base_addr: u64) -> Self {
+ self.virtual_base = Some(virtual_base_addr);
+ self
+ }
+
+ pub fn with_address(mut self, address: u64) -> Self {
+ self.address = Some(address);
+ self
+ }
+}
diff --git a/rust/src/binaryview.rs b/rust/src/binary_view.rs
index 47f0c18d..b508fa8b 100644
--- a/rust/src/binaryview.rs
+++ b/rust/src/binary_view.rs
@@ -14,50 +14,59 @@
//! A view on binary data and queryable interface of a binary file.
//!
-//! One key job of BinaryView is file format parsing which allows Binary Ninja to read, write, insert, remove portions of the file given a virtual address. For the purposes of this documentation we define a virtual address as the memory address that the various pieces of the physical file will be loaded at.
+//! One key job of BinaryView is file format parsing which allows Binary Ninja to read, write,
+//! insert, remove portions of the file given a virtual address.
+//!
+//! For the purposes of this documentation we define a virtual address as the memory address that
+//! the various pieces of the physical file will be loaded at.
//! TODO : Mirror the Python docs for this
use binaryninjacore_sys::*;
-pub use binaryninjacore_sys::BNAnalysisState as AnalysisState;
-pub use binaryninjacore_sys::BNModificationStatus as ModificationStatus;
-
-use std::collections::HashMap;
-use std::ffi::{c_char, c_void};
-use std::ops::Range;
-use std::{ops, ptr, result, slice};
-
use crate::architecture::{Architecture, CoreArchitecture};
-use crate::basicblock::BasicBlock;
-use crate::component::{Component, ComponentBuilder, IntoComponentGuid};
-use crate::databuffer::DataBuffer;
+use crate::basic_block::BasicBlock;
+use crate::component::{Component, IntoComponentGuid};
+use crate::confidence::Conf;
+use crate::data_buffer::DataBuffer;
use crate::debuginfo::DebugInfo;
-use crate::fileaccessor::FileAccessor;
-use crate::filemetadata::FileMetadata;
+use crate::external_library::{ExternalLibrary, ExternalLocation};
+use crate::file_accessor::FileAccessor;
+use crate::file_metadata::FileMetadata;
use crate::flowgraph::FlowGraph;
use crate::function::{Function, NativeBlock};
-use crate::linearview::{LinearDisassemblyLine, LinearViewCursor};
+use crate::linear_view::{LinearDisassemblyLine, LinearViewCursor};
use crate::metadata::Metadata;
use crate::platform::Platform;
+use crate::progress::{NoProgressCallback, ProgressCallback};
+use crate::project::file::ProjectFile;
+use crate::rc::*;
+use crate::references::{CodeReference, DataReference};
use crate::relocation::Relocation;
use crate::section::{Section, SectionBuilder};
use crate::segment::{Segment, SegmentBuilder};
use crate::settings::Settings;
+use crate::string::*;
use crate::symbol::{Symbol, SymbolType};
use crate::tags::{Tag, TagType};
-use crate::typelibrary::TypeLibrary;
+use crate::type_container::TypeContainer;
+use crate::type_library::TypeLibrary;
use crate::types::{
- Conf, DataVariable, NamedTypeReference, QualifiedName, QualifiedNameAndType, Type,
+ NamedTypeReference, QualifiedName, QualifiedNameAndType, QualifiedNameTypeAndId, Type,
};
+use crate::variable::DataVariable;
use crate::Endianness;
-
-use crate::rc::*;
-use crate::references::{CodeReference, DataReference};
-use crate::string::*;
-
+use std::collections::HashMap;
+use std::ffi::{c_char, c_void};
+use std::ops::Range;
+use std::path::Path;
+use std::ptr::NonNull;
+use std::{result, slice};
// TODO : general reorg of modules related to bv
pub type Result<R> = result::Result<R, ()>;
+pub type BinaryViewEventType = BNBinaryViewEventType;
+pub type AnalysisState = BNAnalysisState;
+pub type ModificationStatus = BNModificationStatus;
#[allow(clippy::len_without_is_empty)]
pub trait BinaryViewBase: AsRef<BinaryView> {
@@ -120,7 +129,7 @@ pub trait BinaryViewBase: AsRef<BinaryView> {
0
}
- fn len(&self) -> usize {
+ fn len(&self) -> u64 {
0
}
@@ -136,11 +145,10 @@ pub trait BinaryViewBase: AsRef<BinaryView> {
fn default_endianness(&self) -> Endianness;
fn address_size(&self) -> usize;
- // TODO saving fileaccessor
fn save(&self) -> bool {
self.as_ref()
.parent_view()
- .map(|bv| bv.save())
+ .map(|view| view.save())
.unwrap_or(false)
}
}
@@ -167,25 +175,11 @@ pub struct AnalysisProgress {
pub total: usize,
}
-// TODO: Copied from debuginfo.rs, this should be consolidated
-struct ProgressContext(Option<Box<dyn Fn(usize, usize) -> Result<()>>>);
-
-extern "C" fn cb_progress(ctxt: *mut c_void, cur: usize, max: usize) -> bool {
- ffi_wrap!("BinaryViewExt::cb_progress", unsafe {
- let progress = ctxt as *mut ProgressContext;
- match &(*progress).0 {
- Some(func) => (func)(cur, max).is_ok(),
- None => true,
- }
- })
-}
-
pub trait BinaryViewExt: BinaryViewBase {
fn file(&self) -> Ref<FileMetadata> {
unsafe {
let raw = BNGetFileForView(self.as_ref().handle);
-
- Ref::new(FileMetadata::from_raw(raw))
+ FileMetadata::ref_from_raw(raw)
}
}
@@ -194,27 +188,16 @@ pub trait BinaryViewExt: BinaryViewBase {
unsafe { BnString::from_raw(ptr) }
}
- fn parent_view(&self) -> Result<Ref<BinaryView>> {
- let handle = unsafe { BNGetParentView(self.as_ref().handle) };
-
- if handle.is_null() {
- return Err(());
+ fn parent_view(&self) -> Option<Ref<BinaryView>> {
+ let raw_view_ptr = unsafe { BNGetParentView(self.as_ref().handle) };
+ match raw_view_ptr.is_null() {
+ false => Some(unsafe { BinaryView::ref_from_raw(raw_view_ptr) }),
+ true => None,
}
-
- unsafe { Ok(BinaryView::from_raw(handle)) }
}
- fn raw_view(&self) -> Result<Ref<BinaryView>> {
- let raw = "Raw".into_bytes_with_nul();
-
- let handle =
- unsafe { BNGetFileViewOfType(self.file().as_ref().handle, raw.as_ptr() as *mut _) };
-
- if handle.is_null() {
- return Err(());
- }
-
- unsafe { Ok(BinaryView::from_raw(handle)) }
+ fn raw_view(&self) -> Option<Ref<BinaryView>> {
+ self.file().view_of_type("Raw")
}
fn view_type(&self) -> BnString {
@@ -268,15 +251,11 @@ pub trait BinaryViewExt: BinaryViewBase {
}
fn original_image_base(&self) -> u64 {
- unsafe {
- BNGetOriginalImageBase(self.as_ref().handle)
- }
+ unsafe { BNGetOriginalImageBase(self.as_ref().handle) }
}
fn set_original_image_base(&self, image_base: u64) {
- unsafe {
- BNSetOriginalImageBase(self.as_ref().handle, image_base)
- }
+ unsafe { BNSetOriginalImageBase(self.as_ref().handle, image_base) }
}
fn end(&self) -> u64 {
@@ -326,7 +305,7 @@ pub trait BinaryViewExt: BinaryViewBase {
let mut active_info_list = vec![];
for active_info in active_infos {
- let func = unsafe { Function::from_raw(BNNewFunctionReference(active_info.func)) };
+ let func = unsafe { Function::ref_from_raw(active_info.func) };
active_info_list.push(ActiveAnalysisInfo {
func,
analysis_time: active_info.analysisTime,
@@ -368,7 +347,7 @@ pub trait BinaryViewExt: BinaryViewBase {
fn set_default_arch<A: Architecture>(&self, arch: &A) {
unsafe {
- BNSetDefaultArchitecture(self.as_ref().handle, arch.as_ref().0);
+ BNSetDefaultArchitecture(self.as_ref().handle, arch.as_ref().handle);
}
}
@@ -392,7 +371,7 @@ pub trait BinaryViewExt: BinaryViewBase {
fn instruction_len<A: Architecture>(&self, arch: &A, addr: u64) -> Option<usize> {
unsafe {
- let size = BNGetInstructionLength(self.as_ref().handle, arch.as_ref().0, addr);
+ let size = BNGetInstructionLength(self.as_ref().handle, arch.as_ref().handle, addr);
if size > 0 {
Some(size)
@@ -402,40 +381,37 @@ pub trait BinaryViewExt: BinaryViewBase {
}
}
- fn symbol_by_address(&self, addr: u64) -> Result<Ref<Symbol>> {
+ fn symbol_by_address(&self, addr: u64) -> Option<Ref<Symbol>> {
unsafe {
- let raw_sym = BNGetSymbolByAddress(self.as_ref().handle, addr, ptr::null_mut());
-
- if raw_sym.is_null() {
- return Err(());
+ let raw_sym_ptr =
+ BNGetSymbolByAddress(self.as_ref().handle, addr, std::ptr::null_mut());
+ match raw_sym_ptr.is_null() {
+ false => Some(Symbol::ref_from_raw(raw_sym_ptr)),
+ true => None,
}
-
- Ok(Symbol::ref_from_raw(raw_sym))
}
}
- fn symbol_by_raw_name<S: BnStrCompatible>(&self, raw_name: S) -> Result<Ref<Symbol>> {
+ fn symbol_by_raw_name<S: BnStrCompatible>(&self, raw_name: S) -> Option<Ref<Symbol>> {
let raw_name = raw_name.into_bytes_with_nul();
unsafe {
- let raw_sym = BNGetSymbolByRawName(
+ let raw_sym_ptr = BNGetSymbolByRawName(
self.as_ref().handle,
raw_name.as_ref().as_ptr() as *mut _,
- ptr::null_mut(),
+ std::ptr::null_mut(),
);
-
- if raw_sym.is_null() {
- return Err(());
+ match raw_sym_ptr.is_null() {
+ false => Some(Symbol::ref_from_raw(raw_sym_ptr)),
+ true => None,
}
-
- Ok(Symbol::ref_from_raw(raw_sym))
}
}
fn symbols(&self) -> Array<Symbol> {
unsafe {
let mut count = 0;
- let handles = BNGetSymbols(self.as_ref().handle, &mut count, ptr::null_mut());
+ let handles = BNGetSymbols(self.as_ref().handle, &mut count, std::ptr::null_mut());
Array::new(handles, count, ())
}
@@ -450,14 +426,14 @@ pub trait BinaryViewExt: BinaryViewBase {
self.as_ref().handle,
raw_name.as_ref().as_ptr() as *mut _,
&mut count,
- ptr::null_mut(),
+ std::ptr::null_mut(),
);
Array::new(handles, count, ())
}
}
- fn symbols_in_range(&self, range: ops::Range<u64>) -> Array<Symbol> {
+ fn symbols_in_range(&self, range: Range<u64>) -> Array<Symbol> {
unsafe {
let mut count = 0;
let len = range.end.wrapping_sub(range.start);
@@ -466,7 +442,7 @@ pub trait BinaryViewExt: BinaryViewBase {
range.start,
len,
&mut count,
- ptr::null_mut(),
+ std::ptr::null_mut(),
);
Array::new(handles, count, ())
@@ -476,14 +452,18 @@ pub trait BinaryViewExt: BinaryViewBase {
fn symbols_of_type(&self, ty: SymbolType) -> Array<Symbol> {
unsafe {
let mut count = 0;
- let handles =
- BNGetSymbolsOfType(self.as_ref().handle, ty.into(), &mut count, ptr::null_mut());
+ let handles = BNGetSymbolsOfType(
+ self.as_ref().handle,
+ ty.into(),
+ &mut count,
+ std::ptr::null_mut(),
+ );
Array::new(handles, count, ())
}
}
- fn symbols_of_type_in_range(&self, ty: SymbolType, range: ops::Range<u64>) -> Array<Symbol> {
+ fn symbols_of_type_in_range(&self, ty: SymbolType, range: Range<u64>) -> Array<Symbol> {
unsafe {
let mut count = 0;
let len = range.end.wrapping_sub(range.start);
@@ -493,7 +473,7 @@ pub trait BinaryViewExt: BinaryViewBase {
range.start,
len,
&mut count,
- ptr::null_mut(),
+ std::ptr::null_mut(),
);
Array::new(handles, count, ())
@@ -515,7 +495,7 @@ pub trait BinaryViewExt: BinaryViewBase {
let raw_type = if let Some(t) = ty.into() {
t.handle
} else {
- ptr::null_mut()
+ std::ptr::null_mut()
};
unsafe {
@@ -556,16 +536,15 @@ pub trait BinaryViewExt: BinaryViewBase {
unsafe {
let mut count = 0;
let vars = BNGetDataVariables(self.as_ref().handle, &mut count);
-
Array::new(vars, count, ())
}
}
- fn data_variable_at_address(&self, addr: u64) -> Option<Ref<DataVariable>> {
- let dv = BNDataVariable::default();
+ fn data_variable_at_address(&self, addr: u64) -> Option<DataVariable> {
+ let mut dv = BNDataVariable::default();
unsafe {
- if BNGetDataVariableAtAddress(self.as_ref().handle, addr, std::mem::transmute(&dv)) {
- Some(DataVariable(dv).to_owned())
+ if BNGetDataVariableAtAddress(self.as_ref().handle, addr, &mut dv) {
+ Some(DataVariable::from_owned_raw(dv))
} else {
None
}
@@ -573,15 +552,17 @@ pub trait BinaryViewExt: BinaryViewBase {
}
fn define_auto_data_var<'a, T: Into<Conf<&'a Type>>>(&self, addr: u64, ty: T) {
+ let mut owned_raw_ty = Conf::<&Type>::into_raw(ty.into());
unsafe {
- BNDefineDataVariable(self.as_ref().handle, addr, &mut ty.into().into());
+ BNDefineDataVariable(self.as_ref().handle, addr, &mut owned_raw_ty);
}
}
/// You likely would also like to call [`Self::define_user_symbol`] to bind this data variable with a name
fn define_user_data_var<'a, T: Into<Conf<&'a Type>>>(&self, addr: u64, ty: T) {
+ let mut owned_raw_ty = Conf::<&Type>::into_raw(ty.into());
unsafe {
- BNDefineUserDataVariable(self.as_ref().handle, addr, &mut ty.into().into());
+ BNDefineUserDataVariable(self.as_ref().handle, addr, &mut owned_raw_ty);
}
}
@@ -597,136 +578,131 @@ pub trait BinaryViewExt: BinaryViewBase {
}
}
- fn define_auto_type<S: BnStrCompatible>(
+ fn define_auto_type<T: Into<QualifiedName>, S: BnStrCompatible>(
&self,
- name: S,
+ name: T,
source: S,
type_obj: &Type,
) -> QualifiedName {
- let mut qualified_name = QualifiedName::from(name);
+ let mut raw_name = QualifiedName::into_raw(name.into());
let source_str = source.into_bytes_with_nul();
let name_handle = unsafe {
- let id_str = BNGenerateAutoTypeId(
- source_str.as_ref().as_ptr() as *const _,
- &mut qualified_name.0,
- );
- BNDefineAnalysisType(
- self.as_ref().handle,
- id_str,
- &mut qualified_name.0,
- type_obj.handle,
- )
+ let id_str =
+ BNGenerateAutoTypeId(source_str.as_ref().as_ptr() as *const _, &mut raw_name);
+ BNDefineAnalysisType(self.as_ref().handle, id_str, &mut raw_name, type_obj.handle)
};
- QualifiedName(name_handle)
+ QualifiedName::free_raw(raw_name);
+ QualifiedName::from_owned_raw(name_handle)
}
- fn define_auto_type_with_id<S: BnStrCompatible>(
+ fn define_auto_type_with_id<T: Into<QualifiedName>, S: BnStrCompatible>(
&self,
- name: S,
+ name: T,
id: S,
type_obj: &Type,
) -> QualifiedName {
- let mut qualified_name = QualifiedName::from(name);
+ let mut raw_name = QualifiedName::into_raw(name.into());
let id_str = id.into_bytes_with_nul();
- let name_handle = unsafe {
+ let result_raw_name = unsafe {
BNDefineAnalysisType(
self.as_ref().handle,
id_str.as_ref().as_ptr() as *const _,
- &mut qualified_name.0,
+ &mut raw_name,
type_obj.handle,
)
};
- QualifiedName(name_handle)
+ QualifiedName::free_raw(raw_name);
+ QualifiedName::from_owned_raw(result_raw_name)
}
- fn define_user_type<S: BnStrCompatible>(&self, name: S, type_obj: &Type) {
- let mut qualified_name = QualifiedName::from(name);
- unsafe {
- BNDefineUserAnalysisType(self.as_ref().handle, &mut qualified_name.0, type_obj.handle)
- }
+ fn define_user_type<T: Into<QualifiedName>>(&self, name: T, type_obj: &Type) {
+ let mut raw_name = QualifiedName::into_raw(name.into());
+ unsafe { BNDefineUserAnalysisType(self.as_ref().handle, &mut raw_name, type_obj.handle) }
+ QualifiedName::free_raw(raw_name);
}
- fn define_auto_types<S: BnStrCompatible>(
- &self,
- names_sources_and_types: Vec<(S, S, &Type)>,
- progress: Option<Box<dyn Fn(usize, usize) -> Result<()>>>,
- ) -> HashMap<String, QualifiedName> {
- let mut names = vec![];
- let mut ids = vec![];
- let mut types = vec![];
- let mut api_types =
- Vec::<BNQualifiedNameTypeAndId>::with_capacity(names_sources_and_types.len());
- for (name, source, type_obj) in names_sources_and_types.into_iter() {
- names.push(QualifiedName::from(name));
- ids.push(source.into_bytes_with_nul());
- types.push(type_obj);
- }
+ fn define_auto_types<T, I>(&self, names_sources_and_types: T) -> HashMap<String, QualifiedName>
+ where
+ T: Iterator<Item = I>,
+ I: Into<QualifiedNameTypeAndId>,
+ {
+ self.define_auto_types_with_progress(names_sources_and_types, NoProgressCallback)
+ }
- for ((name, source), type_obj) in names.iter().zip(ids.iter()).zip(types.iter()) {
- api_types.push(BNQualifiedNameTypeAndId {
- name: name.0,
- id: source.as_ref().as_ptr() as *mut _,
- type_: type_obj.handle,
- });
- }
+ fn define_auto_types_with_progress<T, I, P>(
+ &self,
+ names_sources_and_types: T,
+ mut progress: P,
+ ) -> HashMap<String, QualifiedName>
+ where
+ T: Iterator<Item = I>,
+ I: Into<QualifiedNameTypeAndId>,
+ P: ProgressCallback,
+ {
+ let mut types: Vec<BNQualifiedNameTypeAndId> = names_sources_and_types
+ .map(Into::into)
+ .map(QualifiedNameTypeAndId::into_raw)
+ .collect();
+ let mut result_ids: *mut *mut c_char = std::ptr::null_mut();
+ let mut result_names: *mut BNQualifiedName = std::ptr::null_mut();
- let mut progress_raw = ProgressContext(progress);
- let mut result_ids: *mut *mut c_char = ptr::null_mut();
- let mut result_names: *mut BNQualifiedName = ptr::null_mut();
let result_count = unsafe {
BNDefineAnalysisTypes(
self.as_ref().handle,
- api_types.as_mut_ptr(),
- api_types.len(),
- Some(cb_progress),
- &mut progress_raw as *mut _ as *mut c_void,
+ types.as_mut_ptr(),
+ types.len(),
+ Some(P::cb_progress_callback),
+ &mut progress as *mut P as *mut c_void,
&mut result_ids as *mut _,
&mut result_names as *mut _,
)
};
- let mut result = HashMap::with_capacity(result_count);
+ for ty in types {
+ QualifiedNameTypeAndId::free_raw(ty);
+ }
let id_array = unsafe { Array::<BnString>::new(result_ids, result_count, ()) };
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.to_owned(), name.clone());
- }
-
- result
+ id_array
+ .into_iter()
+ .zip(&name_array)
+ .map(|(id, name)| (id.to_owned(), name))
+ .collect()
}
- fn define_user_types<S: BnStrCompatible>(
- &self,
- names_and_types: Vec<(S, &Type)>,
- progress: Option<Box<dyn Fn(usize, usize) -> Result<()>>>,
- ) {
- let mut names = vec![];
- let mut types = vec![];
- let mut api_types = Vec::<BNQualifiedNameAndType>::with_capacity(names_and_types.len());
- for (name, type_obj) in names_and_types.into_iter() {
- names.push(QualifiedName::from(name));
- types.push(type_obj);
- }
+ fn define_user_types<T, I>(&self, names_and_types: T)
+ where
+ T: Iterator<Item = I>,
+ I: Into<QualifiedNameAndType>,
+ {
+ self.define_user_types_with_progress(names_and_types, NoProgressCallback);
+ }
- for (name, type_obj) in names.iter().zip(types.iter()) {
- api_types.push(BNQualifiedNameAndType {
- name: name.0,
- type_: type_obj.handle,
- });
- }
+ fn define_user_types_with_progress<T, I, P>(&self, names_and_types: T, mut progress: P)
+ where
+ T: Iterator<Item = I>,
+ I: Into<QualifiedNameAndType>,
+ P: ProgressCallback,
+ {
+ let mut types: Vec<BNQualifiedNameAndType> = names_and_types
+ .map(Into::into)
+ .map(QualifiedNameAndType::into_raw)
+ .collect();
- let mut progress_raw = ProgressContext(progress);
unsafe {
BNDefineUserAnalysisTypes(
self.as_ref().handle,
- api_types.as_mut_ptr(),
- api_types.len(),
- Some(cb_progress),
- &mut progress_raw as *mut _ as *mut c_void,
+ types.as_mut_ptr(),
+ types.len(),
+ Some(P::cb_progress_callback),
+ &mut progress as *mut P as *mut c_void,
)
};
+
+ for ty in types {
+ QualifiedNameAndType::free_raw(ty);
+ }
}
fn undefine_auto_type<S: BnStrCompatible>(&self, id: S) {
@@ -736,9 +712,10 @@ pub trait BinaryViewExt: BinaryViewBase {
}
}
- fn undefine_user_type<S: BnStrCompatible>(&self, name: S) {
- let mut qualified_name = QualifiedName::from(name);
- unsafe { BNUndefineUserAnalysisType(self.as_ref().handle, &mut qualified_name.0) }
+ fn undefine_user_type<T: Into<QualifiedName>>(&self, name: T) {
+ let mut raw_name = QualifiedName::into_raw(name.into());
+ unsafe { BNUndefineUserAnalysisType(self.as_ref().handle, &mut raw_name) }
+ QualifiedName::free_raw(raw_name);
}
fn types(&self) -> Array<QualifiedNameAndType> {
@@ -757,10 +734,11 @@ pub trait BinaryViewExt: BinaryViewBase {
}
}
- fn get_type_by_name<S: BnStrCompatible>(&self, name: S) -> Option<Ref<Type>> {
+ fn type_by_name<T: Into<QualifiedName>>(&self, name: T) -> Option<Ref<Type>> {
+ let mut raw_name = QualifiedName::into_raw(name.into());
unsafe {
- let mut qualified_name = QualifiedName::from(name);
- let type_handle = BNGetAnalysisTypeByName(self.as_ref().handle, &mut qualified_name.0);
+ let type_handle = BNGetAnalysisTypeByName(self.as_ref().handle, &mut raw_name);
+ QualifiedName::free_raw(raw_name);
if type_handle.is_null() {
return None;
}
@@ -768,7 +746,7 @@ pub trait BinaryViewExt: BinaryViewBase {
}
}
- fn get_type_by_ref(&self, ref_: &NamedTypeReference) -> Option<Ref<Type>> {
+ fn type_by_ref(&self, ref_: &NamedTypeReference) -> Option<Ref<Type>> {
unsafe {
let type_handle = BNGetAnalysisTypeByRef(self.as_ref().handle, ref_.handle);
if type_handle.is_null() {
@@ -778,7 +756,7 @@ pub trait BinaryViewExt: BinaryViewBase {
}
}
- fn get_type_by_id<S: BnStrCompatible>(&self, id: S) -> Option<Ref<Type>> {
+ fn type_by_id<S: BnStrCompatible>(&self, id: S) -> Option<Ref<Type>> {
unsafe {
let id_str = id.into_bytes_with_nul();
let type_handle =
@@ -790,137 +768,129 @@ pub trait BinaryViewExt: BinaryViewBase {
}
}
- fn get_type_name_by_id<S: BnStrCompatible>(&self, id: S) -> Option<QualifiedName> {
+ fn type_name_by_id<S: BnStrCompatible>(&self, id: S) -> Option<QualifiedName> {
unsafe {
let id_str = id.into_bytes_with_nul();
let name_handle =
BNGetAnalysisTypeNameById(self.as_ref().handle, id_str.as_ref().as_ptr() as *mut _);
- let name = QualifiedName(name_handle);
- if name.strings().is_empty() {
- return None;
+ let name = QualifiedName::from_owned_raw(name_handle);
+ // The core will return an empty qualified name if no type name was found.
+ match name.items.is_empty() {
+ true => None,
+ false => Some(name),
}
- Some(name)
}
}
- fn get_type_id<S: BnStrCompatible>(&self, name: S) -> Option<BnString> {
+ fn type_id_by_name<T: Into<QualifiedName>>(&self, name: T) -> Option<BnString> {
+ let mut raw_name = QualifiedName::into_raw(name.into());
unsafe {
- let mut qualified_name = QualifiedName::from(name);
- let id_cstr = BNGetAnalysisTypeId(self.as_ref().handle, &mut qualified_name.0);
+ let id_cstr = BNGetAnalysisTypeId(self.as_ref().handle, &mut raw_name);
+ QualifiedName::free_raw(raw_name);
let id = BnString::from_raw(id_cstr);
- if id.is_empty() {
- return None;
+ match id.is_empty() {
+ true => None,
+ false => Some(id),
}
- Some(id)
}
}
- fn is_type_auto_defined<S: BnStrCompatible>(&self, name: S) -> bool {
- unsafe {
- let mut qualified_name = QualifiedName::from(name);
- BNIsAnalysisTypeAutoDefined(self.as_ref().handle, &mut qualified_name.0)
- }
+ fn is_type_auto_defined<T: Into<QualifiedName>>(&self, name: T) -> bool {
+ let mut raw_name = QualifiedName::into_raw(name.into());
+ let result = unsafe { BNIsAnalysisTypeAutoDefined(self.as_ref().handle, &mut raw_name) };
+ QualifiedName::free_raw(raw_name);
+ result
}
fn segments(&self) -> Array<Segment> {
unsafe {
let mut count = 0;
- let segs = BNGetSegments(self.as_ref().handle, &mut count);
-
- Array::new(segs, count, ())
+ let raw_segments = BNGetSegments(self.as_ref().handle, &mut count);
+ Array::new(raw_segments, count, ())
}
}
- fn segment_at(&self, addr: u64) -> Option<Segment> {
+ fn segment_at(&self, addr: u64) -> Option<Ref<Segment>> {
unsafe {
let raw_seg = BNGetSegmentAt(self.as_ref().handle, addr);
- if !raw_seg.is_null() {
- Some(Segment::from_raw(raw_seg))
- } else {
- None
+ match raw_seg.is_null() {
+ false => Some(Segment::ref_from_raw(raw_seg)),
+ true => None,
}
}
}
/// Adds a segment to the view.
- ///
- /// NOTE: Consider using [BinaryViewExt::begin_bulk_add_segments] and [BinaryViewExt::end_bulk_add_segments]
+ ///
+ /// NOTE: Consider using [BinaryViewExt::begin_bulk_add_segments] and [BinaryViewExt::end_bulk_add_segments]
/// if you plan on adding a number of segments all at once, to avoid unnecessary MemoryMap updates.
fn add_segment(&self, segment: SegmentBuilder) {
segment.create(self.as_ref());
}
-
+
+ // TODO: Replace with BulkModify guard.
/// Start adding segments in bulk. Useful for adding large numbers of segments.
- ///
- /// After calling this any call to [BinaryViewExt::add_segment] will be uncommited until a call to
- /// [BinaryViewExt::end_bulk_add_segments]
- ///
- /// If you wish to discard the uncommited segments you can call [BinaryViewExt::cancel_bulk_add_segments].
- ///
- /// NOTE: This **must** be paired with a later call to [BinaryViewExt::end_bulk_add_segments] or
- /// [BinaryViewExt::cancel_bulk_add_segments], otherwise segments added after this call will stay uncommited.
+ ///
+ /// After calling this any call to [BinaryViewExt::add_segment] will be uncommitted until a call to
+ /// [BinaryViewExt::end_bulk_add_segments]
+ ///
+ /// If you wish to discard the uncommitted segments you can call [BinaryViewExt::cancel_bulk_add_segments].
+ ///
+ /// NOTE: This **must** be paired with a later call to [BinaryViewExt::end_bulk_add_segments] or
+ /// [BinaryViewExt::cancel_bulk_add_segments], otherwise segments added after this call will stay uncommitted.
fn begin_bulk_add_segments(&self) {
- unsafe {
- BNBeginBulkAddSegments(self.as_ref().handle)
- }
+ unsafe { BNBeginBulkAddSegments(self.as_ref().handle) }
}
+ // TODO: Replace with BulkModify guard.
/// Commit all auto and user segments that have been added since the call to [Self::begin_bulk_add_segments].
- ///
+ ///
/// NOTE: This **must** be paired with a prior call to [Self::begin_bulk_add_segments], otherwise this
/// does nothing and segments are added individually.
fn end_bulk_add_segments(&self) {
- unsafe {
- BNEndBulkAddSegments(self.as_ref().handle)
- }
+ unsafe { BNEndBulkAddSegments(self.as_ref().handle) }
}
+ // TODO: Replace with BulkModify guard.
/// Flushes the auto and user segments that have yet to be committed.
- ///
+ ///
/// This is to be used in conjunction with [Self::begin_bulk_add_segments]
/// and [Self::end_bulk_add_segments], where the latter will commit the segments
/// which have been added since [Self::begin_bulk_add_segments], this function
/// will discard them so that they do not get added to the view.
fn cancel_bulk_add_segments(&self) {
- unsafe {
- BNCancelBulkAddSegments(self.as_ref().handle)
- }
+ unsafe { BNCancelBulkAddSegments(self.as_ref().handle) }
}
-
fn add_section<S: BnStrCompatible>(&self, section: SectionBuilder<S>) {
section.create(self.as_ref());
}
fn remove_auto_section<S: BnStrCompatible>(&self, name: S) {
- let name = name.into_bytes_with_nul();
- let name_ptr = name.as_ref().as_ptr() as *mut _;
-
+ let raw_name = name.into_bytes_with_nul();
+ let raw_name_ptr = raw_name.as_ref().as_ptr() as *mut _;
unsafe {
- BNRemoveAutoSection(self.as_ref().handle, name_ptr);
+ BNRemoveAutoSection(self.as_ref().handle, raw_name_ptr);
}
}
fn remove_user_section<S: BnStrCompatible>(&self, name: S) {
- let name = name.into_bytes_with_nul();
- let name_ptr = name.as_ref().as_ptr() as *mut _;
-
+ let raw_name = name.into_bytes_with_nul();
+ let raw_name_ptr = raw_name.as_ref().as_ptr() as *mut _;
unsafe {
- BNRemoveUserSection(self.as_ref().handle, name_ptr);
+ BNRemoveUserSection(self.as_ref().handle, raw_name_ptr);
}
}
- fn section_by_name<S: BnStrCompatible>(&self, name: S) -> Result<Section> {
+ fn section_by_name<S: BnStrCompatible>(&self, name: S) -> Option<Ref<Section>> {
unsafe {
let raw_name = name.into_bytes_with_nul();
let name_ptr = raw_name.as_ref().as_ptr() as *mut _;
- let raw_section = BNGetSectionByName(self.as_ref().handle, name_ptr);
-
- if raw_section.is_null() {
- return Err(());
+ let raw_section_ptr = BNGetSectionByName(self.as_ref().handle, name_ptr);
+ match raw_section_ptr.is_null() {
+ false => Some(Section::ref_from_raw(raw_section_ptr)),
+ true => None,
}
-
- Ok(Section::from_raw(raw_section))
}
}
@@ -928,7 +898,6 @@ pub trait BinaryViewExt: BinaryViewBase {
unsafe {
let mut count = 0;
let sections = BNGetSections(self.as_ref().handle, &mut count);
-
Array::new(sections, count, ())
}
}
@@ -937,7 +906,6 @@ pub trait BinaryViewExt: BinaryViewBase {
unsafe {
let mut count = 0;
let sections = BNGetSectionsAt(self.as_ref().handle, addr, &mut count);
-
Array::new(sections, count, ())
}
}
@@ -949,14 +917,14 @@ pub trait BinaryViewExt: BinaryViewBase {
plat.handle,
addr,
false,
- ptr::null_mut(),
+ std::ptr::null_mut(),
);
if handle.is_null() {
return None;
}
- Some(Function::from_raw(handle))
+ Some(Function::ref_from_raw(handle))
}
}
@@ -970,7 +938,7 @@ pub trait BinaryViewExt: BinaryViewBase {
unsafe {
let func_type = match func_type {
Some(func_type) => func_type.handle,
- None => ptr::null_mut(),
+ None => std::ptr::null_mut(),
};
let handle = BNAddFunctionForAnalysis(
@@ -985,7 +953,7 @@ pub trait BinaryViewExt: BinaryViewBase {
return None;
}
- Some(Function::from_raw(handle))
+ Some(Function::ref_from_raw(handle))
}
}
@@ -1003,7 +971,7 @@ pub trait BinaryViewExt: BinaryViewBase {
return Err(());
}
- Ok(Function::from_raw(func))
+ Ok(Function::ref_from_raw(func))
}
}
@@ -1011,15 +979,13 @@ pub trait BinaryViewExt: BinaryViewBase {
unsafe { BNHasFunctions(self.as_ref().handle) }
}
- fn entry_point_function(&self) -> Result<Ref<Function>> {
+ fn entry_point_function(&self) -> Option<Ref<Function>> {
unsafe {
- let func = BNGetAnalysisEntryPoint(self.as_ref().handle);
-
- if func.is_null() {
- return Err(());
+ let raw_func_ptr = BNGetAnalysisEntryPoint(self.as_ref().handle);
+ match raw_func_ptr.is_null() {
+ false => Some(Function::ref_from_raw(raw_func_ptr)),
+ true => None,
}
-
- Ok(Function::from_raw(func))
}
}
@@ -1063,18 +1029,16 @@ pub trait BinaryViewExt: BinaryViewBase {
}
}
- fn function_at(&self, platform: &Platform, addr: u64) -> Result<Ref<Function>> {
+ fn function_at(&self, platform: &Platform, addr: u64) -> Option<Ref<Function>> {
unsafe {
- let handle = BNGetAnalysisFunction(self.as_ref().handle, platform.handle, addr);
-
- if handle.is_null() {
- return Err(());
+ let raw_func_ptr = BNGetAnalysisFunction(self.as_ref().handle, platform.handle, addr);
+ match raw_func_ptr.is_null() {
+ false => Some(Function::ref_from_raw(raw_func_ptr)),
+ true => None,
}
-
- Ok(Function::from_raw(handle))
}
}
-
+
fn function_start_before(&self, addr: u64) -> u64 {
unsafe { BNGetPreviousFunctionStartBeforeAddress(self.as_ref().handle, addr) }
}
@@ -1087,7 +1051,6 @@ pub trait BinaryViewExt: BinaryViewBase {
unsafe {
let mut count = 0;
let blocks = BNGetBasicBlocksForAddress(self.as_ref().handle, addr, &mut count);
-
Array::new(blocks, count, NativeBlock::new())
}
}
@@ -1096,7 +1059,6 @@ pub trait BinaryViewExt: BinaryViewBase {
unsafe {
let mut count = 0;
let blocks = BNGetBasicBlocksStartingAtAddress(self.as_ref().handle, addr, &mut count);
-
Array::new(blocks, count, NativeBlock::new())
}
}
@@ -1121,7 +1083,7 @@ pub trait BinaryViewExt: BinaryViewBase {
}
fn debug_info(&self) -> Ref<DebugInfo> {
- unsafe { DebugInfo::from_raw(BNGetDebugInfo(self.as_ref().handle)) }
+ unsafe { DebugInfo::ref_from_raw(BNGetDebugInfo(self.as_ref().handle)) }
}
fn set_debug_info(&self, debug_info: &DebugInfo) {
@@ -1194,29 +1156,28 @@ pub trait BinaryViewExt: BinaryViewBase {
}
/// Get a tag type by its name.
- fn get_tag_type<S: BnStrCompatible>(&self, name: S) -> Option<Ref<TagType>> {
+ fn tag_type_by_name<S: BnStrCompatible>(&self, name: S) -> Option<Ref<TagType>> {
let name = name.into_bytes_with_nul();
-
unsafe {
let handle = BNGetTagType(self.as_ref().handle, name.as_ref().as_ptr() as *mut _);
if handle.is_null() {
return None;
}
- Some(TagType::from_raw(handle))
+ Some(TagType::ref_from_raw(handle))
}
}
/// Get a tag by its id.
///
/// Note this does not tell you anything about where it is used.
- fn get_tag<S: BnStrCompatible>(&self, id: S) -> Option<Ref<Tag>> {
+ fn tag_by_id<S: BnStrCompatible>(&self, id: S) -> Option<Ref<Tag>> {
let id = id.into_bytes_with_nul();
unsafe {
let handle = BNGetTag(self.as_ref().handle, id.as_ref().as_ptr() as *mut _);
if handle.is_null() {
return None;
}
- Some(Tag::from_raw(handle))
+ Some(Tag::ref_from_raw(handle))
}
}
@@ -1341,7 +1302,7 @@ pub trait BinaryViewExt: BinaryViewBase {
}
/// Retrieves a list of [CodeReference]s pointing to a given address.
- fn get_code_refs(&self, addr: u64) -> Array<CodeReference> {
+ fn code_refs_to_addr(&self, addr: u64) -> Array<CodeReference> {
unsafe {
let mut count = 0;
let handle = BNGetCodeReferences(self.as_ref().handle, addr, &mut count);
@@ -1349,27 +1310,8 @@ pub trait BinaryViewExt: BinaryViewBase {
}
}
- /// Retrieves a list of addresses pointed to by a given address.
- fn get_code_refs_from(&self, addr: u64, func: Option<&Function>) -> Vec<u64> {
- unsafe {
- let mut count = 0;
- // TODO: We could have done a [CodeReference] however it seems to be poorly written.
- // TODO: It uses manually drop on a ref that only gets dropped in the array, insane behavior.
- // TODO: For now just construct it manually.
- let mut src = BNReferenceSource {
- func: func.map(|f| f.handle).unwrap_or(std::ptr::null_mut()),
- arch: func.map(|f| f.arch().0).unwrap_or(std::ptr::null_mut()),
- addr,
- };
- let addresses = BNGetCodeReferencesFrom(self.as_ref().handle, &mut src, &mut count);
- let res = std::slice::from_raw_parts(addresses, count).to_vec();
- BNFreeAddressList(addresses);
- res
- }
- }
-
/// Retrieves a list of [CodeReference]s pointing into a given [Range].
- fn get_code_refs_in_range(&self, range: Range<u64>) -> Array<CodeReference> {
+ fn code_refs_into_range(&self, range: Range<u64>) -> Array<CodeReference> {
unsafe {
let mut count = 0;
let handle = BNGetCodeReferencesInRange(
@@ -1382,26 +1324,32 @@ pub trait BinaryViewExt: BinaryViewBase {
}
}
- /// Retrieves a list of [DataReference]s pointing to a given address.
- fn get_data_refs(&self, addr: u64) -> Array<DataReference> {
+ /// Retrieves a list of addresses pointed to by a given address.
+ fn code_refs_from_addr(&self, addr: u64, func: Option<&Function>) -> Vec<u64> {
unsafe {
let mut count = 0;
- let handle = BNGetDataReferences(self.as_ref().handle, addr, &mut count);
- Array::new(handle, count, ())
+ let code_ref =
+ CodeReference::new(addr, func.map(|f| f.to_owned()), func.map(|f| f.arch()));
+ let mut raw_code_ref = CodeReference::into_owned_raw(&code_ref);
+ let addresses =
+ BNGetCodeReferencesFrom(self.as_ref().handle, &mut raw_code_ref, &mut count);
+ let res = std::slice::from_raw_parts(addresses, count).to_vec();
+ BNFreeAddressList(addresses);
+ res
}
}
- /// Retrieves a list of [DataReference]s originating from a given address.
- fn get_data_refs_from(&self, addr: u64) -> Array<DataReference> {
+ /// Retrieves a list of [DataReference]s pointing to a given address.
+ fn data_refs_to_addr(&self, addr: u64) -> Array<DataReference> {
unsafe {
let mut count = 0;
- let handle = BNGetDataReferencesFrom(self.as_ref().handle, addr, &mut count);
+ let handle = BNGetDataReferences(self.as_ref().handle, addr, &mut count);
Array::new(handle, count, ())
}
}
/// Retrieves a list of [DataReference]s pointing into a given [Range].
- fn get_data_refs_in_range(&self, range: Range<u64>) -> Array<DataReference> {
+ fn data_refs_into_range(&self, range: Range<u64>) -> Array<DataReference> {
unsafe {
let mut count = 0;
let handle = BNGetDataReferencesInRange(
@@ -1414,44 +1362,40 @@ pub trait BinaryViewExt: BinaryViewBase {
}
}
+ /// Retrieves a list of [DataReference]s originating from a given address.
+ fn data_refs_from_addr(&self, addr: u64) -> Array<DataReference> {
+ unsafe {
+ let mut count = 0;
+ let handle = BNGetDataReferencesFrom(self.as_ref().handle, addr, &mut count);
+ Array::new(handle, count, ())
+ }
+ }
+
/// Retrieves a list of [CodeReference]s for locations in code that use a given named type.
- ///
- /// TODO: It might be cleaner if this used an already allocated type from the core and
- /// used its name instead of this slightly-gross [QualifiedName] hack. Since the returned
- /// object doesn't have any [QualifiedName], I'm assuming the core does not alias
- /// the [QualifiedName] we pass to it and it is safe to destroy it on [Drop], as in this function.
- fn get_code_refs_for_type<B: BnStrCompatible>(&self, name: B) -> Array<CodeReference> {
+ fn code_refs_using_type_name<T: Into<QualifiedName>>(&self, name: T) -> Array<CodeReference> {
+ let mut raw_name = QualifiedName::into_raw(name.into());
unsafe {
let mut count = 0;
- let q_name = &mut QualifiedName::from(name).0;
- let handle = BNGetCodeReferencesForType(
- self.as_ref().handle,
- q_name as *mut BNQualifiedName,
- &mut count,
- );
+ let handle =
+ BNGetCodeReferencesForType(self.as_ref().handle, &mut raw_name, &mut count);
+ QualifiedName::free_raw(raw_name);
Array::new(handle, count, ())
}
}
- /// Retrieves a list of [DataReference]s instances of a given named type in data.
- ///
- /// TODO: It might be cleaner if this used an already allocated type from the core and
- /// used its name instead of this slightly-gross [QualifiedName] hack. Since the returned
- /// object doesn't have any [QualifiedName], I'm assuming the core does not alias
- /// the [QualifiedName] we pass to it and it is safe to destroy it on [Drop], as in this function.
- fn get_data_refs_for_type<B: BnStrCompatible>(&self, name: B) -> Array<DataReference> {
+
+ /// Retrieves a list of [DataReference]s for locations in data that use a given named type.
+ fn data_refs_using_type_name<T: Into<QualifiedName>>(&self, name: T) -> Array<DataReference> {
+ let mut raw_name = QualifiedName::into_raw(name.into());
unsafe {
let mut count = 0;
- let q_name = &mut QualifiedName::from(name).0;
- let handle = BNGetDataReferencesForType(
- self.as_ref().handle,
- q_name as *mut BNQualifiedName,
- &mut count,
- );
+ let handle =
+ BNGetDataReferencesForType(self.as_ref().handle, &mut raw_name, &mut count);
+ QualifiedName::free_raw(raw_name);
Array::new(handle, count, ())
}
}
- fn get_relocations_at(&self, addr: u64) -> Array<Relocation> {
+ fn relocations_at(&self, addr: u64) -> Array<Relocation> {
unsafe {
let mut count = 0;
let handle = BNGetRelocationsAt(self.as_ref().handle, addr, &mut count);
@@ -1459,7 +1403,7 @@ pub trait BinaryViewExt: BinaryViewBase {
}
}
- fn get_relocation_ranges(&self) -> Vec<Range<u64>> {
+ fn relocation_ranges(&self) -> Vec<Range<u64>> {
let ranges = unsafe {
let mut count = 0;
let reloc_ranges_ptr = BNGetRelocationRanges(self.as_ref().handle, &mut count);
@@ -1478,39 +1422,35 @@ pub trait BinaryViewExt: BinaryViewBase {
.collect()
}
- fn component_by_guid<S: BnStrCompatible>(&self, guid: S) -> Option<Component> {
+ fn component_by_guid<S: BnStrCompatible>(&self, guid: S) -> Option<Ref<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,
+ name.as_ref().as_ptr() as *const c_char,
)
};
- core::ptr::NonNull::new(result).map(|h| unsafe { Component::from_raw(h) })
+ NonNull::new(result).map(|h| unsafe { Component::ref_from_raw(h) })
}
- fn root_component(&self) -> Option<Component> {
+ fn root_component(&self) -> Option<Ref<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)
+ NonNull::new(result).map(|h| unsafe { Component::ref_from_raw(h) })
}
- fn component_by_path<P: BnStrCompatible>(&self, path: P) -> Option<Component> {
+ fn component_by_path<P: BnStrCompatible>(&self, path: P) -> Option<Ref<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,
+ path.as_ref().as_ptr() as *const c_char,
)
};
- core::ptr::NonNull::new(result).map(|h| unsafe { Component::from_raw(h) })
+ NonNull::new(result).map(|h| unsafe { Component::ref_from_raw(h) })
}
fn remove_component(&self, component: &Component) -> bool {
- unsafe { BNRemoveComponent(self.as_ref().handle, component.as_raw()) }
+ unsafe { BNRemoveComponent(self.as_ref().handle, component.handle.as_ptr()) }
}
fn remove_component_by_guid<P: IntoComponentGuid>(&self, guid: P) -> bool {
@@ -1518,21 +1458,141 @@ pub trait BinaryViewExt: BinaryViewBase {
unsafe { BNRemoveComponentByGuid(self.as_ref().handle, path.as_ptr()) }
}
- fn data_variable_parent_components(
- &self,
- data_variable: &DataVariable,
- ) -> Array<Component> {
+ 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(),
+ data_variable.address,
&mut count,
)
};
unsafe { Array::new(result, count, ()) }
}
+ fn external_libraries(&self) -> Array<ExternalLibrary> {
+ let mut count = 0;
+ let result = unsafe { BNBinaryViewGetExternalLibraries(self.as_ref().handle, &mut count) };
+ unsafe { Array::new(result, count, ()) }
+ }
+
+ fn external_library<S: BnStrCompatible>(&self, name: S) -> Option<Ref<ExternalLibrary>> {
+ let name_ptr = name.into_bytes_with_nul();
+ let result = unsafe {
+ BNBinaryViewGetExternalLibrary(
+ self.as_ref().handle,
+ name_ptr.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ let result_ptr = NonNull::new(result)?;
+ Some(unsafe { ExternalLibrary::ref_from_raw(result_ptr) })
+ }
+
+ fn remove_external_library<S: BnStrCompatible>(&self, name: S) {
+ let name_ptr = name.into_bytes_with_nul();
+ unsafe {
+ BNBinaryViewRemoveExternalLibrary(
+ self.as_ref().handle,
+ name_ptr.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ }
+
+ fn add_external_library<S: BnStrCompatible>(
+ &self,
+ name: S,
+ backing_file: Option<&ProjectFile>,
+ auto: bool,
+ ) -> Option<Ref<ExternalLibrary>> {
+ let name_ptr = name.into_bytes_with_nul();
+ let result = unsafe {
+ BNBinaryViewAddExternalLibrary(
+ self.as_ref().handle,
+ name_ptr.as_ref().as_ptr() as *const c_char,
+ backing_file
+ .map(|b| b.handle.as_ptr())
+ .unwrap_or(std::ptr::null_mut()),
+ auto,
+ )
+ };
+ NonNull::new(result).map(|h| unsafe { ExternalLibrary::ref_from_raw(h) })
+ }
+
+ fn external_locations(&self) -> Array<ExternalLocation> {
+ let mut count = 0;
+ let result = unsafe { BNBinaryViewGetExternalLocations(self.as_ref().handle, &mut count) };
+ unsafe { Array::new(result, count, ()) }
+ }
+
+ fn external_location_from_symbol(&self, symbol: &Symbol) -> Option<Ref<ExternalLocation>> {
+ let result =
+ unsafe { BNBinaryViewGetExternalLocation(self.as_ref().handle, symbol.handle) };
+ let result_ptr = NonNull::new(result)?;
+ Some(unsafe { ExternalLocation::ref_from_raw(result_ptr) })
+ }
+
+ fn remove_external_location(&self, location: &ExternalLocation) {
+ self.remove_external_location_from_symbol(&location.source_symbol())
+ }
+
+ fn remove_external_location_from_symbol(&self, symbol: &Symbol) {
+ unsafe { BNBinaryViewRemoveExternalLocation(self.as_ref().handle, symbol.handle) };
+ }
+
+ // TODO: This is awful, rewrite this.
+ fn add_external_location<S: BnStrCompatible>(
+ &self,
+ symbol: &Symbol,
+ library: &ExternalLibrary,
+ target_symbol_name: S,
+ target_address: Option<u64>,
+ target_is_auto: bool,
+ ) -> Option<Ref<ExternalLocation>> {
+ let target_symbol_name = target_symbol_name.into_bytes_with_nul();
+ let target_address_ptr = target_address
+ .map(|a| a as *mut u64)
+ .unwrap_or(std::ptr::null_mut());
+ let result = unsafe {
+ BNBinaryViewAddExternalLocation(
+ self.as_ref().handle,
+ symbol.handle,
+ library.handle.as_ptr(),
+ target_symbol_name.as_ref().as_ptr() as *const c_char,
+ target_address_ptr,
+ target_is_auto,
+ )
+ };
+ NonNull::new(result).map(|h| unsafe { ExternalLocation::ref_from_raw(h) })
+ }
+
+ /// Type container for all types (user and auto) in the Binary View.
+ ///
+ /// NOTE: Modifying an auto type will promote it to a user type.
+ fn type_container(&self) -> TypeContainer {
+ let type_container_ptr =
+ NonNull::new(unsafe { BNGetAnalysisTypeContainer(self.as_ref().handle) });
+ // NOTE: I have no idea how this isn't a UAF, see the note in `TypeContainer::from_raw`
+ unsafe { TypeContainer::from_raw(type_container_ptr.unwrap()) }
+ }
+
+ /// Type container for user types in the Binary View.
+ fn user_type_container(&self) -> TypeContainer {
+ let type_container_ptr =
+ NonNull::new(unsafe { BNGetAnalysisUserTypeContainer(self.as_ref().handle) });
+ // NOTE: I have no idea how this isn't a UAF, see the note in `TypeContainer::from_raw`
+ unsafe { TypeContainer::from_raw(type_container_ptr.unwrap()) }
+ }
+
+ /// Type container for auto types in the Binary View.
+ ///
+ /// NOTE: Unlike [`Self::type_container`] modification of auto types will **NOT** promote it to a user type.
+ fn auto_type_container(&self) -> TypeContainer {
+ let type_container_ptr =
+ NonNull::new(unsafe { BNGetAnalysisAutoTypeContainer(self.as_ref().handle) });
+ // NOTE: I have no idea how this isn't a UAF, see the note in `TypeContainer::from_raw`
+ unsafe { TypeContainer::from_raw(type_container_ptr.unwrap()) }
+ }
+
/// 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()) }
@@ -1543,10 +1603,10 @@ pub trait BinaryViewExt: BinaryViewBase {
let result = unsafe {
BNGetBinaryViewTypeLibrary(
self.as_ref().handle,
- name.as_ref().as_ptr() as *const core::ffi::c_char,
+ name.as_ref().as_ptr() as *const c_char,
)
};
- core::ptr::NonNull::new(result).map(|h| unsafe { TypeLibrary::from_raw(h) })
+ NonNull::new(result).map(|h| unsafe { TypeLibrary::from_raw(h) })
}
/// Should be called by custom py:py:class:`BinaryView` implementations
@@ -1557,22 +1617,24 @@ pub trait BinaryViewExt: BinaryViewBase {
/// * `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(
+ fn record_imported_object_library<T: Into<QualifiedName>>(
&self,
lib: &TypeLibrary,
- name: &QualifiedName,
+ name: T,
addr: u64,
platform: &Platform,
) {
+ let mut raw_name = QualifiedName::into_raw(name.into());
unsafe {
BNBinaryViewRecordImportedObjectLibrary(
self.as_ref().handle,
platform.handle,
addr,
lib.as_raw(),
- &name.0 as *const _ as *mut _,
+ &mut raw_name,
)
}
+ QualifiedName::free_raw(raw_name);
}
/// Recursively imports a type from the specified type library, or, if
@@ -1585,22 +1647,20 @@ pub trait BinaryViewExt: BinaryViewBase {
/// 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(
+ fn import_type_library<T: Into<QualifiedName>>(
&self,
- name: &QualifiedName,
+ name: T,
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());
+ .unwrap_or(std::ptr::null_mut());
+ let mut raw_name = QualifiedName::into_raw(name.into());
let result = unsafe {
- BNBinaryViewImportTypeLibraryType(
- self.as_ref().handle,
- &mut lib_ref,
- &name.0 as *const _ as *mut _,
- )
+ BNBinaryViewImportTypeLibraryType(self.as_ref().handle, &mut lib_ref, &mut raw_name)
};
+ QualifiedName::free_raw(raw_name);
(!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) })
}
@@ -1613,22 +1673,20 @@ pub trait BinaryViewExt: BinaryViewBase {
///
/// .. 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(
+ fn import_type_object<T: Into<QualifiedName>>(
&self,
- name: &QualifiedName,
+ name: T,
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());
+ .unwrap_or(std::ptr::null_mut());
+ let mut raw_name = QualifiedName::into_raw(name.into());
let result = unsafe {
- BNBinaryViewImportTypeLibraryObject(
- self.as_ref().handle,
- &mut lib_ref,
- &name.0 as *const _ as *mut _,
- )
+ BNBinaryViewImportTypeLibraryObject(self.as_ref().handle, &mut lib_ref, &mut raw_name)
};
+ QualifiedName::free_raw(raw_name);
(!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) })
}
@@ -1653,30 +1711,44 @@ pub trait BinaryViewExt: BinaryViewBase {
///
/// 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) {
+ fn export_type_to_library<T: Into<QualifiedName>>(
+ &self,
+ lib: &TypeLibrary,
+ name: T,
+ type_obj: &Type,
+ ) {
+ let mut raw_name = QualifiedName::into_raw(name.into());
unsafe {
BNBinaryViewExportTypeToTypeLibrary(
self.as_ref().handle,
lib.as_raw(),
- &name.0 as *const _ as *mut _,
+ &mut raw_name,
type_obj.handle,
)
}
+ QualifiedName::free_raw(raw_name);
}
/// 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) {
+ fn export_object_to_library<T: Into<QualifiedName>>(
+ &self,
+ lib: &TypeLibrary,
+ name: T,
+ type_obj: &Type,
+ ) {
+ let mut raw_name = QualifiedName::into_raw(name.into());
unsafe {
BNBinaryViewExportObjectToTypeLibrary(
self.as_ref().handle,
lib.as_raw(),
- &name.0 as *const _ as *mut _,
+ &mut raw_name,
type_obj.handle,
)
}
+ QualifiedName::free_raw(raw_name);
}
/// Gives you details of which type library and name was used to determine
@@ -1689,8 +1761,8 @@ pub trait BinaryViewExt: BinaryViewBase {
addr: u64,
platform: &Platform,
) -> Option<(TypeLibrary, QualifiedName)> {
- let mut result_lib = ptr::null_mut();
- let mut result_name = Default::default();
+ let mut result_lib = std::ptr::null_mut();
+ let mut result_name = BNQualifiedName::default();
let success = unsafe {
BNBinaryViewLookupImportedObjectLibrary(
self.as_ref().handle,
@@ -1703,35 +1775,54 @@ pub trait BinaryViewExt: BinaryViewBase {
if !success {
return None;
}
- let lib = unsafe { TypeLibrary::from_raw(ptr::NonNull::new(result_lib)?) };
- let name = QualifiedName(result_name);
+ let lib = unsafe { TypeLibrary::from_raw(NonNull::new(result_lib)?) };
+ let name = QualifiedName::from_owned_raw(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(
+ fn lookup_imported_type_library<T: Into<QualifiedName>>(
&self,
- name: &QualifiedNameAndType,
+ name: T,
) -> Option<(TypeLibrary, QualifiedName)> {
- let mut result_lib = ptr::null_mut();
- let mut result_name = Default::default();
+ let raw_name = QualifiedName::into_raw(name.into());
+ let mut result_lib = std::ptr::null_mut();
+ let mut result_name = BNQualifiedName::default();
let success = unsafe {
BNBinaryViewLookupImportedTypeLibrary(
self.as_ref().handle,
- &name.0 as *const _ as *mut _,
+ &raw_name,
&mut result_lib,
&mut result_name,
)
};
+ QualifiedName::free_raw(raw_name);
if !success {
return None;
}
- let lib = unsafe { TypeLibrary::from_raw(ptr::NonNull::new(result_lib)?) };
- let name = QualifiedName(result_name);
+ let lib = unsafe { TypeLibrary::from_raw(NonNull::new(result_lib)?) };
+ let name = QualifiedName::from_owned_raw(result_name);
Some((lib, name))
}
+ //
+ // fn type_archives(&self) -> Array<TypeArchive> {
+ // let mut ids: *mut *mut c_char = std::ptr::null_mut();
+ // let mut paths: *mut *mut c_char = std::ptr::null_mut();
+ // let count = unsafe { BNBinaryViewGetTypeArchives(self.as_ref().handle, &mut ids, &mut paths) };
+ // let path_list = unsafe { Array::<BnString>::new(paths, count, ()) };
+ // let ids_list = unsafe { std::slice::from_raw_parts(ids, count).to_vec() };
+ // let archives = ids_list.iter().filter_map(|id| {
+ // let archive_raw = unsafe { BNBinaryViewGetTypeArchive(self.as_ref().handle, *id) };
+ // match archive_raw.is_null() {
+ // true => None,
+ // false => Some(archive_raw)
+ // }
+ // }).collect();
+ // unsafe { BNFreeStringList(ids, count) };
+ // Array::new(archives)
+ // }
}
impl<T: BinaryViewBase> BinaryViewExt for T {}
@@ -1742,21 +1833,20 @@ pub struct BinaryView {
}
impl BinaryView {
- pub(crate) unsafe fn from_raw(handle: *mut BNBinaryView) -> Ref<Self> {
+ pub(crate) unsafe fn from_raw(handle: *mut BNBinaryView) -> Self {
debug_assert!(!handle.is_null());
+ Self { handle }
+ }
+ pub(crate) unsafe fn ref_from_raw(handle: *mut BNBinaryView) -> Ref<Self> {
+ debug_assert!(!handle.is_null());
Ref::new(Self { handle })
}
- pub fn from_filename<S: BnStrCompatible>(
- meta: &mut FileMetadata,
- filename: S,
- ) -> Result<Ref<Self>> {
- let file = filename.into_bytes_with_nul();
-
- let handle = unsafe {
- BNCreateBinaryDataViewFromFilename(meta.handle, file.as_ref().as_ptr() as *mut _)
- };
+ pub fn from_path(meta: &mut FileMetadata, file_path: impl AsRef<Path>) -> Result<Ref<Self>> {
+ let file = file_path.as_ref().into_bytes_with_nul();
+ let handle =
+ unsafe { BNCreateBinaryDataViewFromFilename(meta.handle, file.as_ptr() as *mut _) };
if handle.is_null() {
return Err(());
@@ -1766,8 +1856,7 @@ impl BinaryView {
}
pub fn from_accessor(meta: &FileMetadata, file: &mut FileAccessor) -> Result<Ref<Self>> {
- let handle =
- unsafe { BNCreateBinaryDataViewFromFile(meta.handle, &mut file.api_object as *mut _) };
+ let handle = unsafe { BNCreateBinaryDataViewFromFile(meta.handle, &mut file.api_object) };
if handle.is_null() {
return Err(());
@@ -1787,6 +1876,17 @@ impl BinaryView {
unsafe { Ok(Ref::new(Self { handle })) }
}
+
+ /// Save the original binary file to the provided `file_path` along with any modifications.
+ pub fn save_to_path(&self, file_path: impl AsRef<Path>) -> bool {
+ let file = file_path.as_ref().into_bytes_with_nul();
+ unsafe { BNSaveToFilename(self.handle, file.as_ptr() as *mut _) }
+ }
+
+ /// Save the original binary file to the provided [`FileAccessor`] along with any modifications.
+ pub fn save_to_accessor(&self, file: &mut FileAccessor) -> bool {
+ unsafe { BNSaveToFile(self.handle, &mut file.api_object) }
+ }
}
impl BinaryViewBase for BinaryView {
@@ -1806,10 +1906,6 @@ impl BinaryViewBase for BinaryView {
unsafe { BNRemoveViewData(self.handle, offset, len as u64) }
}
- fn modification_status(&self, offset: u64) -> ModificationStatus {
- unsafe { BNGetModification(self.handle, offset) }
- }
-
fn offset_valid(&self, offset: u64) -> bool {
unsafe { BNIsValidOffset(self.handle, offset) }
}
@@ -1834,32 +1930,36 @@ impl BinaryViewBase for BinaryView {
unsafe { BNGetNextValidOffset(self.handle, offset) }
}
- fn default_endianness(&self) -> Endianness {
- unsafe { BNGetDefaultEndianness(self.handle) }
+ fn modification_status(&self, offset: u64) -> ModificationStatus {
+ unsafe { BNGetModification(self.handle, offset) }
}
- fn relocatable(&self) -> bool {
- unsafe { BNIsRelocatable(self.handle) }
+ fn start(&self) -> u64 {
+ unsafe { BNGetStartOffset(self.handle) }
}
- fn address_size(&self) -> usize {
- unsafe { BNGetViewAddressSize(self.handle) }
+ fn len(&self) -> u64 {
+ unsafe { BNGetViewLength(self.handle) }
}
- fn start(&self) -> u64 {
- unsafe { BNGetStartOffset(self.handle) }
+ fn executable(&self) -> bool {
+ unsafe { BNIsExecutableView(self.handle) }
}
- fn len(&self) -> usize {
- unsafe { BNGetViewLength(self.handle) as usize }
+ fn relocatable(&self) -> bool {
+ unsafe { BNIsRelocatable(self.handle) }
}
fn entry_point(&self) -> u64 {
unsafe { BNGetEntryPoint(self.handle) }
}
- fn executable(&self) -> bool {
- unsafe { BNIsExecutableView(self.handle) }
+ fn default_endianness(&self) -> Endianness {
+ unsafe { BNGetDefaultEndianness(self.handle) }
+ }
+
+ fn address_size(&self) -> usize {
+ unsafe { BNGetViewAddressSize(self.handle) }
}
}
@@ -1894,13 +1994,25 @@ unsafe impl Sync for BinaryView {}
impl std::fmt::Debug for BinaryView {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(
- f,
- "BinaryView (type: `{}`): '{}', len {:#x}",
- self.view_type(),
- self.file().filename(),
- self.len()
- )
+ f.debug_struct("BinaryView")
+ .field("type_name", &self.type_name())
+ .field("file", &self.file())
+ .field("original_image_base", &self.original_image_base())
+ .field("start", &self.start())
+ .field("end", &self.end())
+ .field("len", &self.len())
+ .field("default_platform", &self.default_platform())
+ .field("default_arch", &self.default_arch())
+ .field("default_endianness", &self.default_endianness())
+ .field("entry_point", &self.entry_point())
+ .field(
+ "entry_point_functions",
+ &self.entry_point_functions().to_vec(),
+ )
+ .field("address_size", &self.address_size())
+ .field("sections", &self.sections().to_vec())
+ .field("segments", &self.segments().to_vec())
+ .finish()
}
}
@@ -1908,14 +2020,14 @@ pub trait BinaryViewEventHandler: 'static + Sync {
fn on_event(&self, binary_view: &BinaryView);
}
-pub type BinaryViewEventType = BNBinaryViewEventType;
-
/// Registers an event listener for binary view events.
///
/// # Example
///
/// ```no_run
-/// use binaryninja::binaryview::{BinaryView, BinaryViewEventHandler, BinaryViewEventType, register_binary_view_event};
+/// use binaryninja::binary_view::{
+/// register_binary_view_event, BinaryView, BinaryViewEventHandler, BinaryViewEventType,
+/// };
///
/// struct EventHandlerContext {
/// // Context holding state available to event handler
@@ -1929,7 +2041,7 @@ pub type BinaryViewEventType = BNBinaryViewEventType;
///
/// #[no_mangle]
/// pub extern "C" fn CorePluginInit() {
-/// let context = EventHandlerContext { };
+/// let context = EventHandlerContext {};
///
/// register_binary_view_event(
/// BinaryViewEventType::BinaryViewInitialAnalysisCompletionEvent,
@@ -1947,7 +2059,7 @@ where
) {
ffi_wrap!("EventHandler::on_event", {
let context = unsafe { &*(ctx as *const Handler) };
- context.on_event(&BinaryView::from_raw(BNNewViewReference(view)));
+ context.on_event(&BinaryView::ref_from_raw(BNNewViewReference(view)));
})
}
diff --git a/rust/src/binary_writer.rs b/rust/src/binary_writer.rs
new file mode 100644
index 00000000..ce713435
--- /dev/null
+++ b/rust/src/binary_writer.rs
@@ -0,0 +1,152 @@
+// Copyright 2022-2024 Vector 35 Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! A convenience class for writing binary data
+
+use binaryninjacore_sys::*;
+use std::fmt::Debug;
+
+use crate::binary_view::{BinaryView, BinaryViewBase};
+use crate::Endianness;
+
+use crate::rc::Ref;
+use std::io::{ErrorKind, Seek, SeekFrom, Write};
+
+pub struct BinaryWriter {
+ view: Ref<BinaryView>,
+ handle: *mut BNBinaryWriter,
+}
+
+impl BinaryWriter {
+ pub fn new(view: &BinaryView) -> Self {
+ let handle = unsafe { BNCreateBinaryWriter(view.handle) };
+ Self {
+ view: view.to_owned(),
+ handle,
+ }
+ }
+
+ pub fn new_with_opts(view: &BinaryView, options: &BinaryWriterOptions) -> Self {
+ let mut writer = Self::new(view);
+ if let Some(endianness) = options.endianness {
+ writer.set_endianness(endianness);
+ }
+ if let Some(address) = options.address {
+ writer.seek_to_offset(address);
+ }
+ writer
+ }
+
+ pub fn endianness(&self) -> Endianness {
+ unsafe { BNGetBinaryWriterEndianness(self.handle) }
+ }
+
+ pub fn set_endianness(&mut self, endianness: Endianness) {
+ unsafe { BNSetBinaryWriterEndianness(self.handle, endianness) }
+ }
+
+ pub fn seek_to_offset(&mut self, offset: u64) {
+ unsafe { BNSeekBinaryWriter(self.handle, offset) }
+ }
+
+ pub fn seek_to_relative_offset(&mut self, offset: i64) {
+ unsafe { BNSeekBinaryWriterRelative(self.handle, offset) }
+ }
+
+ pub fn offset(&self) -> u64 {
+ unsafe { BNGetWriterPosition(self.handle) }
+ }
+}
+
+impl Debug for BinaryWriter {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("BinaryWriter")
+ .field("offset", &self.offset())
+ .field("endianness", &self.endianness())
+ .finish()
+ }
+}
+
+impl Seek for BinaryWriter {
+ /// Seek to the specified position.
+ fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
+ match pos {
+ SeekFrom::Current(offset) => self.seek_to_relative_offset(offset),
+ SeekFrom::Start(offset) => self.seek_to_offset(offset),
+ SeekFrom::End(end_offset) => {
+ let offset =
+ self.view
+ .len()
+ .checked_add_signed(end_offset)
+ .ok_or(std::io::Error::new(
+ ErrorKind::Other,
+ "Seeking from end overflowed",
+ ))?;
+ self.seek_to_offset(offset);
+ }
+ };
+
+ Ok(self.offset())
+ }
+}
+
+impl Write for BinaryWriter {
+ fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
+ let len = buf.len();
+ let result = unsafe { BNWriteData(self.handle, buf.as_ptr() as *mut _, len) };
+ if !result {
+ Err(std::io::Error::new(
+ std::io::ErrorKind::Other,
+ "write out of bounds",
+ ))
+ } else {
+ Ok(len)
+ }
+ }
+
+ fn flush(&mut self) -> std::io::Result<()> {
+ Ok(())
+ }
+}
+
+impl Drop for BinaryWriter {
+ fn drop(&mut self) {
+ unsafe { BNFreeBinaryWriter(self.handle) }
+ }
+}
+
+unsafe impl Sync for BinaryWriter {}
+unsafe impl Send for BinaryWriter {}
+
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
+pub struct BinaryWriterOptions {
+ endianness: Option<Endianness>,
+ address: Option<u64>,
+}
+
+impl BinaryWriterOptions {
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ pub fn with_endianness(mut self, endian: Endianness) -> Self {
+ self.endianness = Some(endian);
+ self
+ }
+
+ pub fn with_address(mut self, address: u64) -> Self {
+ self.address = Some(address);
+ self
+ }
+}
diff --git a/rust/src/binaryreader.rs b/rust/src/binaryreader.rs
deleted file mode 100644
index db681e74..00000000
--- a/rust/src/binaryreader.rs
+++ /dev/null
@@ -1,101 +0,0 @@
-// Copyright 2022-2024 Vector 35 Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! A convenience class for reading binary data
-
-use binaryninjacore_sys::*;
-
-use crate::binaryview::BinaryView;
-use crate::Endianness;
-
-use std::io::{Read, Seek, SeekFrom};
-
-pub struct BinaryReader {
- handle: *mut BNBinaryReader,
-}
-
-impl BinaryReader {
- pub fn new(view: &BinaryView, endian: Endianness) -> Self {
- let handle = unsafe { BNCreateBinaryReader(view.handle) };
- unsafe {
- BNSetBinaryReaderEndianness(handle, endian);
- }
- Self { handle }
- }
-
- pub fn endian(&self) -> Endianness {
- unsafe { BNGetBinaryReaderEndianness(self.handle) }
- }
-
- pub fn set_endian(&self, endian: Endianness) {
- unsafe { BNSetBinaryReaderEndianness(self.handle, endian) }
- }
-
- pub fn offset(&self) -> u64 {
- unsafe { BNGetReaderPosition(self.handle) }
- }
-
- pub fn eof(&self) -> bool {
- unsafe { BNIsEndOfFile(self.handle) }
- }
-}
-
-impl Seek for BinaryReader {
- /// Seek to the specified position.
- ///
- /// # Errors
- /// Seeking relative to [SeekFrom::End] is unsupported and will return an error.
- fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
- unsafe {
- match pos {
- SeekFrom::Current(offset) => BNSeekBinaryReaderRelative(self.handle, offset),
- SeekFrom::Start(offset) => BNSeekBinaryReader(self.handle, offset),
- _ => {
- return Err(std::io::Error::new(
- std::io::ErrorKind::Unsupported,
- "Cannot seek end of BinaryReader",
- ))
- }
- };
- }
-
- Ok(self.offset())
- }
-}
-
-impl Read for BinaryReader {
- fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
- let len = buf.len();
-
- let result = unsafe { BNReadData(self.handle, buf.as_mut_ptr() as *mut _, len) };
-
- if !result {
- Err(std::io::Error::new(
- std::io::ErrorKind::Other,
- "Read out of bounds",
- ))
- } else {
- Ok(len)
- }
- }
-}
-
-impl Drop for BinaryReader {
- fn drop(&mut self) {
- unsafe { BNFreeBinaryReader(self.handle) }
- }
-}
-
-unsafe impl Sync for BinaryReader {}
-unsafe impl Send for BinaryReader {}
diff --git a/rust/src/binarywriter.rs b/rust/src/binarywriter.rs
deleted file mode 100644
index 802a9b37..00000000
--- a/rust/src/binarywriter.rs
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2022-2024 Vector 35 Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! A convenience class for writing binary data
-
-use binaryninjacore_sys::*;
-
-use crate::binaryview::BinaryView;
-use crate::Endianness;
-
-use std::io::{Seek, SeekFrom, Write};
-
-pub struct BinaryWriter {
- handle: *mut BNBinaryWriter,
-}
-
-impl BinaryWriter {
- pub fn new(view: &BinaryView, endian: Endianness) -> Self {
- let handle = unsafe { BNCreateBinaryWriter(view.handle) };
- unsafe {
- BNSetBinaryWriterEndianness(handle, endian);
- }
- Self { handle }
- }
-
- pub fn endian(&self) -> Endianness {
- unsafe { BNGetBinaryWriterEndianness(self.handle) }
- }
-
- pub fn set_endian(&self, endian: Endianness) {
- unsafe { BNSetBinaryWriterEndianness(self.handle, endian) }
- }
-
- pub fn offset(&self) -> u64 {
- unsafe { BNGetWriterPosition(self.handle) }
- }
-}
-
-impl Seek for BinaryWriter {
- /// Seek to the specified position.
- ///
- /// # Errors
- /// Seeking relative to [SeekFrom::End] is unsupported and will return an error.
- fn seek(&mut self, pos: SeekFrom) -> std::io::Result<u64> {
- unsafe {
- match pos {
- SeekFrom::Current(offset) => BNSeekBinaryWriterRelative(self.handle, offset),
- SeekFrom::Start(offset) => BNSeekBinaryWriter(self.handle, offset),
- _ => {
- return Err(std::io::Error::new(
- std::io::ErrorKind::Unsupported,
- "Cannot seek end of BinaryWriter",
- ))
- }
- };
- }
-
- Ok(self.offset())
- }
-}
-
-impl Write for BinaryWriter {
- fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
- let len = buf.len();
- let result = unsafe { BNWriteData(self.handle, buf.as_ptr() as *mut _, len) };
- if !result {
- Err(std::io::Error::new(
- std::io::ErrorKind::Other,
- "write out of bounds",
- ))
- } else {
- Ok(len)
- }
- }
-
- fn flush(&mut self) -> std::io::Result<()> {
- Ok(())
- }
-}
-
-impl Drop for BinaryWriter {
- fn drop(&mut self) {
- unsafe { BNFreeBinaryWriter(self.handle) }
- }
-}
-
-unsafe impl Sync for BinaryWriter {}
-unsafe impl Send for BinaryWriter {}
diff --git a/rust/src/callingconvention.rs b/rust/src/calling_convention.rs
index 37c5012f..929cc26b 100644
--- a/rust/src/callingconvention.rs
+++ b/rust/src/calling_convention.rs
@@ -15,66 +15,64 @@
//! Contains and provides information about different systems' calling conventions to analysis.
use std::borrow::Borrow;
+use std::ffi::c_void;
use std::fmt::{Debug, Formatter};
+use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
-use std::mem;
-use std::os::raw::c_void;
-use std::ptr;
-use std::slice;
use binaryninjacore_sys::*;
-use crate::architecture::{Architecture, ArchitectureExt, CoreArchitecture, Register};
-use crate::rc::{
- CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable,
+use crate::architecture::{
+ Architecture, ArchitectureExt, CoreArchitecture, CoreRegister, Register, RegisterId,
};
+use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
use crate::string::*;
-
+use crate::types::FunctionParameter;
+use crate::variable::Variable;
// TODO
// force valid registers once Arch has _from_id methods
// CallingConvention impl
// dataflow callbacks
-pub trait CallingConventionBase: Sync {
- type Arch: Architecture;
-
- fn caller_saved_registers(&self) -> Vec<<Self::Arch as Architecture>::Register>;
- fn callee_saved_registers(&self) -> Vec<<Self::Arch as Architecture>::Register>;
- fn int_arg_registers(&self) -> Vec<<Self::Arch as Architecture>::Register>;
- fn float_arg_registers(&self) -> Vec<<Self::Arch as Architecture>::Register>;
+pub trait CallingConvention: Sync {
+ fn caller_saved_registers(&self) -> Vec<RegisterId>;
+ fn callee_saved_registers(&self) -> Vec<RegisterId>;
+ fn int_arg_registers(&self) -> Vec<RegisterId>;
+ fn float_arg_registers(&self) -> Vec<RegisterId>;
fn arg_registers_shared_index(&self) -> bool;
fn reserved_stack_space_for_arg_registers(&self) -> bool;
fn stack_adjusted_on_return(&self) -> bool;
fn is_eligible_for_heuristics(&self) -> bool;
- fn return_int_reg(&self) -> Option<<Self::Arch as Architecture>::Register>;
- fn return_hi_int_reg(&self) -> Option<<Self::Arch as Architecture>::Register>;
- fn return_float_reg(&self) -> Option<<Self::Arch as Architecture>::Register>;
+ fn return_int_reg(&self) -> Option<RegisterId>;
+ fn return_hi_int_reg(&self) -> Option<RegisterId>;
+ fn return_float_reg(&self) -> Option<RegisterId>;
- fn global_pointer_reg(&self) -> Option<<Self::Arch as Architecture>::Register>;
+ fn global_pointer_reg(&self) -> Option<RegisterId>;
- fn implicitly_defined_registers(&self) -> Vec<<Self::Arch as Architecture>::Register>;
+ fn implicitly_defined_registers(&self) -> Vec<RegisterId>;
fn are_argument_registers_used_for_var_args(&self) -> bool;
}
-pub fn register_calling_convention<A, N, C>(arch: &A, name: N, cc: C) -> Ref<CallingConvention<A>>
+pub fn register_calling_convention<A, N, C>(arch: &A, name: N, cc: C) -> Ref<CoreCallingConvention>
where
A: Architecture,
N: BnStrCompatible,
- C: 'static + CallingConventionBase<Arch = A>,
+ C: 'static + CallingConvention,
{
struct CustomCallingConventionContext<C>
where
- C: CallingConventionBase,
+ C: CallingConvention,
{
raw_handle: *mut BNCallingConvention,
cc: C,
}
+ // TODO: It would be nice if these callbacks were moved out to the bottom of this file (maybe in another mod)
extern "C" fn cb_free<C>(ctxt: *mut c_void)
where
- C: CallingConventionBase,
+ C: CallingConvention,
{
ffi_wrap!("CallingConvention::free", unsafe {
let _ctxt = Box::from_raw(ctxt as *mut CustomCallingConventionContext<C>);
@@ -86,14 +84,15 @@ where
if regs.is_null() {
return;
}
-
- let _regs = Box::from_raw(ptr::slice_from_raw_parts_mut(regs, count));
+
+ let regs_ptr = std::ptr::slice_from_raw_parts_mut(regs, count);
+ let _regs = Box::from_raw(regs_ptr);
})
}
extern "C" fn cb_caller_saved<C>(ctxt: *mut c_void, count: *mut usize) -> *mut u32
where
- C: CallingConventionBase,
+ C: CallingConvention,
{
ffi_wrap!("CallingConvention::caller_saved_registers", unsafe {
let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
@@ -101,20 +100,20 @@ where
.cc
.caller_saved_registers()
.iter()
- .map(|r| r.id())
+ .map(|r| r.0)
.collect();
// SAFETY: `count` is an out parameter
*count = regs.len();
let regs_ptr = regs.as_mut_ptr();
- mem::forget(regs);
+ std::mem::forget(regs);
regs_ptr
})
}
extern "C" fn cb_callee_saved<C>(ctxt: *mut c_void, count: *mut usize) -> *mut u32
where
- C: CallingConventionBase,
+ C: CallingConvention,
{
ffi_wrap!("CallingConvention::callee_saved_registers", unsafe {
let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
@@ -122,57 +121,52 @@ where
.cc
.callee_saved_registers()
.iter()
- .map(|r| r.id())
+ .map(|r| r.0)
.collect();
// SAFETY: `count` is an out parameter
*count = regs.len();
let regs_ptr = regs.as_mut_ptr();
- mem::forget(regs);
+ std::mem::forget(regs);
regs_ptr
})
}
extern "C" fn cb_int_args<C>(ctxt: *mut c_void, count: *mut usize) -> *mut u32
where
- C: CallingConventionBase,
+ C: CallingConvention,
{
ffi_wrap!("CallingConvention::int_arg_registers", unsafe {
let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
- let mut regs: Vec<_> = ctxt.cc.int_arg_registers().iter().map(|r| r.id()).collect();
+ let mut regs: Vec<_> = ctxt.cc.int_arg_registers().iter().map(|r| r.0).collect();
// SAFETY: `count` is an out parameter
*count = regs.len();
let regs_ptr = regs.as_mut_ptr();
- mem::forget(regs);
+ std::mem::forget(regs);
regs_ptr
})
}
extern "C" fn cb_float_args<C>(ctxt: *mut c_void, count: *mut usize) -> *mut u32
where
- C: CallingConventionBase,
+ C: CallingConvention,
{
ffi_wrap!("CallingConvention::float_arg_registers", unsafe {
let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
- let mut regs: Vec<_> = ctxt
- .cc
- .float_arg_registers()
- .iter()
- .map(|r| r.id())
- .collect();
+ let mut regs: Vec<_> = ctxt.cc.float_arg_registers().iter().map(|r| r.0).collect();
// SAFETY: `count` is an out parameter
*count = regs.len();
let regs_ptr = regs.as_mut_ptr();
- mem::forget(regs);
+ std::mem::forget(regs);
regs_ptr
})
}
extern "C" fn cb_arg_shared_index<C>(ctxt: *mut c_void) -> bool
where
- C: CallingConventionBase,
+ C: CallingConvention,
{
ffi_wrap!("CallingConvention::arg_registers_shared_index", unsafe {
let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
@@ -183,7 +177,7 @@ where
extern "C" fn cb_stack_reserved_arg_regs<C>(ctxt: *mut c_void) -> bool
where
- C: CallingConventionBase,
+ C: CallingConvention,
{
ffi_wrap!(
"CallingConvention::reserved_stack_space_for_arg_registers",
@@ -197,7 +191,7 @@ where
extern "C" fn cb_stack_adjusted_on_return<C>(ctxt: *mut c_void) -> bool
where
- C: CallingConventionBase,
+ C: CallingConvention,
{
ffi_wrap!("CallingConvention::stack_adjusted_on_return", unsafe {
let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
@@ -208,7 +202,7 @@ where
extern "C" fn cb_is_eligible_for_heuristics<C>(ctxt: *mut c_void) -> bool
where
- C: CallingConventionBase,
+ C: CallingConvention,
{
ffi_wrap!("CallingConvention::is_eligible_for_heuristics", unsafe {
let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
@@ -219,13 +213,13 @@ where
extern "C" fn cb_return_int_reg<C>(ctxt: *mut c_void) -> u32
where
- C: CallingConventionBase,
+ C: CallingConvention,
{
ffi_wrap!("CallingConvention::return_int_reg", unsafe {
let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
match ctxt.cc.return_int_reg() {
- Some(r) => r.id(),
+ Some(r) => r.0,
_ => 0xffff_ffff,
}
})
@@ -233,13 +227,13 @@ where
extern "C" fn cb_return_hi_int_reg<C>(ctxt: *mut c_void) -> u32
where
- C: CallingConventionBase,
+ C: CallingConvention,
{
ffi_wrap!("CallingConvention::return_hi_int_reg", unsafe {
let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
match ctxt.cc.return_hi_int_reg() {
- Some(r) => r.id(),
+ Some(r) => r.0,
_ => 0xffff_ffff,
}
})
@@ -247,13 +241,13 @@ where
extern "C" fn cb_return_float_reg<C>(ctxt: *mut c_void) -> u32
where
- C: CallingConventionBase,
+ C: CallingConvention,
{
ffi_wrap!("CallingConvention::return_float_reg", unsafe {
let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
match ctxt.cc.return_float_reg() {
- Some(r) => r.id(),
+ Some(r) => r.0,
_ => 0xffff_ffff,
}
})
@@ -261,13 +255,13 @@ where
extern "C" fn cb_global_pointer_reg<C>(ctxt: *mut c_void) -> u32
where
- C: CallingConventionBase,
+ C: CallingConvention,
{
ffi_wrap!("CallingConvention::global_pointer_reg", unsafe {
let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
match ctxt.cc.global_pointer_reg() {
- Some(r) => r.id(),
+ Some(r) => r.0,
_ => 0xffff_ffff,
}
})
@@ -278,7 +272,7 @@ where
count: *mut usize,
) -> *mut u32
where
- C: CallingConventionBase,
+ C: CallingConvention,
{
ffi_wrap!("CallingConvention::implicitly_defined_registers", unsafe {
let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
@@ -286,26 +280,27 @@ where
.cc
.implicitly_defined_registers()
.iter()
- .map(|r| r.id())
+ .map(|r| r.0)
.collect();
// SAFETY: `count` is an out parameter
*count = regs.len();
let regs_ptr = regs.as_mut_ptr();
- mem::forget(regs);
+ std::mem::forget(regs);
regs_ptr
})
}
- #[allow(clippy::extra_unused_type_parameters)] // TODO : This is bad; need to finish this stub
+ #[allow(clippy::extra_unused_type_parameters)]
extern "C" fn cb_incoming_reg_value<C>(
_ctxt: *mut c_void,
_reg: u32,
_func: *mut BNFunction,
val: *mut BNRegisterValue,
) where
- C: CallingConventionBase,
+ C: CallingConvention,
{
+ // TODO: This is bad; need to finish this stub
ffi_wrap!("CallingConvention::incoming_reg_value", unsafe {
//let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
let val = &mut *val;
@@ -315,15 +310,16 @@ where
})
}
- #[allow(clippy::extra_unused_type_parameters)] // TODO : This is bad; need to finish this stub
+ #[allow(clippy::extra_unused_type_parameters)]
extern "C" fn cb_incoming_flag_value<C>(
_ctxt: *mut c_void,
_flag: u32,
_func: *mut BNFunction,
val: *mut BNRegisterValue,
) where
- C: CallingConventionBase,
+ C: CallingConvention,
{
+ // TODO: This is bad; need to finish this stub
ffi_wrap!("CallingConvention::incoming_flag_value", unsafe {
//let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
let val = &mut *val;
@@ -339,11 +335,11 @@ where
_func: *mut BNFunction,
param: *mut BNVariable,
) where
- C: CallingConventionBase,
+ C: CallingConvention,
{
ffi_wrap!("CallingConvention::incoming_var_for_param", unsafe {
let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
- ptr::write(
+ std::ptr::write(
param,
BNGetDefaultIncomingVariableForParameterVariable(ctxt.raw_handle, var),
);
@@ -356,11 +352,11 @@ where
_func: *mut BNFunction,
param: *mut BNVariable,
) where
- C: CallingConventionBase,
+ C: CallingConvention,
{
ffi_wrap!("CallingConvention::incoming_param_for_var", unsafe {
let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
- ptr::write(
+ std::ptr::write(
param,
BNGetDefaultParameterVariableForIncomingVariable(ctxt.raw_handle, var),
);
@@ -369,7 +365,7 @@ where
extern "C" fn cb_are_argument_registers_used_for_var_args<C>(ctxt: *mut c_void) -> bool
where
- C: CallingConventionBase,
+ C: CallingConvention,
{
ffi_wrap!(
"CallingConvention::are_argument_registers_used_for_var_args",
@@ -383,7 +379,7 @@ where
let name = name.into_bytes_with_nul();
let raw = Box::into_raw(Box::new(CustomCallingConventionContext {
- raw_handle: ptr::null_mut(),
+ raw_handle: std::ptr::null_mut(),
cc,
}));
let mut cc = BNCustomCallingConvention {
@@ -418,40 +414,44 @@ where
unsafe {
let cc_name = name.as_ref().as_ptr() as *mut _;
- let result = BNCreateCallingConvention(arch.as_ref().0, cc_name, &mut cc);
+ let result = BNCreateCallingConvention(arch.as_ref().handle, cc_name, &mut cc);
assert!(!result.is_null());
(*raw).raw_handle = result;
- BNRegisterCallingConvention(arch.as_ref().0, result);
+ BNRegisterCallingConvention(arch.as_ref().handle, result);
- Ref::new(CallingConvention {
+ Ref::new(CoreCallingConvention {
handle: result,
- arch_handle: arch.handle(),
- _arch: PhantomData,
+ arch_handle: arch.as_ref().handle(),
})
}
}
-pub struct CallingConvention<A: Architecture> {
+pub struct CoreCallingConvention {
pub(crate) handle: *mut BNCallingConvention,
- pub(crate) arch_handle: A::Handle,
- _arch: PhantomData<*mut A>,
+ pub(crate) arch_handle: CoreArchitecture,
}
-unsafe impl<A: Architecture> Send for CallingConvention<A> {}
-unsafe impl<A: Architecture> Sync for CallingConvention<A> {}
+impl CoreCallingConvention {
+ pub(crate) unsafe fn from_raw(
+ handle: *mut BNCallingConvention,
+ arch: CoreArchitecture,
+ ) -> Self {
+ CoreCallingConvention {
+ handle,
+ arch_handle: arch,
+ }
+ }
-impl<A: Architecture> CallingConvention<A> {
pub(crate) unsafe fn ref_from_raw(
handle: *mut BNCallingConvention,
- arch: A::Handle,
+ arch: CoreArchitecture,
) -> Ref<Self> {
- Ref::new(CallingConvention {
+ Ref::new(CoreCallingConvention {
handle,
arch_handle: arch,
- _arch: PhantomData,
})
}
@@ -462,37 +462,22 @@ impl<A: Architecture> CallingConvention<A> {
pub fn variables_for_parameters(
&self,
params: &[FunctionParameter],
- permitted_registers: Option<&[A::Register]>,
+ permitted_registers: Option<&[CoreRegister]>,
) -> Vec<Variable> {
- let mut bn_params: Vec<BNFunctionParameter> = vec![];
- let name_strings = params.iter().map(|parameter| &parameter.name);
-
- for (parameter, raw_name) in params.iter().zip(name_strings) {
- let location = match &parameter.location {
- Some(location) => location.raw(),
- None => unsafe { mem::zeroed() },
- };
- bn_params.push(BNFunctionParameter {
- name: BnString::new(raw_name).into_raw(),
- type_: parameter.t.contents.handle,
- typeConfidence: parameter.t.confidence,
- defaultLocation: parameter.location.is_none(),
- location,
- });
- }
-
let mut count: usize = 0;
- let vars: *mut BNVariable = if let Some(permitted_args) = permitted_registers {
- let mut permitted_regs = vec![];
- for r in permitted_args {
- permitted_regs.push(r.id());
- }
+ let raw_params: Vec<BNFunctionParameter> = params
+ .iter()
+ .cloned()
+ .map(FunctionParameter::into_raw)
+ .collect();
+ let raw_vars_ptr: *mut BNVariable = if let Some(permitted_args) = permitted_registers {
+ let permitted_regs = permitted_args.iter().map(|r| r.id().0).collect::<Vec<_>>();
unsafe {
BNGetVariablesForParameters(
self.handle,
- bn_params.as_ptr(),
- bn_params.len(),
+ raw_params.as_ptr(),
+ raw_params.len(),
permitted_regs.as_ptr(),
permitted_regs.len(),
&mut count,
@@ -502,120 +487,129 @@ impl<A: Architecture> CallingConvention<A> {
unsafe {
BNGetVariablesForParametersDefaultPermittedArgs(
self.handle,
- bn_params.as_ptr(),
- bn_params.len(),
+ raw_params.as_ptr(),
+ raw_params.len(),
&mut count,
)
}
};
- let vars_slice = unsafe { slice::from_raw_parts(vars, count) };
- let mut result = vec![];
- for var in vars_slice {
- result.push(unsafe { Variable::from_raw(*var) });
+ for raw_param in raw_params {
+ FunctionParameter::free_raw(raw_param);
}
- unsafe { BNFreeVariableList(vars) };
- result
+ unsafe { Array::<Variable>::new(raw_vars_ptr, count, ()) }.to_vec()
}
}
-impl<A: Architecture> Eq for CallingConvention<A> {}
-impl<A: Architecture> PartialEq for CallingConvention<A> {
+unsafe impl Send for CoreCallingConvention {}
+unsafe impl Sync for CoreCallingConvention {}
+
+impl Eq for CoreCallingConvention {}
+impl PartialEq for CoreCallingConvention {
fn eq(&self, rhs: &Self) -> bool {
self.handle == rhs.handle
}
}
-use crate::types::{FunctionParameter, Variable};
-use std::hash::{Hash, Hasher};
+impl Debug for CoreCallingConvention {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("CoreCallingConvention")
+ .field("name", &self.name())
+ .field("caller_saved_registers", &self.caller_saved_registers())
+ .field("callee_saved_registers", &self.callee_saved_registers())
+ .field("int_arg_registers", &self.int_arg_registers())
+ .field("float_arg_registers", &self.float_arg_registers())
+ .field(
+ "arg_registers_shared_index",
+ &self.arg_registers_shared_index(),
+ )
+ .field(
+ "reserved_stack_space_for_arg_registers",
+ &self.reserved_stack_space_for_arg_registers(),
+ )
+ .field("stack_adjusted_on_return", &self.stack_adjusted_on_return())
+ .field(
+ "is_eligible_for_heuristics",
+ &self.is_eligible_for_heuristics(),
+ )
+ .field("return_int_reg", &self.return_int_reg())
+ .field("return_hi_int_reg", &self.return_hi_int_reg())
+ .field("return_float_reg", &self.return_float_reg())
+ .field("global_pointer_reg", &self.global_pointer_reg())
+ .field(
+ "implicitly_defined_registers",
+ &self.implicitly_defined_registers(),
+ )
+ .field(
+ "are_argument_registers_used_for_var_args",
+ &self.are_argument_registers_used_for_var_args(),
+ )
+ .finish()
+ }
+}
-impl<A: Architecture> Hash for CallingConvention<A> {
+impl Hash for CoreCallingConvention {
fn hash<H: Hasher>(&self, state: &mut H) {
self.handle.hash(state);
}
}
-impl<A: Architecture> CallingConventionBase for CallingConvention<A> {
- type Arch = A;
-
- fn caller_saved_registers(&self) -> Vec<A::Register> {
+impl CallingConvention for CoreCallingConvention {
+ fn caller_saved_registers(&self) -> Vec<RegisterId> {
unsafe {
let mut count = 0;
- let regs = BNGetCallerSavedRegisters(self.handle, &mut count);
- let arch = self.arch_handle.borrow();
-
- let res = slice::from_raw_parts(regs, count)
+ let regs_ptr = BNGetCallerSavedRegisters(self.handle, &mut count);
+ let regs: Vec<RegisterId> = std::slice::from_raw_parts(regs_ptr, count)
.iter()
- .map(|&r| {
- arch.register_from_id(r)
- .expect("bad reg id from CallingConvention")
- })
+ .copied()
+ .map(RegisterId::from)
.collect();
-
- BNFreeRegisterList(regs);
-
- res
+ BNFreeRegisterList(regs_ptr);
+ regs
}
}
- fn callee_saved_registers(&self) -> Vec<A::Register> {
+ fn callee_saved_registers(&self) -> Vec<RegisterId> {
unsafe {
let mut count = 0;
- let regs = BNGetCalleeSavedRegisters(self.handle, &mut count);
- let arch = self.arch_handle.borrow();
-
- let res = slice::from_raw_parts(regs, count)
+ let regs_ptr = BNGetCalleeSavedRegisters(self.handle, &mut count);
+ BNFreeRegisterList(regs_ptr);
+ let regs: Vec<RegisterId> = std::slice::from_raw_parts(regs_ptr, count)
.iter()
- .map(|&r| {
- arch.register_from_id(r)
- .expect("bad reg id from CallingConvention")
- })
+ .copied()
+ .map(RegisterId::from)
.collect();
-
- BNFreeRegisterList(regs);
-
- res
+ BNFreeRegisterList(regs_ptr);
+ regs
}
}
- fn int_arg_registers(&self) -> Vec<A::Register> {
+ fn int_arg_registers(&self) -> Vec<RegisterId> {
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)
+ let regs_ptr = BNGetIntegerArgumentRegisters(self.handle, &mut count);
+ let regs: Vec<RegisterId> = std::slice::from_raw_parts(regs_ptr, count)
.iter()
- .map(|&r| {
- arch.register_from_id(r)
- .expect("bad reg id from CallingConvention")
- })
+ .copied()
+ .map(RegisterId::from)
.collect();
-
- BNFreeRegisterList(regs);
-
- res
+ BNFreeRegisterList(regs_ptr);
+ regs
}
}
- fn float_arg_registers(&self) -> Vec<A::Register> {
+ fn float_arg_registers(&self) -> Vec<RegisterId> {
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)
+ let regs_ptr = BNGetFloatArgumentRegisters(self.handle, &mut count);
+ let regs: Vec<RegisterId> = std::slice::from_raw_parts(regs_ptr, count)
.iter()
- .map(|&r| {
- arch.register_from_id(r)
- .expect("bad reg id from CallingConvention")
- })
+ .copied()
+ .map(RegisterId::from)
.collect();
-
- BNFreeRegisterList(regs);
-
- res
+ BNFreeRegisterList(regs_ptr);
+ regs
}
}
@@ -632,39 +626,65 @@ impl<A: Architecture> CallingConventionBase for CallingConvention<A> {
}
fn is_eligible_for_heuristics(&self) -> bool {
- false
+ unsafe { BNIsEligibleForHeuristics(self.handle) }
}
- fn return_int_reg(&self) -> Option<A::Register> {
+ fn return_int_reg(&self) -> Option<RegisterId> {
match unsafe { BNGetIntegerReturnValueRegister(self.handle) } {
- id if id < 0x8000_0000 => self.arch_handle.borrow().register_from_id(id),
+ id if id < 0x8000_0000 => self
+ .arch_handle
+ .borrow()
+ .register_from_id(RegisterId(id))
+ .map(|r| r.id()),
_ => None,
}
}
- fn return_hi_int_reg(&self) -> Option<A::Register> {
+ fn return_hi_int_reg(&self) -> Option<RegisterId> {
match unsafe { BNGetHighIntegerReturnValueRegister(self.handle) } {
- id if id < 0x8000_0000 => self.arch_handle.borrow().register_from_id(id),
+ id if id < 0x8000_0000 => self
+ .arch_handle
+ .borrow()
+ .register_from_id(RegisterId(id))
+ .map(|r| r.id()),
_ => None,
}
}
- fn return_float_reg(&self) -> Option<A::Register> {
+ fn return_float_reg(&self) -> Option<RegisterId> {
match unsafe { BNGetFloatReturnValueRegister(self.handle) } {
- id if id < 0x8000_0000 => self.arch_handle.borrow().register_from_id(id),
+ id if id < 0x8000_0000 => self
+ .arch_handle
+ .borrow()
+ .register_from_id(RegisterId(id))
+ .map(|r| r.id()),
_ => None,
}
}
- fn global_pointer_reg(&self) -> Option<A::Register> {
+ fn global_pointer_reg(&self) -> Option<RegisterId> {
match unsafe { BNGetGlobalPointerRegister(self.handle) } {
- id if id < 0x8000_0000 => self.arch_handle.borrow().register_from_id(id),
+ id if id < 0x8000_0000 => self
+ .arch_handle
+ .borrow()
+ .register_from_id(RegisterId(id))
+ .map(|r| r.id()),
_ => None,
}
}
- fn implicitly_defined_registers(&self) -> Vec<A::Register> {
- Vec::new()
+ fn implicitly_defined_registers(&self) -> Vec<RegisterId> {
+ unsafe {
+ let mut count = 0;
+ let regs_ptr = BNGetImplicitlyDefinedRegisters(self.handle, &mut count);
+ let regs: Vec<RegisterId> = std::slice::from_raw_parts(regs_ptr, count)
+ .iter()
+ .copied()
+ .map(RegisterId::from)
+ .collect();
+ BNFreeRegisterList(regs_ptr);
+ regs
+ }
}
fn are_argument_registers_used_for_var_args(&self) -> bool {
@@ -672,7 +692,7 @@ impl<A: Architecture> CallingConventionBase for CallingConvention<A> {
}
}
-impl<A: Architecture> ToOwned for CallingConvention<A> {
+impl ToOwned for CoreCallingConvention {
type Owned = Ref<Self>;
fn to_owned(&self) -> Self::Owned {
@@ -680,12 +700,11 @@ impl<A: Architecture> ToOwned for CallingConvention<A> {
}
}
-unsafe impl<A: Architecture> RefCountable for CallingConvention<A> {
+unsafe impl RefCountable for CoreCallingConvention {
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
Ref::new(Self {
handle: BNNewCallingConventionReference(handle.handle),
- arch_handle: handle.arch_handle.clone(),
- _arch: PhantomData,
+ arch_handle: handle.arch_handle,
})
}
@@ -694,52 +713,46 @@ unsafe impl<A: Architecture> RefCountable for CallingConvention<A> {
}
}
-impl<A: Architecture> CoreArrayProvider for CallingConvention<A> {
+impl CoreArrayProvider for CoreCallingConvention {
type Raw = *mut BNCallingConvention;
- type Context = A::Handle;
- type Wrapped<'a> = Guard<'a, CallingConvention<A>>;
+ type Context = CoreArchitecture;
+ type Wrapped<'a> = Guard<'a, CoreCallingConvention>;
}
-unsafe impl<A: Architecture> CoreArrayProviderInner for CallingConvention<A> {
+unsafe impl CoreArrayProviderInner for CoreCallingConvention {
unsafe fn free(raw: *mut *mut BNCallingConvention, count: usize, _content: &Self::Context) {
BNFreeCallingConventionList(raw, count);
}
+
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
Guard::new(
- CallingConvention {
+ CoreCallingConvention {
handle: *raw,
- arch_handle: context.clone(),
- _arch: Default::default(),
+ arch_handle: *context,
},
context,
)
}
}
-impl Debug for CallingConvention<CoreArchitecture> {
- fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
- write!(f, "<cc: {} arch: {}>", self.name(), self.arch_handle.name())
- }
-}
-
pub struct ConventionBuilder<A: Architecture> {
- caller_saved_registers: Vec<A::Register>,
- callee_saved_registers: Vec<A::Register>,
- int_arg_registers: Vec<A::Register>,
- float_arg_registers: Vec<A::Register>,
+ caller_saved_registers: Vec<RegisterId>,
+ callee_saved_registers: Vec<RegisterId>,
+ int_arg_registers: Vec<RegisterId>,
+ float_arg_registers: Vec<RegisterId>,
arg_registers_shared_index: bool,
reserved_stack_space_for_arg_registers: bool,
stack_adjusted_on_return: bool,
is_eligible_for_heuristics: bool,
- return_int_reg: Option<A::Register>,
- return_hi_int_reg: Option<A::Register>,
- return_float_reg: Option<A::Register>,
+ return_int_reg: Option<RegisterId>,
+ return_hi_int_reg: Option<RegisterId>,
+ return_float_reg: Option<RegisterId>,
- global_pointer_reg: Option<A::Register>,
+ global_pointer_reg: Option<RegisterId>,
- implicitly_defined_registers: Vec<A::Register>,
+ implicitly_defined_registers: Vec<RegisterId>,
are_argument_registers_used_for_var_args: bool,
@@ -747,9 +760,6 @@ pub struct ConventionBuilder<A: Architecture> {
_arch: PhantomData<*const A>,
}
-unsafe impl<A: Architecture> Send for ConventionBuilder<A> {}
-unsafe impl<A: Architecture> Sync for ConventionBuilder<A> {}
-
macro_rules! bool_arg {
($name:ident) => {
pub fn $name(mut self, val: bool) -> Self {
@@ -765,7 +775,10 @@ macro_rules! reg_list {
{
// FIXME NLL
let arch = self.arch_handle.borrow();
- let arch_regs = regs.iter().filter_map(|&r| arch.register_by_name(r));
+ let arch_regs = regs
+ .iter()
+ .filter_map(|&r| arch.register_by_name(r))
+ .map(|r| r.id());
self.$name = arch_regs.collect();
}
@@ -781,7 +794,7 @@ macro_rules! reg {
{
// FIXME NLL
let arch = self.arch_handle.borrow();
- self.$name = arch.register_by_name(reg);
+ self.$name = arch.register_by_name(reg).map(|r| r.id());
}
self
@@ -837,29 +850,26 @@ impl<A: Architecture> ConventionBuilder<A> {
bool_arg!(are_argument_registers_used_for_var_args);
- pub fn register(self, name: &str) -> Ref<CallingConvention<A>> {
+ pub fn register(self, name: &str) -> Ref<CoreCallingConvention> {
let arch = self.arch_handle.clone();
-
register_calling_convention(arch.borrow(), name, self)
}
}
-impl<A: Architecture> CallingConventionBase for ConventionBuilder<A> {
- type Arch = A;
-
- fn caller_saved_registers(&self) -> Vec<A::Register> {
+impl<A: Architecture> CallingConvention for ConventionBuilder<A> {
+ fn caller_saved_registers(&self) -> Vec<RegisterId> {
self.caller_saved_registers.clone()
}
- fn callee_saved_registers(&self) -> Vec<A::Register> {
+ fn callee_saved_registers(&self) -> Vec<RegisterId> {
self.callee_saved_registers.clone()
}
- fn int_arg_registers(&self) -> Vec<A::Register> {
+ fn int_arg_registers(&self) -> Vec<RegisterId> {
self.int_arg_registers.clone()
}
- fn float_arg_registers(&self) -> Vec<A::Register> {
+ fn float_arg_registers(&self) -> Vec<RegisterId> {
self.float_arg_registers.clone()
}
@@ -879,23 +889,23 @@ impl<A: Architecture> CallingConventionBase for ConventionBuilder<A> {
self.is_eligible_for_heuristics
}
- fn return_int_reg(&self) -> Option<A::Register> {
+ fn return_int_reg(&self) -> Option<RegisterId> {
self.return_int_reg
}
- fn return_hi_int_reg(&self) -> Option<A::Register> {
+ fn return_hi_int_reg(&self) -> Option<RegisterId> {
self.return_hi_int_reg
}
- fn return_float_reg(&self) -> Option<A::Register> {
+ fn return_float_reg(&self) -> Option<RegisterId> {
self.return_float_reg
}
- fn global_pointer_reg(&self) -> Option<A::Register> {
+ fn global_pointer_reg(&self) -> Option<RegisterId> {
self.global_pointer_reg
}
- fn implicitly_defined_registers(&self) -> Vec<A::Register> {
+ fn implicitly_defined_registers(&self) -> Vec<RegisterId> {
self.implicitly_defined_registers.clone()
}
@@ -903,3 +913,6 @@ impl<A: Architecture> CallingConventionBase for ConventionBuilder<A> {
self.are_argument_registers_used_for_var_args
}
}
+
+unsafe impl<A: Architecture> Send for ConventionBuilder<A> {}
+unsafe impl<A: Architecture> Sync for ConventionBuilder<A> {}
diff --git a/rust/src/collaboration.rs b/rust/src/collaboration.rs
new file mode 100644
index 00000000..1dce8104
--- /dev/null
+++ b/rust/src/collaboration.rs
@@ -0,0 +1,153 @@
+//! The collaboration API is **unstable** and as such will undergo breaking changes in the near future!
+
+mod changeset;
+mod file;
+mod folder;
+mod group;
+mod merge;
+mod permission;
+mod project;
+mod remote;
+mod snapshot;
+mod sync;
+mod undo;
+mod user;
+
+pub use changeset::*;
+pub use file::*;
+pub use folder::*;
+pub use group::*;
+pub use merge::*;
+pub use permission::*;
+pub use project::*;
+pub use remote::*;
+pub use snapshot::*;
+use std::ffi::c_char;
+use std::ptr::NonNull;
+pub use sync::*;
+pub use user::*;
+
+use binaryninjacore_sys::*;
+
+use crate::rc::{Array, Ref};
+use crate::string::{BnStrCompatible, BnString};
+
+// TODO: Should we pull metadata and information required to call a function? Or should we add documentation
+// TODO: on what functions need to have been called prior? I feel like we should make the user have to pull
+// TODO: the data because they have a greater understanding of where the function is being called from.
+
+/// Get the single actively connected Remote (for ux simplification), if any
+pub fn active_remote() -> Option<Ref<Remote>> {
+ let value = unsafe { BNCollaborationGetActiveRemote() };
+ NonNull::new(value).map(|h| unsafe { Remote::ref_from_raw(h) })
+}
+
+/// Set the single actively connected Remote
+pub fn set_active_remote(remote: Option<&Remote>) {
+ let remote_ptr = remote.map_or(std::ptr::null_mut(), |r| r.handle.as_ptr());
+ unsafe { BNCollaborationSetActiveRemote(remote_ptr) }
+}
+
+/// Load the list of known Remotes from local Settings
+pub fn load_remotes() -> Result<(), ()> {
+ let success = unsafe { BNCollaborationLoadRemotes() };
+ success.then_some(()).ok_or(())
+}
+
+/// List of known/connected Remotes
+pub fn known_remotes() -> Array<Remote> {
+ let mut count = 0;
+ let value = unsafe { BNCollaborationGetRemotes(&mut count) };
+ assert!(!value.is_null());
+ unsafe { Array::new(value, count, ()) }
+}
+
+/// Get Remote by unique `id`
+pub fn get_remote_by_id<S: BnStrCompatible>(id: S) -> Option<Ref<Remote>> {
+ let id = id.into_bytes_with_nul();
+ let value = unsafe { BNCollaborationGetRemoteById(id.as_ref().as_ptr() as *const c_char) };
+ NonNull::new(value).map(|h| unsafe { Remote::ref_from_raw(h) })
+}
+
+/// Get Remote by `address`
+pub fn get_remote_by_address<S: BnStrCompatible>(address: S) -> Option<Ref<Remote>> {
+ let address = address.into_bytes_with_nul();
+ let value =
+ unsafe { BNCollaborationGetRemoteByAddress(address.as_ref().as_ptr() as *const c_char) };
+ NonNull::new(value).map(|h| unsafe { Remote::ref_from_raw(h) })
+}
+
+/// Get Remote by `name`
+pub fn get_remote_by_name<S: BnStrCompatible>(name: S) -> Option<Ref<Remote>> {
+ let name = name.into_bytes_with_nul();
+ let value = unsafe { BNCollaborationGetRemoteByName(name.as_ref().as_ptr() as *const c_char) };
+ NonNull::new(value).map(|h| unsafe { Remote::ref_from_raw(h) })
+}
+
+/// Remove a Remote from the list of known remotes (saved to Settings)
+pub fn remove_known_remote(remote: &Remote) {
+ unsafe { BNCollaborationRemoveRemote(remote.handle.as_ptr()) }
+}
+
+/// Save the list of known Remotes to local Settings
+pub fn save_remotes() {
+ unsafe { BNCollaborationSaveRemotes() }
+}
+
+pub fn store_data_in_keychain<K, I, DK, DV>(key: K, data: I) -> bool
+where
+ K: BnStrCompatible,
+ I: IntoIterator<Item = (DK, DV)>,
+ DK: BnStrCompatible,
+ DV: BnStrCompatible,
+{
+ let key = key.into_bytes_with_nul();
+ let (data_keys, data_values): (Vec<DK::Result>, Vec<DV::Result>) = data
+ .into_iter()
+ .map(|(k, v)| (k.into_bytes_with_nul(), v.into_bytes_with_nul()))
+ .unzip();
+ let data_keys_ptr: Box<[*const c_char]> = data_keys
+ .iter()
+ .map(|k| k.as_ref().as_ptr() as *const c_char)
+ .collect();
+ let data_values_ptr: Box<[*const c_char]> = data_values
+ .iter()
+ .map(|v| v.as_ref().as_ptr() as *const c_char)
+ .collect();
+ unsafe {
+ BNCollaborationStoreDataInKeychain(
+ key.as_ref().as_ptr() as *const c_char,
+ data_keys_ptr.as_ptr() as *mut _,
+ data_values_ptr.as_ptr() as *mut _,
+ data_keys.len(),
+ )
+ }
+}
+
+pub fn has_data_in_keychain<K: BnStrCompatible>(key: K) -> bool {
+ let key = key.into_bytes_with_nul();
+ unsafe { BNCollaborationHasDataInKeychain(key.as_ref().as_ptr() as *const c_char) }
+}
+
+pub fn get_data_from_keychain<K: BnStrCompatible>(
+ key: K,
+) -> Option<(Array<BnString>, Array<BnString>)> {
+ let key = key.into_bytes_with_nul();
+ let mut keys = std::ptr::null_mut();
+ let mut values = std::ptr::null_mut();
+ let count = unsafe {
+ BNCollaborationGetDataFromKeychain(
+ key.as_ref().as_ptr() as *const c_char,
+ &mut keys,
+ &mut values,
+ )
+ };
+ let keys = (!keys.is_null()).then(|| unsafe { Array::new(keys, count, ()) });
+ let values = (!values.is_null()).then(|| unsafe { Array::new(values, count, ()) });
+ keys.zip(values)
+}
+
+pub fn delete_data_from_keychain<K: BnStrCompatible>(key: K) -> bool {
+ let key = key.into_bytes_with_nul();
+ unsafe { BNCollaborationDeleteDataFromKeychain(key.as_ref().as_ptr() as *const c_char) }
+}
diff --git a/rust/src/collaboration/changeset.rs b/rust/src/collaboration/changeset.rs
new file mode 100644
index 00000000..9d7cdb7c
--- /dev/null
+++ b/rust/src/collaboration/changeset.rs
@@ -0,0 +1,116 @@
+use binaryninjacore_sys::*;
+use std::ffi::c_char;
+use std::ptr::NonNull;
+
+use super::{RemoteFile, RemoteUser};
+
+use crate::database::snapshot::SnapshotId;
+use crate::database::Database;
+use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
+use crate::string::{BnStrCompatible, BnString};
+
+/// A collection of snapshots in a local database
+#[repr(transparent)]
+pub struct Changeset {
+ handle: NonNull<BNCollaborationChangeset>,
+}
+
+impl Changeset {
+ pub(crate) unsafe fn from_raw(handle: NonNull<BNCollaborationChangeset>) -> Self {
+ Self { handle }
+ }
+
+ #[allow(unused)]
+ pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNCollaborationChangeset>) -> Ref<Self> {
+ Ref::new(Self { handle })
+ }
+
+ /// Owning database for snapshots
+ pub fn database(&self) -> Result<Database, ()> {
+ let result = unsafe { BNCollaborationChangesetGetDatabase(self.handle.as_ptr()) };
+ let raw = NonNull::new(result).ok_or(())?;
+ Ok(unsafe { Database::from_raw(raw) })
+ }
+
+ /// Relevant remote File object
+ pub fn file(&self) -> Result<Ref<RemoteFile>, ()> {
+ let result = unsafe { BNCollaborationChangesetGetFile(self.handle.as_ptr()) };
+ NonNull::new(result)
+ .map(|raw| unsafe { RemoteFile::ref_from_raw(raw) })
+ .ok_or(())
+ }
+
+ /// List of snapshot ids in the database
+ pub fn snapshot_ids(&self) -> Result<Array<SnapshotId>, ()> {
+ let mut count = 0;
+ let result =
+ unsafe { BNCollaborationChangesetGetSnapshotIds(self.handle.as_ptr(), &mut count) };
+ (!result.is_null())
+ .then(|| unsafe { Array::new(result, count, ()) })
+ .ok_or(())
+ }
+
+ /// Relevant remote author User
+ pub fn author(&self) -> Result<Ref<RemoteUser>, ()> {
+ let result = unsafe { BNCollaborationChangesetGetAuthor(self.handle.as_ptr()) };
+ NonNull::new(result)
+ .map(|raw| unsafe { RemoteUser::ref_from_raw(raw) })
+ .ok_or(())
+ }
+
+ /// Changeset name
+ pub fn name(&self) -> BnString {
+ let result = unsafe { BNCollaborationChangesetGetName(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ /// Set the name of the changeset, e.g. in a name changeset function.
+ pub fn set_name<S: BnStrCompatible>(&self, value: S) -> bool {
+ let value = value.into_bytes_with_nul();
+ unsafe {
+ BNCollaborationChangesetSetName(
+ self.handle.as_ptr(),
+ value.as_ref().as_ptr() as *const c_char,
+ )
+ }
+ }
+}
+
+impl ToOwned for Changeset {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for Changeset {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: NonNull::new(BNNewCollaborationChangesetReference(handle.handle.as_ptr()))
+ .unwrap(),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeCollaborationChangeset(handle.handle.as_ptr());
+ }
+}
+
+impl CoreArrayProvider for Changeset {
+ type Raw = *mut BNCollaborationChangeset;
+ type Context = ();
+ type Wrapped<'a> = Guard<'a, Self>;
+}
+
+unsafe impl CoreArrayProviderInner for Changeset {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeCollaborationChangesetList(raw, count)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
+ let raw_ptr = NonNull::new(*raw).unwrap();
+ Guard::new(Self::from_raw(raw_ptr), context)
+ }
+}
diff --git a/rust/src/collaboration/file.rs b/rust/src/collaboration/file.rs
new file mode 100644
index 00000000..2651d3c7
--- /dev/null
+++ b/rust/src/collaboration/file.rs
@@ -0,0 +1,647 @@
+use std::ffi::{c_char, c_void};
+use std::fmt::{Debug, Formatter};
+use std::ptr::NonNull;
+use std::time::SystemTime;
+
+use binaryninjacore_sys::*;
+
+use super::{
+ sync, DatabaseConflictHandler, DatabaseConflictHandlerFail, NameChangeset, NoNameChangeset,
+ Remote, RemoteFolder, RemoteProject, RemoteSnapshot,
+};
+
+use crate::binary_view::{BinaryView, BinaryViewExt};
+use crate::database::Database;
+use crate::file_metadata::FileMetadata;
+use crate::progress::{NoProgressCallback, ProgressCallback, SplitProgressBuilder};
+use crate::project::file::ProjectFile;
+use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
+use crate::string::{BnStrCompatible, BnString};
+
+pub type RemoteFileType = BNRemoteFileType;
+
+/// A remote project file. It controls the various snapshots and raw file contents associated with the analysis.
+#[repr(transparent)]
+pub struct RemoteFile {
+ pub(crate) handle: NonNull<BNRemoteFile>,
+}
+
+impl RemoteFile {
+ pub(crate) unsafe fn from_raw(handle: NonNull<BNRemoteFile>) -> Self {
+ Self { handle }
+ }
+
+ pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNRemoteFile>) -> Ref<Self> {
+ Ref::new(Self { handle })
+ }
+
+ /// Look up the remote File for a local database, or None if there is no matching
+ /// remote File found.
+ /// See [RemoteFile::get_for_binary_view] to load from a [BinaryView].
+ pub fn get_for_local_database(database: &Database) -> Result<Option<Ref<RemoteFile>>, ()> {
+ // TODO: This sync should be removed?
+ if !sync::pull_files(database)? {
+ return Ok(None);
+ }
+ sync::get_remote_file_for_local_database(database)
+ }
+
+ /// Look up the [`RemoteFile`] for a local [`BinaryView`], or None if there is no matching
+ /// remote File found.
+ pub fn get_for_binary_view(bv: &BinaryView) -> Result<Option<Ref<RemoteFile>>, ()> {
+ let file = bv.file();
+ let Some(database) = file.database() else {
+ return Ok(None);
+ };
+ RemoteFile::get_for_local_database(&database)
+ }
+
+ pub fn core_file(&self) -> Result<ProjectFile, ()> {
+ let result = unsafe { BNRemoteFileGetCoreFile(self.handle.as_ptr()) };
+ NonNull::new(result)
+ .map(|handle| unsafe { ProjectFile::from_raw(handle) })
+ .ok_or(())
+ }
+
+ pub fn project(&self) -> Result<Ref<RemoteProject>, ()> {
+ let result = unsafe { BNRemoteFileGetProject(self.handle.as_ptr()) };
+ NonNull::new(result)
+ .map(|handle| unsafe { RemoteProject::ref_from_raw(handle) })
+ .ok_or(())
+ }
+
+ pub fn remote(&self) -> Result<Ref<Remote>, ()> {
+ let result = unsafe { BNRemoteFileGetRemote(self.handle.as_ptr()) };
+ NonNull::new(result)
+ .map(|handle| unsafe { Remote::ref_from_raw(handle) })
+ .ok_or(())
+ }
+
+ /// Parent folder, if one exists. None if this is in the root of the project.
+ pub fn folder(&self) -> Result<Option<Ref<RemoteFolder>>, ()> {
+ let project = self.project()?;
+ if !project.has_pulled_folders() {
+ project.pull_folders()?;
+ }
+ let result = unsafe { BNRemoteFileGetFolder(self.handle.as_ptr()) };
+ Ok(NonNull::new(result).map(|handle| unsafe { RemoteFolder::ref_from_raw(handle) }))
+ }
+
+ /// Set the parent folder of a file.
+ pub fn set_folder(&self, folder: Option<&RemoteFolder>) -> Result<(), ()> {
+ let folder_raw = folder.map_or(std::ptr::null_mut(), |f| f.handle.as_ptr());
+ let success = unsafe { BNRemoteFileSetFolder(self.handle.as_ptr(), folder_raw) };
+ success.then_some(()).ok_or(())
+ }
+
+ pub fn set_metadata<S: BnStrCompatible>(&self, folder: S) -> Result<(), ()> {
+ let folder_raw = folder.into_bytes_with_nul();
+ let success = unsafe {
+ BNRemoteFileSetMetadata(
+ self.handle.as_ptr(),
+ folder_raw.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ success.then_some(()).ok_or(())
+ }
+
+ /// Web API endpoint URL
+ pub fn url(&self) -> BnString {
+ let result = unsafe { BNRemoteFileGetUrl(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ /// Chat log API endpoint URL
+ pub fn chat_log_url(&self) -> BnString {
+ let result = unsafe { BNRemoteFileGetChatLogUrl(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ pub fn user_positions_url(&self) -> BnString {
+ let result = unsafe { BNRemoteFileGetUserPositionsUrl(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ /// Unique ID
+ pub fn id(&self) -> BnString {
+ let result = unsafe { BNRemoteFileGetId(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ /// All files share the same properties, but files with different types may make different
+ /// uses of those properties, or not use some of them at all.
+ pub fn file_type(&self) -> RemoteFileType {
+ unsafe { BNRemoteFileGetType(self.handle.as_ptr()) }
+ }
+
+ /// Created date of the file
+ pub fn created(&self) -> SystemTime {
+ let result = unsafe { BNRemoteFileGetCreated(self.handle.as_ptr()) };
+ crate::ffi::time_from_bn(result.try_into().unwrap())
+ }
+
+ pub fn created_by(&self) -> BnString {
+ let result = unsafe { BNRemoteFileGetCreatedBy(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ /// Last modified of the file
+ pub fn last_modified(&self) -> SystemTime {
+ let result = unsafe { BNRemoteFileGetLastModified(self.handle.as_ptr()) };
+ crate::ffi::time_from_bn(result.try_into().unwrap())
+ }
+
+ /// Date of last snapshot in the file
+ pub fn last_snapshot(&self) -> SystemTime {
+ let result = unsafe { BNRemoteFileGetLastSnapshot(self.handle.as_ptr()) };
+ crate::ffi::time_from_bn(result.try_into().unwrap())
+ }
+
+ /// Username of user who pushed the last snapshot in the file
+ pub fn last_snapshot_by(&self) -> BnString {
+ let result = unsafe { BNRemoteFileGetLastSnapshotBy(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ pub fn last_snapshot_name(&self) -> BnString {
+ let result = unsafe { BNRemoteFileGetLastSnapshotName(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ /// Hash of file contents (no algorithm guaranteed)
+ pub fn hash(&self) -> BnString {
+ let result = unsafe { BNRemoteFileGetHash(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ /// Displayed name of file
+ pub fn name(&self) -> BnString {
+ let result = unsafe { BNRemoteFileGetName(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ /// Set the description of the file. You will need to push the file to update the remote version.
+ pub fn set_name<S: BnStrCompatible>(&self, name: S) -> Result<(), ()> {
+ let name = name.into_bytes_with_nul();
+ let success = unsafe {
+ BNRemoteFileSetName(
+ self.handle.as_ptr(),
+ name.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ success.then_some(()).ok_or(())
+ }
+
+ /// Desciprtion of the file
+ pub fn description(&self) -> BnString {
+ let result = unsafe { BNRemoteFileGetDescription(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ /// Set the description of the file. You will need to push the file to update the remote version.
+ pub fn set_description<S: BnStrCompatible>(&self, description: S) -> Result<(), ()> {
+ let description = description.into_bytes_with_nul();
+ let success = unsafe {
+ BNRemoteFileSetDescription(
+ self.handle.as_ptr(),
+ description.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ success.then_some(()).ok_or(())
+ }
+
+ pub fn metadata(&self) -> BnString {
+ let result = unsafe { BNRemoteFileGetMetadata(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ /// Size of raw content of file, in bytes
+ pub fn size(&self) -> u64 {
+ unsafe { BNRemoteFileGetSize(self.handle.as_ptr()) }
+ }
+
+ /// Get the default filepath for a remote File. This is based off the Setting for
+ /// collaboration.directory, the file's id, the file's project's id, and the file's
+ /// remote's id.
+ pub fn default_path(&self) -> BnString {
+ let result = unsafe { BNCollaborationDefaultFilePath(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ /// If the file has pulled the snapshots yet
+ pub fn has_pulled_snapshots(&self) -> bool {
+ unsafe { BNRemoteFileHasPulledSnapshots(self.handle.as_ptr()) }
+ }
+
+ /// Get the list of snapshots in this file.
+ ///
+ /// NOTE: If snapshots have not been pulled, they will be pulled upon calling this.
+ pub fn snapshots(&self) -> Result<Array<RemoteSnapshot>, ()> {
+ // TODO: This sync should be removed?
+ if !self.has_pulled_snapshots() {
+ self.pull_snapshots()?;
+ }
+ let mut count = 0;
+ let result = unsafe { BNRemoteFileGetSnapshots(self.handle.as_ptr(), &mut count) };
+ (!result.is_null())
+ .then(|| unsafe { Array::new(result, count, ()) })
+ .ok_or(())
+ }
+
+ /// Get a specific Snapshot in the File by its id
+ ///
+ /// NOTE: If snapshots have not been pulled, they will be pulled upon calling this.
+ pub fn snapshot_by_id<S: BnStrCompatible>(
+ &self,
+ id: S,
+ ) -> Result<Option<Ref<RemoteSnapshot>>, ()> {
+ // TODO: This sync should be removed?
+ if !self.has_pulled_snapshots() {
+ self.pull_snapshots()?;
+ }
+ let id = id.into_bytes_with_nul();
+ let result = unsafe {
+ BNRemoteFileGetSnapshotById(self.handle.as_ptr(), id.as_ref().as_ptr() as *const c_char)
+ };
+ Ok(NonNull::new(result).map(|handle| unsafe { RemoteSnapshot::ref_from_raw(handle) }))
+ }
+
+ /// Pull the list of Snapshots from the Remote.
+ pub fn pull_snapshots(&self) -> Result<(), ()> {
+ self.pull_snapshots_with_progress(NoProgressCallback)
+ }
+
+ /// Pull the list of Snapshots from the Remote.
+ pub fn pull_snapshots_with_progress<P: ProgressCallback>(
+ &self,
+ mut progress: P,
+ ) -> Result<(), ()> {
+ let success = unsafe {
+ BNRemoteFilePullSnapshots(
+ self.handle.as_ptr(),
+ Some(P::cb_progress_callback),
+ &mut progress as *mut P as *mut c_void,
+ )
+ };
+ success.then_some(()).ok_or(())
+ }
+
+ /// Create a new snapshot on the remote (and pull it)
+ ///
+ /// * `name` - Snapshot name
+ /// * `contents` - Snapshot contents
+ /// * `analysis_cache_contents` - Contents of analysis cache of snapshot
+ /// * `file` - New file contents (if contents changed)
+ /// * `parent_ids` - List of ids of parent snapshots (or empty if this is a root snapshot)
+ pub fn create_snapshot<S, I>(
+ &self,
+ name: S,
+ contents: &mut [u8],
+ analysis_cache_contexts: &mut [u8],
+ file: &mut [u8],
+ parent_ids: I,
+ ) -> Result<Ref<RemoteSnapshot>, ()>
+ where
+ S: BnStrCompatible,
+ I: IntoIterator,
+ I::Item: BnStrCompatible,
+ {
+ self.create_snapshot_with_progress(
+ name,
+ contents,
+ analysis_cache_contexts,
+ file,
+ parent_ids,
+ NoProgressCallback,
+ )
+ }
+
+ /// Create a new snapshot on the remote (and pull it)
+ ///
+ /// * `name` - Snapshot name
+ /// * `contents` - Snapshot contents
+ /// * `analysis_cache_contents` - Contents of analysis cache of snapshot
+ /// * `file` - New file contents (if contents changed)
+ /// * `parent_ids` - List of ids of parent snapshots (or empty if this is a root snapshot)
+ /// * `progress` - Function to call on progress updates
+ pub fn create_snapshot_with_progress<S, I, P>(
+ &self,
+ name: S,
+ contents: &mut [u8],
+ analysis_cache_contexts: &mut [u8],
+ file: &mut [u8],
+ parent_ids: I,
+ mut progress: P,
+ ) -> Result<Ref<RemoteSnapshot>, ()>
+ where
+ S: BnStrCompatible,
+ P: ProgressCallback,
+ I: IntoIterator,
+ I::Item: BnStrCompatible,
+ {
+ let name = name.into_bytes_with_nul();
+ let parent_ids: Vec<_> = parent_ids
+ .into_iter()
+ .map(|id| id.into_bytes_with_nul())
+ .collect();
+ let mut parent_ids_raw: Vec<_> = parent_ids
+ .iter()
+ .map(|x| x.as_ref().as_ptr() as *const c_char)
+ .collect();
+ let result = unsafe {
+ BNRemoteFileCreateSnapshot(
+ self.handle.as_ptr(),
+ name.as_ref().as_ptr() as *const c_char,
+ contents.as_mut_ptr(),
+ contents.len(),
+ analysis_cache_contexts.as_mut_ptr(),
+ analysis_cache_contexts.len(),
+ file.as_mut_ptr(),
+ file.len(),
+ parent_ids_raw.as_mut_ptr(),
+ parent_ids_raw.len(),
+ Some(P::cb_progress_callback),
+ &mut progress as *mut P as *mut c_void,
+ )
+ };
+ let handle = NonNull::new(result).ok_or(())?;
+ Ok(unsafe { RemoteSnapshot::ref_from_raw(handle) })
+ }
+
+ // Delete a snapshot from the remote
+ pub fn delete_snapshot(&self, snapshot: &RemoteSnapshot) -> Result<(), ()> {
+ let success =
+ unsafe { BNRemoteFileDeleteSnapshot(self.handle.as_ptr(), snapshot.handle.as_ptr()) };
+ success.then_some(()).ok_or(())
+ }
+
+ // TODO - This passes and returns a c++ `std::vector<T>`. A BnData can be implement in rust, but the
+ // coreAPI need to include a `FreeData` function, similar to `BNFreeString` does.
+ // The C++ API just assumes that both use the same allocator, and the python API seems to just leak this
+ // memory, never dropping it.
+ //pub fn download_file<S, F>(&self, mut progress_function: F) -> BnData
+ //where
+ // S: BnStrCompatible,
+ // F: ProgressCallback,
+ //{
+ // let mut data = ptr::null_mut();
+ // let mut data_len = 0;
+ // let result = unsafe {
+ // BNRemoteFileDownload(
+ // self.handle.as_ptr(),
+ // Some(F::cb_progress_callback),
+ // &mut progress_function as *mut _ as *mut c_void,
+ // &mut data,
+ // &mut data_len,
+ // )
+ // };
+ // todo!()
+ //}
+
+ pub fn request_user_positions(&self) -> BnString {
+ let result = unsafe { BNRemoteFileRequestUserPositions(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ pub fn request_chat_log(&self) -> BnString {
+ let result = unsafe { BNRemoteFileRequestChatLog(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ // TODO: AsRef<Path>
+ /// Download a file from its remote, saving all snapshots to a database in the
+ /// specified location. Returns a FileContext for opening the file later.
+ ///
+ /// * `db_path` - File path for saved database
+ /// * `progress_function` - Function to call for progress updates
+ pub fn download<S>(&self, db_path: S) -> Result<Ref<FileMetadata>, ()>
+ where
+ S: BnStrCompatible,
+ {
+ sync::download_file(self, db_path)
+ }
+
+ // TODO: AsRef<Path>
+ /// Download a file from its remote, saving all snapshots to a database in the
+ /// specified location. Returns a FileContext for opening the file later.
+ ///
+ /// * `db_path` - File path for saved database
+ /// * `progress_function` - Function to call for progress updates
+ pub fn download_with_progress<S, F>(
+ &self,
+ db_path: S,
+ progress_function: F,
+ ) -> Result<Ref<FileMetadata>, ()>
+ where
+ S: BnStrCompatible,
+ F: ProgressCallback,
+ {
+ sync::download_file_with_progress(self, db_path, progress_function)
+ }
+
+ /// Download a remote file and save it to a BNDB at the given `path`, returning the associated [`FileMetadata`].
+ pub fn download_database<S: BnStrCompatible>(&self, path: S) -> Result<Ref<FileMetadata>, ()> {
+ let file = self.download(path)?;
+ let database = file.database().ok_or(())?;
+ self.sync(&database, DatabaseConflictHandlerFail, NoNameChangeset)?;
+ Ok(file)
+ }
+
+ // TODO: This might be a bad helper... maybe remove...
+ // TODO: AsRef<Path>
+ /// Download a remote file and save it to a BNDB at the given `path`.
+ pub fn download_database_with_progress<S: BnStrCompatible>(
+ &self,
+ path: S,
+ progress: impl ProgressCallback,
+ ) -> Result<Ref<FileMetadata>, ()> {
+ let mut progress = progress.split(&[50, 50]);
+ let file = self.download_with_progress(path, progress.next_subpart().unwrap())?;
+ let database = file.database().ok_or(())?;
+ self.sync_with_progress(
+ &database,
+ DatabaseConflictHandlerFail,
+ NoNameChangeset,
+ progress.next_subpart().unwrap(),
+ )?;
+ Ok(file)
+ }
+
+ /// Completely sync a file, pushing/pulling/merging/applying changes
+ ///
+ /// * `bv_or_db` - Binary view or database to sync with
+ /// * `conflict_handler` - Function to call to resolve snapshot conflicts
+ /// * `name_changeset` - Function to call for naming a pushed changeset, if necessary
+ pub fn sync<C: DatabaseConflictHandler, N: NameChangeset>(
+ &self,
+ database: &Database,
+ conflict_handler: C,
+ name_changeset: N,
+ ) -> Result<(), ()> {
+ sync::sync_database(database, self, conflict_handler, name_changeset)
+ }
+
+ /// Completely sync a file, pushing/pulling/merging/applying changes
+ ///
+ /// * `bv_or_db` - Binary view or database to sync with
+ /// * `conflict_handler` - Function to call to resolve snapshot conflicts
+ /// * `name_changeset` - Function to call for naming a pushed changeset, if necessary
+ /// * `progress` - Function to call for progress updates
+ pub fn sync_with_progress<C: DatabaseConflictHandler, P: ProgressCallback, N: NameChangeset>(
+ &self,
+ database: &Database,
+ conflict_handler: C,
+ name_changeset: N,
+ progress: P,
+ ) -> Result<(), ()> {
+ sync::sync_database_with_progress(
+ database,
+ self,
+ conflict_handler,
+ name_changeset,
+ progress,
+ )
+ }
+
+ /// Pull updated snapshots from the remote. Merge local changes with remote changes and
+ /// potentially create a new snapshot for unsaved changes, named via name_changeset.
+ ///
+ /// * `bv_or_db` - Binary view or database to sync with
+ /// * `conflict_handler` - Function to call to resolve snapshot conflicts
+ /// * `name_changeset` - Function to call for naming a pushed changeset, if necessary
+ pub fn pull<C, N>(
+ &self,
+ database: &Database,
+ conflict_handler: C,
+ name_changeset: N,
+ ) -> Result<usize, ()>
+ where
+ C: DatabaseConflictHandler,
+ N: NameChangeset,
+ {
+ sync::pull_database(database, self, conflict_handler, name_changeset)
+ }
+
+ /// Pull updated snapshots from the remote. Merge local changes with remote changes and
+ /// potentially create a new snapshot for unsaved changes, named via name_changeset.
+ ///
+ /// * `bv_or_db` - Binary view or database to sync with
+ /// * `conflict_handler` - Function to call to resolve snapshot conflicts
+ /// * `name_changeset` - Function to call for naming a pushed changeset, if necessary
+ /// * `progress` - Function to call for progress updates
+ pub fn pull_with_progress<C, P, N>(
+ &self,
+ database: &Database,
+ conflict_handler: C,
+ name_changeset: N,
+ progress: P,
+ ) -> Result<usize, ()>
+ where
+ C: DatabaseConflictHandler,
+ P: ProgressCallback,
+ N: NameChangeset,
+ {
+ sync::pull_database_with_progress(
+ database,
+ self,
+ conflict_handler,
+ name_changeset,
+ progress,
+ )
+ }
+
+ /// Push locally added snapshots to the remote.
+ ///
+ /// * `bv_or_db` - Binary view or database to sync with
+ pub fn push<P>(&self, database: &Database) -> Result<usize, ()>
+ where
+ P: ProgressCallback,
+ {
+ sync::push_database(database, self)
+ }
+
+ /// Push locally added snapshots to the remote.
+ ///
+ /// * `bv_or_db` - Binary view or database to sync with
+ /// * `progress` - Function to call for progress updates
+ pub fn push_with_progress<P>(&self, database: &Database, progress: P) -> Result<usize, ()>
+ where
+ P: ProgressCallback,
+ {
+ sync::push_database_with_progress(database, self, progress)
+ }
+}
+
+impl Debug for RemoteFile {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("RemoteFile")
+ .field("id", &self.id())
+ .field("name", &self.name())
+ .field("description", &self.description())
+ .field("metadata", &self.metadata())
+ .field("size", &self.size())
+ .field(
+ "snapshot_count",
+ &self.snapshots().map(|s| s.len()).unwrap_or(0),
+ )
+ .finish()
+ }
+}
+
+impl PartialEq for RemoteFile {
+ fn eq(&self, other: &Self) -> bool {
+ self.id() == other.id()
+ }
+}
+impl Eq for RemoteFile {}
+
+impl ToOwned for RemoteFile {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for RemoteFile {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: NonNull::new(BNNewRemoteFileReference(handle.handle.as_ptr())).unwrap(),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeRemoteFile(handle.handle.as_ptr());
+ }
+}
+
+impl CoreArrayProvider for RemoteFile {
+ type Raw = *mut BNRemoteFile;
+ type Context = ();
+ type Wrapped<'a> = Guard<'a, Self>;
+}
+
+unsafe impl CoreArrayProviderInner for RemoteFile {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeRemoteFileList(raw, count)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
+ let raw_ptr = NonNull::new(*raw).unwrap();
+ Guard::new(Self::from_raw(raw_ptr), context)
+ }
+}
diff --git a/rust/src/collaboration/folder.rs b/rust/src/collaboration/folder.rs
new file mode 100644
index 00000000..90a85f1c
--- /dev/null
+++ b/rust/src/collaboration/folder.rs
@@ -0,0 +1,180 @@
+use super::{Remote, RemoteProject};
+use binaryninjacore_sys::*;
+use std::ffi::c_char;
+use std::ptr::NonNull;
+
+use crate::project::folder::ProjectFolder;
+use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
+use crate::string::{BnStrCompatible, BnString};
+
+#[repr(transparent)]
+pub struct RemoteFolder {
+ pub(crate) handle: NonNull<BNRemoteFolder>,
+}
+
+impl RemoteFolder {
+ pub(crate) unsafe fn from_raw(handle: NonNull<BNRemoteFolder>) -> Self {
+ Self { handle }
+ }
+
+ pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNRemoteFolder>) -> Ref<Self> {
+ Ref::new(Self { handle })
+ }
+
+ // TODO: Rename to local folder?
+ // TODO: Bump this to an option
+ /// Get the core folder associated with this remote folder.
+ pub fn core_folder(&self) -> Result<Ref<ProjectFolder>, ()> {
+ let result = unsafe { BNRemoteFolderGetCoreFolder(self.handle.as_ptr()) };
+ NonNull::new(result)
+ .map(|handle| unsafe { ProjectFolder::ref_from_raw(handle) })
+ .ok_or(())
+ }
+
+ // TODO: Bump this to an option
+ /// Get the owning project of this folder.
+ pub fn project(&self) -> Result<Ref<RemoteProject>, ()> {
+ let result = unsafe { BNRemoteFolderGetProject(self.handle.as_ptr()) };
+ NonNull::new(result)
+ .map(|handle| unsafe { RemoteProject::ref_from_raw(handle) })
+ .ok_or(())
+ }
+
+ // TODO: Bump this to an option
+ /// Get the owning remote of this folder.
+ pub fn remote(&self) -> Result<Ref<Remote>, ()> {
+ let result = unsafe { BNRemoteFolderGetRemote(self.handle.as_ptr()) };
+ NonNull::new(result)
+ .map(|handle| unsafe { Remote::ref_from_raw(handle) })
+ .ok_or(())
+ }
+
+ // TODO: Should this pull folders?
+ // TODO: If it does we keep the result?
+ /// Get the parent folder, if available.
+ pub fn parent(&self) -> Result<Option<Ref<RemoteFolder>>, ()> {
+ let project = self.project()?;
+ // TODO: This sync should be removed?
+ if !project.has_pulled_folders() {
+ project.pull_folders()?;
+ }
+ let mut parent_handle = std::ptr::null_mut();
+ let success = unsafe { BNRemoteFolderGetParent(self.handle.as_ptr(), &mut parent_handle) };
+ success
+ .then(|| {
+ NonNull::new(parent_handle)
+ .map(|handle| unsafe { RemoteFolder::ref_from_raw(handle) })
+ })
+ .ok_or(())
+ }
+
+ /// Set the parent folder. You will need to push the folder to update the remote version.
+ pub fn set_parent(&self, parent: Option<&RemoteFolder>) -> Result<(), ()> {
+ let parent_handle = parent.map_or(std::ptr::null_mut(), |p| p.handle.as_ptr());
+ let success = unsafe { BNRemoteFolderSetParent(self.handle.as_ptr(), parent_handle) };
+ success.then_some(()).ok_or(())
+ }
+
+ /// Get web API endpoint URL.
+ pub fn url(&self) -> BnString {
+ let result = unsafe { BNRemoteFolderGetUrl(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ /// Get unique ID.
+ pub fn id(&self) -> BnString {
+ let result = unsafe { BNRemoteFolderGetId(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ /// Unique id of parent folder, if there is a parent. None, otherwise
+ pub fn parent_id(&self) -> Option<BnString> {
+ let mut parent_id = std::ptr::null_mut();
+ let have = unsafe { BNRemoteFolderGetParentId(self.handle.as_ptr(), &mut parent_id) };
+ have.then(|| unsafe { BnString::from_raw(parent_id) })
+ }
+
+ /// Displayed name of folder
+ pub fn name(&self) -> BnString {
+ let result = unsafe { BNRemoteFolderGetName(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ /// Set the display name of the folder. You will need to push the folder to update the remote version.
+ pub fn set_name<S: BnStrCompatible>(&self, name: S) -> Result<(), ()> {
+ let name = name.into_bytes_with_nul();
+ let success = unsafe {
+ BNRemoteFolderSetName(
+ self.handle.as_ptr(),
+ name.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ success.then_some(()).ok_or(())
+ }
+
+ /// Description of the folder
+ pub fn description(&self) -> BnString {
+ let result = unsafe { BNRemoteFolderGetDescription(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ /// Set the description of the folder. You will need to push the folder to update the remote version.
+ pub fn set_description<S: BnStrCompatible>(&self, description: S) -> Result<(), ()> {
+ let description = description.into_bytes_with_nul();
+ let success = unsafe {
+ BNRemoteFolderSetDescription(
+ self.handle.as_ptr(),
+ description.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ success.then_some(()).ok_or(())
+ }
+}
+
+impl PartialEq for RemoteFolder {
+ fn eq(&self, other: &Self) -> bool {
+ self.id() == other.id()
+ }
+}
+impl Eq for RemoteFolder {}
+
+impl ToOwned for RemoteFolder {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for RemoteFolder {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: NonNull::new(BNNewRemoteFolderReference(handle.handle.as_ptr())).unwrap(),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeRemoteFolder(handle.handle.as_ptr());
+ }
+}
+
+impl CoreArrayProvider for RemoteFolder {
+ type Raw = *mut BNRemoteFolder;
+ type Context = ();
+ type Wrapped<'a> = Guard<'a, Self>;
+}
+
+unsafe impl CoreArrayProviderInner for RemoteFolder {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeRemoteFolderList(raw, count)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
+ let raw_ptr = NonNull::new(*raw).unwrap();
+ Guard::new(Self::from_raw(raw_ptr), context)
+ }
+}
diff --git a/rust/src/collaboration/group.rs b/rust/src/collaboration/group.rs
new file mode 100644
index 00000000..bad09d6c
--- /dev/null
+++ b/rust/src/collaboration/group.rs
@@ -0,0 +1,196 @@
+use super::Remote;
+use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
+use crate::string::{BnStrCompatible, BnString};
+use binaryninjacore_sys::*;
+use std::ffi::c_char;
+use std::fmt;
+use std::fmt::{Display, Formatter};
+use std::ptr::NonNull;
+
+#[repr(transparent)]
+pub struct RemoteGroup {
+ pub(crate) handle: NonNull<BNCollaborationGroup>,
+}
+
+impl RemoteGroup {
+ pub(crate) unsafe fn from_raw(handle: NonNull<BNCollaborationGroup>) -> Self {
+ Self { handle }
+ }
+
+ pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNCollaborationGroup>) -> Ref<Self> {
+ Ref::new(Self { handle })
+ }
+
+ /// Owning Remote
+ pub fn remote(&self) -> Result<Ref<Remote>, ()> {
+ let value = unsafe { BNCollaborationGroupGetRemote(self.handle.as_ptr()) };
+ NonNull::new(value)
+ .map(|handle| unsafe { Remote::ref_from_raw(handle) })
+ .ok_or(())
+ }
+
+ /// Web api endpoint url
+ pub fn url(&self) -> BnString {
+ let value = unsafe { BNCollaborationGroupGetUrl(self.handle.as_ptr()) };
+ assert!(!value.is_null());
+ unsafe { BnString::from_raw(value) }
+ }
+
+ /// Unique id
+ pub fn id(&self) -> GroupId {
+ GroupId(unsafe { BNCollaborationGroupGetId(self.handle.as_ptr()) })
+ }
+
+ /// Group name
+ pub fn name(&self) -> BnString {
+ let value = unsafe { BNCollaborationGroupGetName(self.handle.as_ptr()) };
+ assert!(!value.is_null());
+ unsafe { BnString::from_raw(value) }
+ }
+
+ /// Set group name
+ /// You will need to push the group to update the Remote.
+ pub fn set_name<U: BnStrCompatible>(&self, name: U) {
+ let name = name.into_bytes_with_nul();
+ unsafe {
+ BNCollaborationGroupSetName(
+ self.handle.as_ptr(),
+ name.as_ref().as_ptr() as *const c_char,
+ )
+ }
+ }
+
+ /// Get list of users in the group
+ pub fn users(&self) -> Result<(Array<BnString>, Array<BnString>), ()> {
+ let mut usernames = std::ptr::null_mut();
+ let mut user_ids = std::ptr::null_mut();
+ let mut count = 0;
+ // TODO: This should only fail if collaboration is not supported.
+ // TODO: Because you should not have a RemoteGroup at that point we can ignore?
+ let success = unsafe {
+ BNCollaborationGroupGetUsers(
+ self.handle.as_ptr(),
+ &mut user_ids,
+ &mut usernames,
+ &mut count,
+ )
+ };
+ success
+ .then(|| unsafe {
+ let ids = Array::new(user_ids, count, ());
+ let users = Array::new(usernames, count, ());
+ (ids, users)
+ })
+ .ok_or(())
+ }
+
+ // TODO: Are any permissions required to the set the remote group users?
+ /// Set the list of users in a group by their usernames.
+ /// You will need to push the group to update the Remote.
+ pub fn set_users<I>(&self, usernames: I) -> Result<(), ()>
+ where
+ I: IntoIterator,
+ I::Item: BnStrCompatible,
+ {
+ let usernames: Vec<_> = usernames
+ .into_iter()
+ .map(|u| u.into_bytes_with_nul())
+ .collect();
+ let mut usernames_raw: Vec<_> = usernames
+ .iter()
+ .map(|s| s.as_ref().as_ptr() as *const c_char)
+ .collect();
+ // TODO: This should only fail if collaboration is not supported.
+ // TODO: Because you should not have a RemoteGroup at that point we can ignore?
+ // TODO: Do you need any permissions to do this?
+ let success = unsafe {
+ BNCollaborationGroupSetUsernames(
+ self.handle.as_ptr(),
+ usernames_raw.as_mut_ptr(),
+ usernames_raw.len(),
+ )
+ };
+ success.then_some(()).ok_or(())
+ }
+
+ /// Test if a group has a user with the given username
+ pub fn contains_user<U: BnStrCompatible>(&self, username: U) -> bool {
+ let username = username.into_bytes_with_nul();
+ unsafe {
+ BNCollaborationGroupContainsUser(
+ self.handle.as_ptr(),
+ username.as_ref().as_ptr() as *const c_char,
+ )
+ }
+ }
+}
+
+impl PartialEq for RemoteGroup {
+ fn eq(&self, other: &Self) -> bool {
+ self.id() == other.id()
+ }
+}
+impl Eq for RemoteGroup {}
+
+impl ToOwned for RemoteGroup {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for RemoteGroup {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: NonNull::new(BNNewCollaborationGroupReference(handle.handle.as_ptr())).unwrap(),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeCollaborationGroup(handle.handle.as_ptr());
+ }
+}
+
+impl CoreArrayProvider for RemoteGroup {
+ type Raw = *mut BNCollaborationGroup;
+ type Context = ();
+ type Wrapped<'a> = Guard<'a, Self>;
+}
+
+unsafe impl CoreArrayProviderInner for RemoteGroup {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeCollaborationGroupList(raw, count)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
+ let raw_ptr = NonNull::new(*raw).unwrap();
+ Guard::new(Self::from_raw(raw_ptr), context)
+ }
+}
+
+#[repr(transparent)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct GroupId(pub u64);
+
+impl Display for GroupId {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_fmt(format_args!("{}", self.0))
+ }
+}
+
+impl CoreArrayProvider for GroupId {
+ type Raw = u64;
+ type Context = ();
+ type Wrapped<'a> = GroupId;
+}
+
+unsafe impl CoreArrayProviderInner for GroupId {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNCollaborationFreeIdList(raw, count)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ GroupId(*raw)
+ }
+}
diff --git a/rust/src/collaboration/merge.rs b/rust/src/collaboration/merge.rs
new file mode 100644
index 00000000..2d28725c
--- /dev/null
+++ b/rust/src/collaboration/merge.rs
@@ -0,0 +1,204 @@
+use binaryninjacore_sys::*;
+use std::ffi::c_char;
+use std::ptr::NonNull;
+
+use crate::database::{snapshot::Snapshot, Database};
+use crate::file_metadata::FileMetadata;
+use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
+use crate::string::{BnStrCompatible, BnString};
+
+pub type MergeConflictDataType = BNMergeConflictDataType;
+
+/// Structure representing an individual merge conflict
+#[repr(transparent)]
+pub struct MergeConflict {
+ handle: NonNull<BNAnalysisMergeConflict>,
+}
+
+impl MergeConflict {
+ pub(crate) unsafe fn from_raw(handle: NonNull<BNAnalysisMergeConflict>) -> Self {
+ Self { handle }
+ }
+
+ #[allow(unused)]
+ pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNAnalysisMergeConflict>) -> Ref<Self> {
+ Ref::new(Self { handle })
+ }
+
+ /// Database backing all snapshots in the merge conflict
+ pub fn database(&self) -> Database {
+ let result = unsafe { BNAnalysisMergeConflictGetDatabase(self.handle.as_ptr()) };
+ unsafe { Database::from_raw(NonNull::new(result).unwrap()) }
+ }
+
+ /// Snapshot which is the parent of the two being merged
+ pub fn base_snapshot(&self) -> Option<Snapshot> {
+ let result = unsafe { BNAnalysisMergeConflictGetBaseSnapshot(self.handle.as_ptr()) };
+ NonNull::new(result).map(|handle| unsafe { Snapshot::from_raw(handle) })
+ }
+
+ /// First snapshot being merged
+ pub fn first_snapshot(&self) -> Option<Snapshot> {
+ let result = unsafe { BNAnalysisMergeConflictGetFirstSnapshot(self.handle.as_ptr()) };
+ NonNull::new(result).map(|handle| unsafe { Snapshot::from_raw(handle) })
+ }
+
+ /// Second snapshot being merged
+ pub fn second_snapshot(&self) -> Option<Snapshot> {
+ let result = unsafe { BNAnalysisMergeConflictGetSecondSnapshot(self.handle.as_ptr()) };
+ NonNull::new(result).map(|handle| unsafe { Snapshot::from_raw(handle) })
+ }
+
+ pub fn path_item_string<S: BnStrCompatible>(&self, path: S) -> Result<BnString, ()> {
+ let path = path.into_bytes_with_nul();
+ let result = unsafe {
+ BNAnalysisMergeConflictGetPathItemString(
+ self.handle.as_ptr(),
+ path.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ (!result.is_null())
+ .then(|| unsafe { BnString::from_raw(result) })
+ .ok_or(())
+ }
+
+ /// FileMetadata with contents of file for base snapshot
+ /// This function is slow! Only use it if you really need it.
+ pub fn base_file(&self) -> Option<Ref<FileMetadata>> {
+ let result = unsafe { BNAnalysisMergeConflictGetBaseFile(self.handle.as_ptr()) };
+ (!result.is_null()).then(|| unsafe { Ref::new(FileMetadata::from_raw(result)) })
+ }
+
+ /// FileMetadata with contents of file for first snapshot
+ /// This function is slow! Only use it if you really need it.
+ pub fn first_file(&self) -> Option<Ref<FileMetadata>> {
+ let result = unsafe { BNAnalysisMergeConflictGetFirstFile(self.handle.as_ptr()) };
+ (!result.is_null()).then(|| unsafe { Ref::new(FileMetadata::from_raw(result)) })
+ }
+
+ /// FileMetadata with contents of file for second snapshot
+ /// This function is slow! Only use it if you really need it.
+ pub fn second_file(&self) -> Option<Ref<FileMetadata>> {
+ let result = unsafe { BNAnalysisMergeConflictGetSecondFile(self.handle.as_ptr()) };
+ (!result.is_null()).then(|| unsafe { Ref::new(FileMetadata::from_raw(result)) })
+ }
+
+ /// Json String for conflicting data in the base snapshot
+ pub fn base(&self) -> Option<BnString> {
+ let result = unsafe { BNAnalysisMergeConflictGetBase(self.handle.as_ptr()) };
+ (!result.is_null()).then(|| unsafe { BnString::from_raw(result) })
+ }
+
+ /// Json object for conflicting data in the base snapshot
+ pub fn first(&self) -> Option<BnString> {
+ let result = unsafe { BNAnalysisMergeConflictGetFirst(self.handle.as_ptr()) };
+ (!result.is_null()).then(|| unsafe { BnString::from_raw(result) })
+ }
+
+ /// Json object for conflicting data in the second snapshot
+ pub fn second(&self) -> Option<BnString> {
+ let result = unsafe { BNAnalysisMergeConflictGetSecond(self.handle.as_ptr()) };
+ (!result.is_null()).then(|| unsafe { BnString::from_raw(result) })
+ }
+
+ /// Type of data in the conflict, Text/Json/Binary
+ pub fn data_type(&self) -> MergeConflictDataType {
+ unsafe { BNAnalysisMergeConflictGetDataType(self.handle.as_ptr()) }
+ }
+
+ /// String representing the type name of the data, not the same as data_type.
+ /// This is like "typeName" or "tag" depending on what object the conflict represents.
+ pub fn conflict_type(&self) -> BnString {
+ let result = unsafe { BNAnalysisMergeConflictGetType(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ /// Lookup key for the merge conflict, ideally a tree path that contains the name of the conflict
+ /// and all the recursive children leading up to this conflict.
+ pub fn key(&self) -> BnString {
+ let result = unsafe { BNAnalysisMergeConflictGetKey(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ /// Call this when you've resolved the conflict to save the result
+ pub fn success<S: BnStrCompatible>(&self, value: S) -> Result<(), ()> {
+ let value = value.into_bytes_with_nul();
+ let success = unsafe {
+ BNAnalysisMergeConflictSuccess(
+ self.handle.as_ptr(),
+ value.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ success.then_some(()).ok_or(())
+ }
+
+ // TODO: Make a safe version of this that checks the path and if it holds a number
+ pub unsafe fn get_path_item_number<S: BnStrCompatible>(&self, path_key: S) -> Option<u64> {
+ let path_key = path_key.into_bytes_with_nul();
+ let value = unsafe {
+ BNAnalysisMergeConflictGetPathItem(
+ self.handle.as_ptr(),
+ path_key.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ match value.is_null() {
+ // SAFETY: The path must be a number.
+ false => Some(value as u64),
+ true => None,
+ }
+ }
+
+ pub unsafe fn get_path_item_string<S: BnStrCompatible>(&self, path_key: S) -> Option<BnString> {
+ let path_key = path_key.into_bytes_with_nul();
+ let value = unsafe {
+ BNAnalysisMergeConflictGetPathItemString(
+ self.handle.as_ptr(),
+ path_key.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ match value.is_null() {
+ false => Some(unsafe { BnString::from_raw(value) }),
+ true => None,
+ }
+ }
+}
+
+impl ToOwned for MergeConflict {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for MergeConflict {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: NonNull::new(BNNewAnalysisMergeConflictReference(handle.handle.as_ptr()))
+ .unwrap(),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeAnalysisMergeConflict(handle.handle.as_ptr());
+ }
+}
+
+impl CoreArrayProvider for MergeConflict {
+ type Raw = *mut BNAnalysisMergeConflict;
+ type Context = ();
+ type Wrapped<'a> = Guard<'a, Self>;
+}
+
+unsafe impl CoreArrayProviderInner for MergeConflict {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeAnalysisMergeConflictList(raw, count)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
+ let raw_ptr = NonNull::new(*raw).unwrap();
+ Guard::new(Self::from_raw(raw_ptr), context)
+ }
+}
diff --git a/rust/src/collaboration/permission.rs b/rust/src/collaboration/permission.rs
new file mode 100644
index 00000000..99a7a4f2
--- /dev/null
+++ b/rust/src/collaboration/permission.rs
@@ -0,0 +1,159 @@
+use super::{GroupId, Remote, RemoteProject};
+use binaryninjacore_sys::*;
+use std::ptr::NonNull;
+
+use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
+use crate::string::BnString;
+
+pub type CollaborationPermissionLevel = BNCollaborationPermissionLevel;
+
+/// Struct representing a permission grant for a user or group on a project.
+#[repr(transparent)]
+pub struct Permission {
+ pub(crate) handle: NonNull<BNCollaborationPermission>,
+}
+
+impl Permission {
+ pub(crate) unsafe fn from_raw(handle: NonNull<BNCollaborationPermission>) -> Self {
+ Self { handle }
+ }
+
+ pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNCollaborationPermission>) -> Ref<Self> {
+ Ref::new(Self { handle })
+ }
+
+ pub fn remote(&self) -> Result<Ref<Remote>, ()> {
+ let result = unsafe { BNCollaborationPermissionGetRemote(self.handle.as_ptr()) };
+ NonNull::new(result)
+ .map(|handle| unsafe { Remote::ref_from_raw(handle) })
+ .ok_or(())
+ }
+
+ pub fn project(&self) -> Result<Ref<RemoteProject>, ()> {
+ let result = unsafe { BNCollaborationPermissionGetProject(self.handle.as_ptr()) };
+ NonNull::new(result)
+ .map(|handle| unsafe { RemoteProject::ref_from_raw(handle) })
+ .ok_or(())
+ }
+
+ /// Web api endpoint url
+ pub fn url(&self) -> BnString {
+ let value = unsafe { BNCollaborationPermissionGetUrl(self.handle.as_ptr()) };
+ assert!(!value.is_null());
+ unsafe { BnString::from_raw(value) }
+ }
+
+ /// unique id
+ pub fn id(&self) -> BnString {
+ let value = unsafe { BNCollaborationPermissionGetId(self.handle.as_ptr()) };
+ assert!(!value.is_null());
+ unsafe { BnString::from_raw(value) }
+ }
+
+ /// Level of permission
+ pub fn level(&self) -> CollaborationPermissionLevel {
+ unsafe { BNCollaborationPermissionGetLevel(self.handle.as_ptr()) }
+ }
+
+ /// Change the level of the permission
+ /// You will need to push the group to update the Remote.
+ pub fn set_level(&self, level: CollaborationPermissionLevel) {
+ unsafe { BNCollaborationPermissionSetLevel(self.handle.as_ptr(), level) }
+ }
+
+ /// Id of affected group
+ pub fn group_id(&self) -> Option<GroupId> {
+ let value = unsafe { BNCollaborationPermissionGetGroupId(self.handle.as_ptr()) };
+ if value != 0 {
+ Some(GroupId(value))
+ } else {
+ None
+ }
+ }
+
+ /// Name of affected group
+ pub fn group_name(&self) -> Option<BnString> {
+ let value = unsafe { BNCollaborationPermissionGetGroupName(self.handle.as_ptr()) };
+ assert!(!value.is_null());
+ let result = unsafe { BnString::from_raw(value) };
+ (!result.is_empty()).then_some(result)
+ }
+
+ /// Id of affected user
+ pub fn user_id(&self) -> Option<BnString> {
+ let value = unsafe { BNCollaborationPermissionGetUserId(self.handle.as_ptr()) };
+ assert!(!value.is_null());
+ let result = unsafe { BnString::from_raw(value) };
+ (!result.is_empty()).then_some(result)
+ }
+
+ /// Name of affected user
+ pub fn username(&self) -> Option<BnString> {
+ let value = unsafe { BNCollaborationPermissionGetUsername(self.handle.as_ptr()) };
+ assert!(!value.is_null());
+ let result = unsafe { BnString::from_raw(value) };
+ (!result.is_empty()).then_some(result)
+ }
+
+ /// If the permission grants the affect user/group the ability to read files in the project
+ pub fn can_view(&self) -> bool {
+ unsafe { BNCollaborationPermissionCanView(self.handle.as_ptr()) }
+ }
+
+ /// If the permission grants the affect user/group the ability to edit files in the project
+ pub fn can_edit(&self) -> bool {
+ unsafe { BNCollaborationPermissionCanEdit(self.handle.as_ptr()) }
+ }
+
+ /// If the permission grants the affect user/group the ability to administer the project
+ pub fn can_admin(&self) -> bool {
+ unsafe { BNCollaborationPermissionCanAdmin(self.handle.as_ptr()) }
+ }
+}
+
+impl PartialEq for Permission {
+ fn eq(&self, other: &Self) -> bool {
+ self.id() == other.id()
+ }
+}
+impl Eq for Permission {}
+
+impl ToOwned for Permission {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for Permission {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: NonNull::new(BNNewCollaborationPermissionReference(
+ handle.handle.as_ptr(),
+ ))
+ .unwrap(),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeCollaborationPermission(handle.handle.as_ptr());
+ }
+}
+
+impl CoreArrayProvider for Permission {
+ type Raw = *mut BNCollaborationPermission;
+ type Context = ();
+ type Wrapped<'a> = Guard<'a, Self>;
+}
+
+unsafe impl CoreArrayProviderInner for Permission {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeCollaborationPermissionList(raw, count)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
+ let raw_ptr = NonNull::new(*raw).unwrap();
+ Guard::new(Self::from_raw(raw_ptr), context)
+ }
+}
diff --git a/rust/src/collaboration/project.rs b/rust/src/collaboration/project.rs
new file mode 100644
index 00000000..1455f6c3
--- /dev/null
+++ b/rust/src/collaboration/project.rs
@@ -0,0 +1,1001 @@
+use std::ffi::{c_char, c_void};
+use std::ptr::NonNull;
+use std::time::SystemTime;
+
+use binaryninjacore_sys::*;
+
+use super::{
+ sync, CollaborationPermissionLevel, NameChangeset, Permission, Remote, RemoteFile,
+ RemoteFileType, RemoteFolder,
+};
+
+use crate::binary_view::{BinaryView, BinaryViewExt};
+use crate::database::Database;
+use crate::file_metadata::FileMetadata;
+use crate::progress::{NoProgressCallback, ProgressCallback};
+use crate::project::Project;
+use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
+use crate::string::{BnStrCompatible, BnString};
+
+#[repr(transparent)]
+pub struct RemoteProject {
+ pub(crate) handle: NonNull<BNRemoteProject>,
+}
+
+impl RemoteProject {
+ pub(crate) unsafe fn from_raw(handle: NonNull<BNRemoteProject>) -> Self {
+ Self { handle }
+ }
+
+ pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNRemoteProject>) -> Ref<Self> {
+ Ref::new(Self { handle })
+ }
+
+ /// Determine if the project is open (it needs to be opened before you can access its files)
+ pub fn is_open(&self) -> bool {
+ unsafe { BNRemoteProjectIsOpen(self.handle.as_ptr()) }
+ }
+
+ /// Open the project, allowing various file and folder based apis to work, as well as
+ /// connecting a core Project
+ pub fn open(&self) -> Result<(), ()> {
+ self.open_with_progress(NoProgressCallback)
+ }
+
+ /// Open the project, allowing various file and folder based apis to work, as well as
+ /// connecting a core Project
+ pub fn open_with_progress<F: ProgressCallback>(&self, mut progress: F) -> Result<(), ()> {
+ if self.is_open() {
+ return Ok(());
+ }
+ let success = unsafe {
+ BNRemoteProjectOpen(
+ self.handle.as_ptr(),
+ Some(F::cb_progress_callback),
+ &mut progress as *mut F as *mut c_void,
+ )
+ };
+ success.then_some(()).ok_or(())
+ }
+
+ /// Close the project and stop all background operations (e.g. file uploads)
+ pub fn close(&self) {
+ unsafe { BNRemoteProjectClose(self.handle.as_ptr()) }
+ }
+
+ /// Get the Remote Project for a Database
+ pub fn get_for_local_database(database: &Database) -> Result<Option<Ref<Self>>, ()> {
+ // TODO: This sync should be removed?
+ if sync::pull_projects(database)? {
+ return Ok(None);
+ }
+ sync::get_remote_project_for_local_database(database)
+ }
+
+ /// Get the Remote Project for a BinaryView
+ pub fn get_for_binaryview(bv: &BinaryView) -> Result<Option<Ref<Self>>, ()> {
+ let file = bv.file();
+ let Some(database) = file.database() else {
+ return Ok(None);
+ };
+ Self::get_for_local_database(&database)
+ }
+
+ /// Get the core [`Project`] for the remote project.
+ ///
+ /// NOTE: If the project has not been opened, it will be opened upon calling this.
+ pub fn core_project(&self) -> Result<Ref<Project>, ()> {
+ // TODO: This sync should be removed?
+ self.open()?;
+
+ let value = unsafe { BNRemoteProjectGetCoreProject(self.handle.as_ptr()) };
+ NonNull::new(value)
+ .map(|handle| unsafe { Project::ref_from_raw(handle) })
+ .ok_or(())
+ }
+
+ /// Get the owning remote
+ pub fn remote(&self) -> Result<Ref<Remote>, ()> {
+ let value = unsafe { BNRemoteProjectGetRemote(self.handle.as_ptr()) };
+ NonNull::new(value)
+ .map(|handle| unsafe { Remote::ref_from_raw(handle) })
+ .ok_or(())
+ }
+
+ /// Get the URL of the project
+ pub fn url(&self) -> BnString {
+ let result = unsafe { BNRemoteProjectGetUrl(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ /// Get the unique ID of the project
+ pub fn id(&self) -> BnString {
+ let result = unsafe { BNRemoteProjectGetId(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ /// Created date of the project
+ pub fn created(&self) -> SystemTime {
+ let result = unsafe { BNRemoteProjectGetCreated(self.handle.as_ptr()) };
+ crate::ffi::time_from_bn(result.try_into().unwrap())
+ }
+
+ /// Last modification of the project
+ pub fn last_modified(&self) -> SystemTime {
+ let result = unsafe { BNRemoteProjectGetLastModified(self.handle.as_ptr()) };
+ crate::ffi::time_from_bn(result.try_into().unwrap())
+ }
+
+ /// Displayed name of file
+ pub fn name(&self) -> BnString {
+ let result = unsafe { BNRemoteProjectGetName(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ /// Set the description of the file. You will need to push the file to update the remote version.
+ pub fn set_name<S: BnStrCompatible>(&self, name: S) -> Result<(), ()> {
+ let name = name.into_bytes_with_nul();
+ let success = unsafe {
+ BNRemoteProjectSetName(
+ self.handle.as_ptr(),
+ name.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ success.then_some(()).ok_or(())
+ }
+
+ /// Desciprtion of the file
+ pub fn description(&self) -> BnString {
+ let result = unsafe { BNRemoteProjectGetDescription(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ /// Set the description of the file. You will need to push the file to update the remote version.
+ pub fn set_description<S: BnStrCompatible>(&self, description: S) -> Result<(), ()> {
+ let description = description.into_bytes_with_nul();
+ let success = unsafe {
+ BNRemoteProjectSetDescription(
+ self.handle.as_ptr(),
+ description.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ success.then_some(()).ok_or(())
+ }
+
+ /// Get the number of files in a project (without needing to pull them first)
+ pub fn received_file_count(&self) -> u64 {
+ unsafe { BNRemoteProjectGetReceivedFileCount(self.handle.as_ptr()) }
+ }
+
+ /// Get the number of folders in a project (without needing to pull them first)
+ pub fn received_folder_count(&self) -> u64 {
+ unsafe { BNRemoteProjectGetReceivedFolderCount(self.handle.as_ptr()) }
+ }
+
+ /// Get the default directory path for a remote Project. This is based off the Setting for
+ /// collaboration.directory, the project's id, and the project's remote's id.
+ pub fn default_path(&self) -> Result<BnString, ()> {
+ sync::default_project_path(self)
+ }
+
+ /// If the project has pulled the folders yet
+ pub fn has_pulled_files(&self) -> bool {
+ unsafe { BNRemoteProjectHasPulledFiles(self.handle.as_ptr()) }
+ }
+
+ /// If the project has pulled the folders yet
+ pub fn has_pulled_folders(&self) -> bool {
+ unsafe { BNRemoteProjectHasPulledFolders(self.handle.as_ptr()) }
+ }
+
+ /// If the project has pulled the group permissions yet
+ pub fn has_pulled_group_permissions(&self) -> bool {
+ unsafe { BNRemoteProjectHasPulledGroupPermissions(self.handle.as_ptr()) }
+ }
+
+ /// If the project has pulled the user permissions yet
+ pub fn has_pulled_user_permissions(&self) -> bool {
+ unsafe { BNRemoteProjectHasPulledUserPermissions(self.handle.as_ptr()) }
+ }
+
+ /// If the currently logged in user is an administrator of the project (and can edit
+ /// permissions and such for the project).
+ pub fn is_admin(&self) -> bool {
+ unsafe { BNRemoteProjectIsAdmin(self.handle.as_ptr()) }
+ }
+
+ /// Get the list of files in this project.
+ ///
+ /// NOTE: If the project has not been opened, it will be opened upon calling this.
+ /// NOTE: If folders have not been pulled, they will be pulled upon calling this.
+ /// NOTE: If files have not been pulled, they will be pulled upon calling this.
+ pub fn files(&self) -> Result<Array<RemoteFile>, ()> {
+ // TODO: This sync should be removed?
+ if !self.has_pulled_files() {
+ self.pull_files()?;
+ }
+
+ let mut count = 0;
+ let result = unsafe { BNRemoteProjectGetFiles(self.handle.as_ptr(), &mut count) };
+ (!result.is_null())
+ .then(|| unsafe { Array::new(result, count, ()) })
+ .ok_or(())
+ }
+
+ /// Get a specific File in the Project by its id
+ ///
+ /// NOTE: If the project has not been opened, it will be opened upon calling this.
+ /// NOTE: If files have not been pulled, they will be pulled upon calling this.
+ pub fn get_file_by_id<S: BnStrCompatible>(&self, id: S) -> Result<Option<Ref<RemoteFile>>, ()> {
+ // TODO: This sync should be removed?
+ if !self.has_pulled_files() {
+ self.pull_files()?;
+ }
+ let id = id.into_bytes_with_nul();
+ let result = unsafe {
+ BNRemoteProjectGetFileById(self.handle.as_ptr(), id.as_ref().as_ptr() as *const c_char)
+ };
+ Ok(NonNull::new(result).map(|handle| unsafe { RemoteFile::ref_from_raw(handle) }))
+ }
+
+ /// Get a specific File in the Project by its name
+ ///
+ /// NOTE: If the project has not been opened, it will be opened upon calling this.
+ /// NOTE: If files have not been pulled, they will be pulled upon calling this.
+ pub fn get_file_by_name<S: BnStrCompatible>(
+ &self,
+ name: S,
+ ) -> Result<Option<Ref<RemoteFile>>, ()> {
+ // TODO: This sync should be removed?
+ if !self.has_pulled_files() {
+ self.pull_files()?;
+ }
+ let id = name.into_bytes_with_nul();
+ let result = unsafe {
+ BNRemoteProjectGetFileByName(
+ self.handle.as_ptr(),
+ id.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ Ok(NonNull::new(result).map(|handle| unsafe { RemoteFile::ref_from_raw(handle) }))
+ }
+
+ /// Pull the list of files from the Remote.
+ ///
+ /// NOTE: If the project has not been opened, it will be opened upon calling this.
+ /// NOTE: If folders have not been pulled, they will be pulled upon calling this.
+ pub fn pull_files(&self) -> Result<(), ()> {
+ self.pull_files_with_progress(NoProgressCallback)
+ }
+
+ /// Pull the list of files from the Remote.
+ ///
+ /// NOTE: If the project has not been opened, it will be opened upon calling this.
+ /// NOTE: If folders have not been pulled, they will be pulled upon calling this.
+ pub fn pull_files_with_progress<P: ProgressCallback>(&self, mut progress: P) -> Result<(), ()> {
+ // TODO: This sync should be removed?
+ if !self.has_pulled_folders() {
+ self.pull_folders()?;
+ }
+ let success = unsafe {
+ BNRemoteProjectPullFiles(
+ self.handle.as_ptr(),
+ Some(P::cb_progress_callback),
+ &mut progress as *mut P as *mut c_void,
+ )
+ };
+ success.then_some(()).ok_or(())
+ }
+
+ /// Create a new file on the remote and return a reference to the created file
+ ///
+ /// NOTE: If the project has not been opened, it will be opened upon calling this.
+ ///
+ /// * `filename` - File name
+ /// * `contents` - File contents
+ /// * `name` - Displayed file name
+ /// * `description` - File description
+ /// * `parent_folder` - Folder that will contain the file
+ /// * `file_type` - Type of File to create
+ pub fn create_file<F, N, D>(
+ &self,
+ filename: F,
+ contents: &[u8],
+ name: N,
+ description: D,
+ parent_folder: Option<&RemoteFolder>,
+ file_type: RemoteFileType,
+ ) -> Result<Ref<RemoteFile>, ()>
+ where
+ F: BnStrCompatible,
+ N: BnStrCompatible,
+ D: BnStrCompatible,
+ {
+ self.create_file_with_progress(
+ filename,
+ contents,
+ name,
+ description,
+ parent_folder,
+ file_type,
+ NoProgressCallback,
+ )
+ }
+
+ /// Create a new file on the remote and return a reference to the created file
+ ///
+ /// NOTE: If the project has not been opened, it will be opened upon calling this.
+ ///
+ /// * `filename` - File name
+ /// * `contents` - File contents
+ /// * `name` - Displayed file name
+ /// * `description` - File description
+ /// * `parent_folder` - Folder that will contain the file
+ /// * `file_type` - Type of File to create
+ /// * `progress` - Function to call on upload progress updates
+ pub fn create_file_with_progress<F, N, D, P>(
+ &self,
+ filename: F,
+ contents: &[u8],
+ name: N,
+ description: D,
+ parent_folder: Option<&RemoteFolder>,
+ file_type: RemoteFileType,
+ mut progress: P,
+ ) -> Result<Ref<RemoteFile>, ()>
+ where
+ F: BnStrCompatible,
+ N: BnStrCompatible,
+ D: BnStrCompatible,
+ P: ProgressCallback,
+ {
+ // TODO: This sync should be removed?
+ self.open()?;
+
+ let filename = filename.into_bytes_with_nul();
+ let name = name.into_bytes_with_nul();
+ let description = description.into_bytes_with_nul();
+ let folder_handle = parent_folder.map_or(std::ptr::null_mut(), |f| f.handle.as_ptr());
+ let file_ptr = unsafe {
+ BNRemoteProjectCreateFile(
+ self.handle.as_ptr(),
+ filename.as_ref().as_ptr() as *const c_char,
+ contents.as_ptr() as *mut _,
+ contents.len(),
+ name.as_ref().as_ptr() as *const c_char,
+ description.as_ref().as_ptr() as *const c_char,
+ folder_handle,
+ file_type,
+ Some(P::cb_progress_callback),
+ &mut progress as *mut P as *mut c_void,
+ )
+ };
+
+ NonNull::new(file_ptr)
+ .map(|handle| unsafe { RemoteFile::ref_from_raw(handle) })
+ .ok_or(())
+ }
+
+ /// Push an updated File object to the Remote
+ ///
+ /// NOTE: If the project has not been opened, it will be opened upon calling this.
+ pub fn push_file<I, K, V>(&self, file: &RemoteFile, extra_fields: I) -> Result<(), ()>
+ where
+ I: Iterator<Item = (K, V)>,
+ K: BnStrCompatible,
+ V: BnStrCompatible,
+ {
+ // TODO: This sync should be removed?
+ self.open()?;
+
+ let (keys, values): (Vec<_>, Vec<_>) = extra_fields
+ .into_iter()
+ .map(|(k, v)| (k.into_bytes_with_nul(), v.into_bytes_with_nul()))
+ .unzip();
+ let mut keys_raw = keys
+ .iter()
+ .map(|s| s.as_ref().as_ptr() as *const c_char)
+ .collect::<Vec<_>>();
+ let mut values_raw = values
+ .iter()
+ .map(|s| s.as_ref().as_ptr() as *const c_char)
+ .collect::<Vec<_>>();
+ let success = unsafe {
+ BNRemoteProjectPushFile(
+ self.handle.as_ptr(),
+ file.handle.as_ptr(),
+ keys_raw.as_mut_ptr(),
+ values_raw.as_mut_ptr(),
+ keys_raw.len(),
+ )
+ };
+ success.then_some(()).ok_or(())
+ }
+
+ pub fn delete_file(&self, file: &RemoteFile) -> Result<(), ()> {
+ // TODO: This sync should be removed?
+ self.open()?;
+
+ let success =
+ unsafe { BNRemoteProjectDeleteFile(self.handle.as_ptr(), file.handle.as_ptr()) };
+ success.then_some(()).ok_or(())
+ }
+
+ /// Get the list of folders in this project.
+ ///
+ /// NOTE: If the project has not been opened, it will be opened upon calling this.
+ /// NOTE: If folders have not been pulled, they will be pulled upon calling this.
+ pub fn folders(&self) -> Result<Array<RemoteFolder>, ()> {
+ // TODO: This sync should be removed?
+ if !self.has_pulled_folders() {
+ self.pull_folders()?;
+ }
+ let mut count = 0;
+ let result = unsafe { BNRemoteProjectGetFolders(self.handle.as_ptr(), &mut count) };
+ if result.is_null() {
+ return Err(());
+ }
+ Ok(unsafe { Array::new(result, count, ()) })
+ }
+
+ /// Get a specific Folder in the Project by its id
+ ///
+ /// NOTE: If the project has not been opened, it will be opened upon calling this.
+ /// NOTE: If folders have not been pulled, they will be pulled upon calling this.
+ pub fn get_folder_by_id<S: BnStrCompatible>(
+ &self,
+ id: S,
+ ) -> Result<Option<Ref<RemoteFolder>>, ()> {
+ // TODO: This sync should be removed?
+ if !self.has_pulled_folders() {
+ self.pull_folders()?;
+ }
+ let id = id.into_bytes_with_nul();
+ let result = unsafe {
+ BNRemoteProjectGetFolderById(
+ self.handle.as_ptr(),
+ id.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ Ok(NonNull::new(result).map(|handle| unsafe { RemoteFolder::ref_from_raw(handle) }))
+ }
+
+ /// Pull the list of folders from the Remote.
+ ///
+ /// NOTE: If the project has not been opened, it will be opened upon calling this.
+ pub fn pull_folders(&self) -> Result<(), ()> {
+ self.pull_folders_with_progress(NoProgressCallback)
+ }
+
+ /// Pull the list of folders from the Remote.
+ ///
+ /// NOTE: If the project has not been opened, it will be opened upon calling this.
+ pub fn pull_folders_with_progress<P: ProgressCallback>(
+ &self,
+ mut progress: P,
+ ) -> Result<(), ()> {
+ // TODO: This sync should be removed?
+ self.open()?;
+
+ let success = unsafe {
+ BNRemoteProjectPullFolders(
+ self.handle.as_ptr(),
+ Some(P::cb_progress_callback),
+ &mut progress as *mut P as *mut c_void,
+ )
+ };
+ success.then_some(()).ok_or(())
+ }
+
+ /// Create a new folder on the remote (and pull it)
+ ///
+ /// NOTE: If the project has not been opened, it will be opened upon calling this.
+ ///
+ /// * `name` - Displayed folder name
+ /// * `description` - Folder description
+ /// * `parent` - Parent folder (optional)
+ pub fn create_folder<N, D>(
+ &self,
+ name: N,
+ description: D,
+ parent_folder: Option<&RemoteFolder>,
+ ) -> Result<Ref<RemoteFolder>, ()>
+ where
+ N: BnStrCompatible,
+ D: BnStrCompatible,
+ {
+ self.create_folder_with_progress(name, description, parent_folder, NoProgressCallback)
+ }
+
+ /// Create a new folder on the remote (and pull it)
+ ///
+ /// NOTE: If the project has not been opened, it will be opened upon calling this.
+ ///
+ /// * `name` - Displayed folder name
+ /// * `description` - Folder description
+ /// * `parent` - Parent folder (optional)
+ /// * `progress` - Function to call on upload progress updates
+ pub fn create_folder_with_progress<N, D, P>(
+ &self,
+ name: N,
+ description: D,
+ parent_folder: Option<&RemoteFolder>,
+ mut progress: P,
+ ) -> Result<Ref<RemoteFolder>, ()>
+ where
+ N: BnStrCompatible,
+ D: BnStrCompatible,
+ P: ProgressCallback,
+ {
+ // TODO: This sync should be removed?
+ self.open()?;
+
+ let name = name.into_bytes_with_nul();
+ let description = description.into_bytes_with_nul();
+ let folder_handle = parent_folder.map_or(std::ptr::null_mut(), |f| f.handle.as_ptr());
+ let file_ptr = unsafe {
+ BNRemoteProjectCreateFolder(
+ self.handle.as_ptr(),
+ name.as_ref().as_ptr() as *const c_char,
+ description.as_ref().as_ptr() as *const c_char,
+ folder_handle,
+ Some(P::cb_progress_callback),
+ &mut progress as *mut P as *mut c_void,
+ )
+ };
+
+ NonNull::new(file_ptr)
+ .map(|handle| unsafe { RemoteFolder::ref_from_raw(handle) })
+ .ok_or(())
+ }
+
+ /// Push an updated Folder object to the Remote
+ ///
+ /// NOTE: If the project has not been opened, it will be opened upon calling this.
+ ///
+ /// * `folder` - Folder object which has been updated
+ /// * `extra_fields` - Extra HTTP fields to send with the update
+ pub fn push_folder<I, K, V>(&self, folder: &RemoteFolder, extra_fields: I) -> Result<(), ()>
+ where
+ I: Iterator<Item = (K, V)>,
+ K: BnStrCompatible,
+ V: BnStrCompatible,
+ {
+ // TODO: This sync should be removed?
+ self.open()?;
+
+ let (keys, values): (Vec<_>, Vec<_>) = extra_fields
+ .into_iter()
+ .map(|(k, v)| (k.into_bytes_with_nul(), v.into_bytes_with_nul()))
+ .unzip();
+ let mut keys_raw = keys
+ .iter()
+ .map(|s| s.as_ref().as_ptr() as *const c_char)
+ .collect::<Vec<_>>();
+ let mut values_raw = values
+ .iter()
+ .map(|s| s.as_ref().as_ptr() as *const c_char)
+ .collect::<Vec<_>>();
+ let success = unsafe {
+ BNRemoteProjectPushFolder(
+ self.handle.as_ptr(),
+ folder.handle.as_ptr(),
+ keys_raw.as_mut_ptr(),
+ values_raw.as_mut_ptr(),
+ keys_raw.len(),
+ )
+ };
+ success.then_some(()).ok_or(())
+ }
+
+ /// Delete a folder from the remote
+ ///
+ /// NOTE: If the project has not been opened, it will be opened upon calling this.
+ pub fn delete_folder(&self, folder: &RemoteFolder) -> Result<(), ()> {
+ // TODO: This sync should be removed?
+ self.open()?;
+
+ let success =
+ unsafe { BNRemoteProjectDeleteFolder(self.handle.as_ptr(), folder.handle.as_ptr()) };
+ success.then_some(()).ok_or(())
+ }
+
+ /// Get the list of group permissions in this project.
+ ///
+ /// NOTE: If group permissions have not been pulled, they will be pulled upon calling this.
+ pub fn group_permissions(&self) -> Result<Array<Permission>, ()> {
+ // TODO: This sync should be removed?
+ if !self.has_pulled_group_permissions() {
+ self.pull_group_permissions()?;
+ }
+
+ let mut count: usize = 0;
+ let value = unsafe { BNRemoteProjectGetGroupPermissions(self.handle.as_ptr(), &mut count) };
+ assert!(!value.is_null());
+ Ok(unsafe { Array::new(value, count, ()) })
+ }
+
+ /// Get the list of user permissions in this project.
+ ///
+ /// NOTE: If user permissions have not been pulled, they will be pulled upon calling this.
+ pub fn user_permissions(&self) -> Result<Array<Permission>, ()> {
+ // TODO: This sync should be removed?
+ if !self.has_pulled_user_permissions() {
+ self.pull_user_permissions()?;
+ }
+
+ let mut count: usize = 0;
+ let value = unsafe { BNRemoteProjectGetUserPermissions(self.handle.as_ptr(), &mut count) };
+ assert!(!value.is_null());
+ Ok(unsafe { Array::new(value, count, ()) })
+ }
+
+ /// Get a specific permission in the Project by its id.
+ ///
+ /// NOTE: If group or user permissions have not been pulled, they will be pulled upon calling this.
+ pub fn get_permission_by_id<S: BnStrCompatible>(
+ &self,
+ id: S,
+ ) -> Result<Option<Ref<Permission>>, ()> {
+ // TODO: This sync should be removed?
+ if !self.has_pulled_user_permissions() {
+ self.pull_user_permissions()?;
+ }
+ // TODO: This sync should be removed?
+ if !self.has_pulled_group_permissions() {
+ self.pull_group_permissions()?;
+ }
+
+ let id = id.into_bytes_with_nul();
+ let value = unsafe {
+ BNRemoteProjectGetPermissionById(self.handle.as_ptr(), id.as_ref().as_ptr() as *const _)
+ };
+ Ok(NonNull::new(value).map(|v| unsafe { Permission::ref_from_raw(v) }))
+ }
+
+ /// Pull the list of group permissions from the Remote.
+ pub fn pull_group_permissions(&self) -> Result<(), ()> {
+ self.pull_group_permissions_with_progress(NoProgressCallback)
+ }
+
+ /// Pull the list of group permissions from the Remote.
+ pub fn pull_group_permissions_with_progress<F: ProgressCallback>(
+ &self,
+ mut progress: F,
+ ) -> Result<(), ()> {
+ let success = unsafe {
+ BNRemoteProjectPullGroupPermissions(
+ self.handle.as_ptr(),
+ Some(F::cb_progress_callback),
+ &mut progress as *mut F as *mut c_void,
+ )
+ };
+ success.then_some(()).ok_or(())
+ }
+
+ /// Pull the list of user permissions from the Remote.
+ pub fn pull_user_permissions(&self) -> Result<(), ()> {
+ self.pull_user_permissions_with_progress(NoProgressCallback)
+ }
+
+ /// Pull the list of user permissions from the Remote.
+ pub fn pull_user_permissions_with_progress<F: ProgressCallback>(
+ &self,
+ mut progress: F,
+ ) -> Result<(), ()> {
+ let success = unsafe {
+ BNRemoteProjectPullUserPermissions(
+ self.handle.as_ptr(),
+ Some(F::cb_progress_callback),
+ &mut progress as *mut F as *mut c_void,
+ )
+ };
+ success.then_some(()).ok_or(())
+ }
+
+ /// Create a new group permission on the remote (and pull it).
+ ///
+ /// # Arguments
+ ///
+ /// * `group_id` - Group id
+ /// * `level` - Permission level
+ pub fn create_group_permission(
+ &self,
+ group_id: i64,
+ level: CollaborationPermissionLevel,
+ ) -> Result<Ref<Permission>, ()> {
+ self.create_group_permission_with_progress(group_id, level, NoProgressCallback)
+ }
+
+ /// Create a new group permission on the remote (and pull it).
+ ///
+ /// # Arguments
+ ///
+ /// * `group_id` - Group id
+ /// * `level` - Permission level
+ /// * `progress` - Function to call for upload progress updates
+ pub fn create_group_permission_with_progress<F: ProgressCallback>(
+ &self,
+ group_id: i64,
+ level: CollaborationPermissionLevel,
+ mut progress: F,
+ ) -> Result<Ref<Permission>, ()> {
+ let value = unsafe {
+ BNRemoteProjectCreateGroupPermission(
+ self.handle.as_ptr(),
+ group_id,
+ level,
+ Some(F::cb_progress_callback),
+ &mut progress as *mut F as *mut c_void,
+ )
+ };
+
+ NonNull::new(value)
+ .map(|v| unsafe { Permission::ref_from_raw(v) })
+ .ok_or(())
+ }
+
+ /// Create a new user permission on the remote (and pull it).
+ ///
+ /// # Arguments
+ ///
+ /// * `user_id` - User id
+ /// * `level` - Permission level
+ pub fn create_user_permission<S: BnStrCompatible>(
+ &self,
+ user_id: S,
+ level: CollaborationPermissionLevel,
+ ) -> Result<Ref<Permission>, ()> {
+ self.create_user_permission_with_progress(user_id, level, NoProgressCallback)
+ }
+
+ /// Create a new user permission on the remote (and pull it).
+ ///
+ /// # Arguments
+ ///
+ /// * `user_id` - User id
+ /// * `level` - Permission level
+ /// * `progress` - The progress callback to call
+ pub fn create_user_permission_with_progress<S: BnStrCompatible, F: ProgressCallback>(
+ &self,
+ user_id: S,
+ level: CollaborationPermissionLevel,
+ mut progress: F,
+ ) -> Result<Ref<Permission>, ()> {
+ let user_id = user_id.into_bytes_with_nul();
+ let value = unsafe {
+ BNRemoteProjectCreateUserPermission(
+ self.handle.as_ptr(),
+ user_id.as_ref().as_ptr() as *const c_char,
+ level,
+ Some(F::cb_progress_callback),
+ &mut progress as *mut F as *mut c_void,
+ )
+ };
+
+ NonNull::new(value)
+ .map(|v| unsafe { Permission::ref_from_raw(v) })
+ .ok_or(())
+ }
+
+ /// Push project permissions to the remote.
+ ///
+ /// # Arguments
+ ///
+ /// * `permission` - Permission object which has been updated
+ /// * `extra_fields` - Extra HTTP fields to send with the update
+ pub fn push_permission<I, K, V>(
+ &self,
+ permission: &Permission,
+ extra_fields: I,
+ ) -> Result<(), ()>
+ where
+ I: Iterator<Item = (K, V)>,
+ K: BnStrCompatible,
+ V: BnStrCompatible,
+ {
+ let (keys, values): (Vec<_>, Vec<_>) = extra_fields
+ .into_iter()
+ .map(|(k, v)| (k.into_bytes_with_nul(), v.into_bytes_with_nul()))
+ .unzip();
+ let mut keys_raw = keys
+ .iter()
+ .map(|s| s.as_ref().as_ptr() as *const c_char)
+ .collect::<Vec<_>>();
+ let mut values_raw = values
+ .iter()
+ .map(|s| s.as_ref().as_ptr() as *const c_char)
+ .collect::<Vec<_>>();
+
+ let success = unsafe {
+ BNRemoteProjectPushPermission(
+ self.handle.as_ptr(),
+ permission.handle.as_ptr(),
+ keys_raw.as_mut_ptr(),
+ values_raw.as_mut_ptr(),
+ keys_raw.len(),
+ )
+ };
+ success.then_some(()).ok_or(())
+ }
+
+ /// Delete a permission from the remote.
+ pub fn delete_permission(&self, permission: &Permission) -> Result<(), ()> {
+ let success = unsafe {
+ BNRemoteProjectDeletePermission(self.handle.as_ptr(), permission.handle.as_ptr())
+ };
+ success.then_some(()).ok_or(())
+ }
+
+ /// Determine if a user is in any of the view/edit/admin groups.
+ ///
+ /// # Arguments
+ ///
+ /// * `username` - Username of user to check
+ pub fn can_user_view<S: BnStrCompatible>(&self, username: S) -> bool {
+ let username = username.into_bytes_with_nul();
+ unsafe {
+ BNRemoteProjectCanUserView(
+ self.handle.as_ptr(),
+ username.as_ref().as_ptr() as *const c_char,
+ )
+ }
+ }
+
+ /// Determine if a user is in any of the edit/admin groups.
+ ///
+ /// # Arguments
+ ///
+ /// * `username` - Username of user to check
+ pub fn can_user_edit<S: BnStrCompatible>(&self, username: S) -> bool {
+ let username = username.into_bytes_with_nul();
+ unsafe {
+ BNRemoteProjectCanUserEdit(
+ self.handle.as_ptr(),
+ username.as_ref().as_ptr() as *const c_char,
+ )
+ }
+ }
+
+ /// Determine if a user is in the admin group.
+ ///
+ /// # Arguments
+ ///
+ /// * `username` - Username of user to check
+ pub fn can_user_admin<S: BnStrCompatible>(&self, username: S) -> bool {
+ let username = username.into_bytes_with_nul();
+ unsafe {
+ BNRemoteProjectCanUserAdmin(
+ self.handle.as_ptr(),
+ username.as_ref().as_ptr() as *const c_char,
+ )
+ }
+ }
+
+ /// Get the default directory path for a remote Project. This is based off
+ /// the Setting for collaboration.directory, the project's id, and the
+ /// project's remote's id.
+ pub fn default_project_path(&self) -> BnString {
+ let result = unsafe { BNCollaborationDefaultProjectPath(self.handle.as_ptr()) };
+ unsafe { BnString::from_raw(result) }
+ }
+
+ /// Upload a file, with database, to the remote under the given project
+ ///
+ /// * `metadata` - Local file with database
+ /// * `parent_folder` - Optional parent folder in which to place this file
+ /// * `name_changeset` - Function to call for naming a pushed changeset, if necessary
+ pub fn upload_database<C>(
+ &self,
+ metadata: &FileMetadata,
+ parent_folder: Option<&RemoteFolder>,
+ name_changeset: C,
+ ) -> Result<Ref<RemoteFile>, ()>
+ where
+ C: NameChangeset,
+ {
+ // TODO: Do we want this?
+ // TODO: If you have not yet pulled files you will have never filled the map you will be placing your
+ // TODO: New file in.
+ if !self.has_pulled_files() {
+ self.pull_files()?;
+ }
+ sync::upload_database(self, parent_folder, metadata, name_changeset)
+ }
+
+ /// Upload a file, with database, to the remote under the given project
+ ///
+ /// * `metadata` - Local file with database
+ /// * `parent_folder` - Optional parent folder in which to place this file
+ /// * `progress` -: Function to call for progress updates
+ /// * `name_changeset` - Function to call for naming a pushed changeset, if necessary
+ pub fn upload_database_with_progress<C>(
+ &self,
+ metadata: &FileMetadata,
+ parent_folder: Option<&RemoteFolder>,
+ name_changeset: C,
+ progress_function: impl ProgressCallback,
+ ) -> Result<Ref<RemoteFile>, ()>
+ where
+ C: NameChangeset,
+ {
+ sync::upload_database_with_progress(
+ self,
+ parent_folder,
+ metadata,
+ name_changeset,
+ progress_function,
+ )
+ }
+
+ // TODO: check remotebrowser.cpp for implementation
+ ///// Upload a file to the project, creating a new File and pulling it
+ /////
+ ///// NOTE: If the project has not been opened, it will be opened upon calling this.
+ /////
+ ///// * `target` - Path to file on disk or BinaryView/FileMetadata object of
+ ///// already-opened file
+ ///// * `parent_folder` - Parent folder to place the uploaded file in
+ ///// * `progress` - Function to call for progress updates
+ //pub fn upload_new_file<S: BnStrCompatible, P: ProgressCallback>(
+ // &self,
+ // target: S,
+ // parent_folder: Option<&RemoteFolder>,
+ // progress: P,
+ // open_view_options: u32,
+ //) -> Result<(), ()> {
+ // if !self.open(NoProgressCallback)? {
+ // return Err(());
+ // }
+ // let target = target.into_bytes_with_nul();
+ // todo!();
+ //}
+}
+
+impl PartialEq for RemoteProject {
+ fn eq(&self, other: &Self) -> bool {
+ self.id() == other.id()
+ }
+}
+impl Eq for RemoteProject {}
+
+impl ToOwned for RemoteProject {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for RemoteProject {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: NonNull::new(BNNewRemoteProjectReference(handle.handle.as_ptr())).unwrap(),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeRemoteProject(handle.handle.as_ptr());
+ }
+}
+
+impl CoreArrayProvider for RemoteProject {
+ type Raw = *mut BNRemoteProject;
+ type Context = ();
+ type Wrapped<'a> = Guard<'a, Self>;
+}
+
+unsafe impl CoreArrayProviderInner for RemoteProject {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeRemoteProjectList(raw, count)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
+ let raw_ptr = NonNull::new(*raw).unwrap();
+ Guard::new(Self::from_raw(raw_ptr), context)
+ }
+}
diff --git a/rust/src/collaboration/remote.rs b/rust/src/collaboration/remote.rs
new file mode 100644
index 00000000..baf2ce80
--- /dev/null
+++ b/rust/src/collaboration/remote.rs
@@ -0,0 +1,959 @@
+use binaryninjacore_sys::*;
+use std::ffi::{c_char, c_void};
+use std::ptr::NonNull;
+
+use super::{sync, GroupId, RemoteGroup, RemoteProject, RemoteUser};
+
+use crate::binary_view::BinaryView;
+use crate::database::Database;
+use crate::enterprise;
+use crate::progress::{NoProgressCallback, ProgressCallback};
+use crate::project::Project;
+use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
+use crate::string::{BnStrCompatible, BnString};
+
+#[repr(transparent)]
+pub struct Remote {
+ pub(crate) handle: NonNull<BNRemote>,
+}
+
+impl Remote {
+ pub(crate) unsafe fn from_raw(handle: NonNull<BNRemote>) -> Self {
+ Self { handle }
+ }
+
+ pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNRemote>) -> Ref<Self> {
+ Ref::new(Self { handle })
+ }
+
+ /// Create a Remote and add it to the list of known remotes (saved to Settings)
+ pub fn new<N: BnStrCompatible, A: BnStrCompatible>(name: N, address: A) -> Ref<Self> {
+ let name = name.into_bytes_with_nul();
+ let address = address.into_bytes_with_nul();
+ let result = unsafe {
+ BNCollaborationCreateRemote(
+ name.as_ref().as_ptr() as *const c_char,
+ address.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ unsafe { Self::ref_from_raw(NonNull::new(result).unwrap()) }
+ }
+
+ /// Get the Remote for a Database
+ pub fn get_for_local_database(database: &Database) -> Result<Option<Ref<Remote>>, ()> {
+ sync::get_remote_for_local_database(database)
+ }
+
+ /// Get the Remote for a Binary View
+ pub fn get_for_binary_view(bv: &BinaryView) -> Result<Option<Ref<Remote>>, ()> {
+ sync::get_remote_for_binary_view(bv)
+ }
+
+ /// Checks if the remote has pulled metadata like its id, etc.
+ pub fn has_loaded_metadata(&self) -> bool {
+ unsafe { BNRemoteHasLoadedMetadata(self.handle.as_ptr()) }
+ }
+
+ /// Gets the unique id. If metadata has not been pulled, it will be pulled upon calling this.
+ pub fn unique_id(&self) -> Result<BnString, ()> {
+ if !self.has_loaded_metadata() {
+ self.load_metadata()?;
+ }
+ let result = unsafe { BNRemoteGetUniqueId(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ Ok(unsafe { BnString::from_raw(result) })
+ }
+
+ /// Gets the name of the remote.
+ pub fn name(&self) -> BnString {
+ let result = unsafe { BNRemoteGetName(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ /// Gets the address of the remote.
+ pub fn address(&self) -> BnString {
+ let result = unsafe { BNRemoteGetAddress(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ /// Checks if the remote is connected.
+ pub fn is_connected(&self) -> bool {
+ unsafe { BNRemoteIsConnected(self.handle.as_ptr()) }
+ }
+
+ /// Gets the username used to connect to the remote.
+ pub fn username(&self) -> BnString {
+ let result = unsafe { BNRemoteGetUsername(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ /// Gets the token used to connect to the remote.
+ pub fn token(&self) -> BnString {
+ let result = unsafe { BNRemoteGetToken(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ /// Gets the server version. If metadata has not been pulled, it will be pulled upon calling this.
+ pub fn server_version(&self) -> Result<i32, ()> {
+ if !self.has_loaded_metadata() {
+ self.load_metadata()?;
+ }
+ Ok(unsafe { BNRemoteGetServerVersion(self.handle.as_ptr()) })
+ }
+
+ /// Gets the server build id. If metadata has not been pulled, it will be pulled upon calling this.
+ pub fn server_build_id(&self) -> Result<BnString, ()> {
+ if !self.has_loaded_metadata() {
+ self.load_metadata()?;
+ }
+ unsafe {
+ Ok(BnString::from_raw(BNRemoteGetServerBuildId(
+ self.handle.as_ptr(),
+ )))
+ }
+ }
+
+ /// Gets the list of supported authentication backends on the server.
+ /// If metadata has not been pulled, it will be pulled upon calling this.
+ pub fn auth_backends(&self) -> Result<(Array<BnString>, Array<BnString>), ()> {
+ if !self.has_loaded_metadata() {
+ self.load_metadata()?;
+ }
+
+ let mut backend_ids = std::ptr::null_mut();
+ let mut backend_names = std::ptr::null_mut();
+ let mut count = 0;
+ let success = unsafe {
+ BNRemoteGetAuthBackends(
+ self.handle.as_ptr(),
+ &mut backend_ids,
+ &mut backend_names,
+ &mut count,
+ )
+ };
+ success
+ .then(|| unsafe {
+ (
+ Array::new(backend_ids, count, ()),
+ Array::new(backend_names, count, ()),
+ )
+ })
+ .ok_or(())
+ }
+
+ /// Checks if the current user is an administrator.
+ pub fn is_admin(&self) -> Result<bool, ()> {
+ if !self.has_pulled_users() {
+ self.pull_users()?;
+ }
+ Ok(unsafe { BNRemoteIsAdmin(self.handle.as_ptr()) })
+ }
+
+ /// Checks if the remote is the same as the Enterprise License server.
+ pub fn is_enterprise(&self) -> Result<bool, ()> {
+ if !self.has_loaded_metadata() {
+ self.load_metadata()?;
+ }
+ Ok(unsafe { BNRemoteIsEnterprise(self.handle.as_ptr()) })
+ }
+
+ /// Loads metadata from the remote, including unique id and versions.
+ pub fn load_metadata(&self) -> Result<(), ()> {
+ let success = unsafe { BNRemoteLoadMetadata(self.handle.as_ptr()) };
+ success.then_some(()).ok_or(())
+ }
+
+ /// Requests an authentication token using a username and password.
+ pub fn request_authentication_token<U: BnStrCompatible, P: BnStrCompatible>(
+ &self,
+ username: U,
+ password: P,
+ ) -> Option<BnString> {
+ let username = username.into_bytes_with_nul();
+ let password = password.into_bytes_with_nul();
+ let token = unsafe {
+ BNRemoteRequestAuthenticationToken(
+ self.handle.as_ptr(),
+ username.as_ref().as_ptr() as *const c_char,
+ password.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ if token.is_null() {
+ None
+ } else {
+ Some(unsafe { BnString::from_raw(token) })
+ }
+ }
+
+ /// Connects to the Remote, loading metadata and optionally acquiring a token.
+ ///
+ /// Use [Remote::connect_with_opts] if you cannot otherwise automatically connect using enterprise.
+ pub fn connect(&self) -> Result<(), ()> {
+ // TODO: implement SecretsProvider
+ if self.is_enterprise()? && enterprise::is_server_authenticated() {
+ self.connect_with_opts(ConnectionOptions::from_enterprise()?)
+ } else {
+ // TODO: Make this error instead.
+ let username =
+ std::env::var("BN_ENTERPRISE_USERNAME").expect("No username for connection!");
+ let password =
+ std::env::var("BN_ENTERPRISE_PASSWORD").expect("No password for connection!");
+ let connection_opts = ConnectionOptions::new_with_password(username, password);
+ self.connect_with_opts(connection_opts)
+ }
+ }
+
+ // TODO: This needs docs and proper error.
+ pub fn connect_with_opts(&self, options: ConnectionOptions) -> Result<(), ()> {
+ // TODO: Should we make used load metadata first?
+ if !self.has_loaded_metadata() {
+ self.load_metadata()?;
+ }
+ let token = match options.token {
+ Some(token) => token,
+ None => {
+ // TODO: If password not defined than error saying no token or password
+ let password = options
+ .password
+ .expect("No password or token for connection!");
+ let token = self.request_authentication_token(&options.username, password);
+ // TODO: Error if None.
+ token.unwrap().to_string()
+ }
+ };
+ let username = options.username.into_bytes_with_nul();
+ let username_ptr = username.as_ptr() as *const c_char;
+ let token = token.into_bytes_with_nul();
+ let token_ptr = token.as_ptr() as *const c_char;
+ let success = unsafe { BNRemoteConnect(self.handle.as_ptr(), username_ptr, token_ptr) };
+ success.then_some(()).ok_or(())
+ }
+
+ /// Disconnects from the remote.
+ pub fn disconnect(&self) -> Result<(), ()> {
+ let success = unsafe { BNRemoteDisconnect(self.handle.as_ptr()) };
+ success.then_some(()).ok_or(())
+ }
+
+ /// Checks if the project has pulled the projects yet.
+ pub fn has_pulled_projects(&self) -> bool {
+ unsafe { BNRemoteHasPulledProjects(self.handle.as_ptr()) }
+ }
+
+ /// Checks if the project has pulled the groups yet.
+ pub fn has_pulled_groups(&self) -> bool {
+ unsafe { BNRemoteHasPulledGroups(self.handle.as_ptr()) }
+ }
+
+ /// Checks if the project has pulled the users yet.
+ pub fn has_pulled_users(&self) -> bool {
+ unsafe { BNRemoteHasPulledUsers(self.handle.as_ptr()) }
+ }
+
+ /// Gets the list of projects in this project.
+ ///
+ /// NOTE: If projects have not been pulled, they will be pulled upon calling this.
+ pub fn projects(&self) -> Result<Array<RemoteProject>, ()> {
+ if !self.has_pulled_projects() {
+ self.pull_projects()?;
+ }
+
+ let mut count = 0;
+ let value = unsafe { BNRemoteGetProjects(self.handle.as_ptr(), &mut count) };
+ if value.is_null() {
+ return Err(());
+ }
+ Ok(unsafe { Array::new(value, count, ()) })
+ }
+
+ /// Gets a specific project in the Remote by its id.
+ ///
+ /// NOTE: If projects have not been pulled, they will be pulled upon calling this.
+ pub fn get_project_by_id<S: BnStrCompatible>(
+ &self,
+ id: S,
+ ) -> Result<Option<Ref<RemoteProject>>, ()> {
+ if !self.has_pulled_projects() {
+ self.pull_projects()?;
+ }
+
+ let id = id.into_bytes_with_nul();
+ let value = unsafe {
+ BNRemoteGetProjectById(self.handle.as_ptr(), id.as_ref().as_ptr() as *const c_char)
+ };
+ Ok(NonNull::new(value).map(|handle| unsafe { RemoteProject::ref_from_raw(handle) }))
+ }
+
+ /// Gets a specific project in the Remote by its name.
+ ///
+ /// NOTE: If projects have not been pulled, they will be pulled upon calling this.
+ pub fn get_project_by_name<S: BnStrCompatible>(
+ &self,
+ name: S,
+ ) -> Result<Option<Ref<RemoteProject>>, ()> {
+ if !self.has_pulled_projects() {
+ self.pull_projects()?;
+ }
+
+ let name = name.into_bytes_with_nul();
+ let value = unsafe {
+ BNRemoteGetProjectByName(
+ self.handle.as_ptr(),
+ name.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ Ok(NonNull::new(value).map(|handle| unsafe { RemoteProject::ref_from_raw(handle) }))
+ }
+
+ /// Pulls the list of projects from the Remote.
+ pub fn pull_projects(&self) -> Result<(), ()> {
+ self.pull_projects_with_progress(NoProgressCallback)
+ }
+
+ /// Pulls the list of projects from the Remote.
+ ///
+ /// # Arguments
+ ///
+ /// * `progress` - Function to call for progress updates
+ pub fn pull_projects_with_progress<F: ProgressCallback>(
+ &self,
+ mut progress: F,
+ ) -> Result<(), ()> {
+ let success = unsafe {
+ BNRemotePullProjects(
+ self.handle.as_ptr(),
+ Some(F::cb_progress_callback),
+ &mut progress as *mut F as *mut c_void,
+ )
+ };
+ success.then_some(()).ok_or(())
+ }
+
+ /// Creates a new project on the remote (and pull it).
+ ///
+ /// # Arguments
+ ///
+ /// * `name` - Project name
+ /// * `description` - Project description
+ pub fn create_project<N: BnStrCompatible, D: BnStrCompatible>(
+ &self,
+ name: N,
+ description: D,
+ ) -> Result<Ref<RemoteProject>, ()> {
+ // TODO: Do we want this?
+ // TODO: If you have not yet pulled projects you will have never filled the map you will be placing your
+ // TODO: New project in.
+ if !self.has_pulled_projects() {
+ self.pull_projects()?;
+ }
+ let name = name.into_bytes_with_nul();
+ let description = description.into_bytes_with_nul();
+ let value = unsafe {
+ BNRemoteCreateProject(
+ self.handle.as_ptr(),
+ name.as_ref().as_ptr() as *const c_char,
+ description.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ NonNull::new(value)
+ .map(|handle| unsafe { RemoteProject::ref_from_raw(handle) })
+ .ok_or(())
+ }
+
+ /// Create a new project on the remote from a local project.
+ pub fn import_local_project(&self, project: &Project) -> Option<Ref<RemoteProject>> {
+ self.import_local_project_with_progress(project, NoProgressCallback)
+ }
+
+ /// Create a new project on the remote from a local project.
+ pub fn import_local_project_with_progress<P: ProgressCallback>(
+ &self,
+ project: &Project,
+ mut progress: P,
+ ) -> Option<Ref<RemoteProject>> {
+ let value = unsafe {
+ BNRemoteImportLocalProject(
+ self.handle.as_ptr(),
+ project.handle.as_ptr(),
+ Some(P::cb_progress_callback),
+ &mut progress as *mut P as *mut c_void,
+ )
+ };
+ NonNull::new(value).map(|handle| unsafe { RemoteProject::ref_from_raw(handle) })
+ }
+
+ /// Pushes an updated Project object to the Remote.
+ ///
+ /// # Arguments
+ ///
+ /// * `project` - Project object which has been updated
+ /// * `extra_fields` - Extra HTTP fields to send with the update
+ pub fn push_project<I, K, V>(&self, project: &RemoteProject, extra_fields: I) -> Result<(), ()>
+ where
+ I: Iterator<Item = (K, V)>,
+ K: BnStrCompatible,
+ V: BnStrCompatible,
+ {
+ let (keys, values): (Vec<_>, Vec<_>) = extra_fields
+ .into_iter()
+ .map(|(k, v)| (k.into_bytes_with_nul(), v.into_bytes_with_nul()))
+ .unzip();
+ let mut keys_raw = keys
+ .iter()
+ .map(|s| s.as_ref().as_ptr() as *const c_char)
+ .collect::<Vec<_>>();
+ let mut values_raw = values
+ .iter()
+ .map(|s| s.as_ref().as_ptr() as *const c_char)
+ .collect::<Vec<_>>();
+
+ let success = unsafe {
+ BNRemotePushProject(
+ self.handle.as_ptr(),
+ project.handle.as_ptr(),
+ keys_raw.as_mut_ptr(),
+ values_raw.as_mut_ptr(),
+ keys_raw.len(),
+ )
+ };
+ success.then_some(()).ok_or(())
+ }
+
+ /// Deletes a project from the remote.
+ pub fn delete_project(&self, project: &RemoteProject) -> Result<(), ()> {
+ let success =
+ unsafe { BNRemoteDeleteProject(self.handle.as_ptr(), project.handle.as_ptr()) };
+ success.then_some(()).ok_or(())
+ }
+
+ /// Gets the list of groups in this project.
+ ///
+ /// If groups have not been pulled, they will be pulled upon calling this.
+ /// This function is only available to accounts with admin status on the Remote.
+ pub fn groups(&self) -> Result<Array<RemoteGroup>, ()> {
+ if !self.has_pulled_groups() {
+ self.pull_groups()?;
+ }
+
+ let mut count = 0;
+ let value = unsafe { BNRemoteGetGroups(self.handle.as_ptr(), &mut count) };
+ if value.is_null() {
+ return Err(());
+ }
+ Ok(unsafe { Array::new(value, count, ()) })
+ }
+
+ /// Gets a specific group in the Remote by its id.
+ ///
+ /// If groups have not been pulled, they will be pulled upon calling this.
+ /// This function is only available to accounts with admin status on the Remote.
+ pub fn get_group_by_id(&self, id: GroupId) -> Result<Option<Ref<RemoteGroup>>, ()> {
+ if !self.has_pulled_groups() {
+ self.pull_groups()?;
+ }
+
+ let value = unsafe { BNRemoteGetGroupById(self.handle.as_ptr(), id.0) };
+ Ok(NonNull::new(value).map(|handle| unsafe { RemoteGroup::ref_from_raw(handle) }))
+ }
+
+ /// Gets a specific group in the Remote by its name.
+ ///
+ /// If groups have not been pulled, they will be pulled upon calling this.
+ /// This function is only available to accounts with admin status on the Remote.
+ pub fn get_group_by_name<S: BnStrCompatible>(
+ &self,
+ name: S,
+ ) -> Result<Option<Ref<RemoteGroup>>, ()> {
+ if !self.has_pulled_groups() {
+ self.pull_groups()?;
+ }
+
+ let name = name.into_bytes_with_nul();
+ let value = unsafe {
+ BNRemoteGetGroupByName(
+ self.handle.as_ptr(),
+ name.as_ref().as_ptr() as *const c_char,
+ )
+ };
+
+ Ok(NonNull::new(value).map(|handle| unsafe { RemoteGroup::ref_from_raw(handle) }))
+ }
+
+ /// Searches for groups in the Remote with a given prefix.
+ ///
+ /// # Arguments
+ ///
+ /// * `prefix` - Prefix of name for groups
+ pub fn search_groups<S: BnStrCompatible>(
+ &self,
+ prefix: S,
+ ) -> Result<(Array<GroupId>, Array<BnString>), ()> {
+ let prefix = prefix.into_bytes_with_nul();
+ let mut count = 0;
+ let mut group_ids = std::ptr::null_mut();
+ let mut group_names = std::ptr::null_mut();
+
+ let success = unsafe {
+ BNRemoteSearchGroups(
+ self.handle.as_ptr(),
+ prefix.as_ref().as_ptr() as *const c_char,
+ &mut group_ids,
+ &mut group_names,
+ &mut count,
+ )
+ };
+ if !success {
+ return Err(());
+ }
+ Ok(unsafe {
+ (
+ Array::new(group_ids, count, ()),
+ Array::new(group_names, count, ()),
+ )
+ })
+ }
+
+ /// Pulls the list of groups from the Remote.
+ /// This function is only available to accounts with admin status on the Remote.
+ pub fn pull_groups(&self) -> Result<(), ()> {
+ self.pull_groups_with_progress(NoProgressCallback)
+ }
+
+ /// Pulls the list of groups from the Remote.
+ /// This function is only available to accounts with admin status on the Remote.
+ ///
+ /// # Arguments
+ ///
+ /// * `progress` - Function to call for progress updates
+ pub fn pull_groups_with_progress<F: ProgressCallback>(
+ &self,
+ mut progress: F,
+ ) -> Result<(), ()> {
+ let success = unsafe {
+ BNRemotePullGroups(
+ self.handle.as_ptr(),
+ Some(F::cb_progress_callback),
+ &mut progress as *mut F as *mut c_void,
+ )
+ };
+ success.then_some(()).ok_or(())
+ }
+
+ /// Creates a new group on the remote (and pull it).
+ /// This function is only available to accounts with admin status on the Remote.
+ ///
+ /// # Arguments
+ ///
+ /// * `name` - Group name
+ /// * `usernames` - List of usernames of users in the group
+ pub fn create_group<N, I>(&self, name: N, usernames: I) -> Result<Ref<RemoteGroup>, ()>
+ where
+ N: BnStrCompatible,
+ I: IntoIterator,
+ I::Item: BnStrCompatible,
+ {
+ let name = name.into_bytes_with_nul();
+ let usernames: Vec<_> = usernames
+ .into_iter()
+ .map(|s| s.into_bytes_with_nul())
+ .collect();
+ let mut username_ptrs: Vec<_> = usernames
+ .iter()
+ .map(|s| s.as_ref().as_ptr() as *const c_char)
+ .collect();
+
+ let value = unsafe {
+ BNRemoteCreateGroup(
+ self.handle.as_ptr(),
+ name.as_ref().as_ptr() as *const c_char,
+ username_ptrs.as_mut_ptr(),
+ username_ptrs.len(),
+ )
+ };
+ NonNull::new(value)
+ .map(|handle| unsafe { RemoteGroup::ref_from_raw(handle) })
+ .ok_or(())
+ }
+
+ /// Pushes an updated Group object to the Remote.
+ /// This function is only available to accounts with admin status on the Remote.
+ ///
+ /// # Arguments
+ ///
+ /// * `group` - Group object which has been updated
+ /// * `extra_fields` - Extra HTTP fields to send with the update
+ pub fn push_group<I, K, V>(&self, group: &RemoteGroup, extra_fields: I) -> Result<(), ()>
+ where
+ I: IntoIterator<Item = (K, V)>,
+ K: BnStrCompatible,
+ V: BnStrCompatible,
+ {
+ let (keys, values): (Vec<_>, Vec<_>) = extra_fields
+ .into_iter()
+ .map(|(k, v)| (k.into_bytes_with_nul(), v.into_bytes_with_nul()))
+ .unzip();
+ let mut keys_raw: Vec<_> = keys
+ .iter()
+ .map(|s| s.as_ref().as_ptr() as *const c_char)
+ .collect();
+ let mut values_raw: Vec<_> = values
+ .iter()
+ .map(|s| s.as_ref().as_ptr() as *const c_char)
+ .collect();
+
+ let success = unsafe {
+ BNRemotePushGroup(
+ self.handle.as_ptr(),
+ group.handle.as_ptr(),
+ keys_raw.as_mut_ptr(),
+ values_raw.as_mut_ptr(),
+ keys.len(),
+ )
+ };
+ success.then_some(()).ok_or(())
+ }
+
+ /// Deletes the specified group from the remote.
+ ///
+ /// NOTE: This function is only available to accounts with admin status on the Remote
+ ///
+ /// # Arguments
+ ///
+ /// * `group` - Reference to the group to delete.
+ pub fn delete_group(&self, group: &RemoteGroup) -> Result<(), ()> {
+ let success = unsafe { BNRemoteDeleteGroup(self.handle.as_ptr(), group.handle.as_ptr()) };
+ success.then_some(()).ok_or(())
+ }
+
+ /// Retrieves the list of users in the project.
+ ///
+ /// NOTE: If users have not been pulled, they will be pulled upon calling this.
+ ///
+ /// NOTE: This function is only available to accounts with admin status on the Remote
+ pub fn users(&self) -> Result<Array<RemoteUser>, ()> {
+ if !self.has_pulled_users() {
+ self.pull_users()?;
+ }
+ let mut count = 0;
+ let value = unsafe { BNRemoteGetUsers(self.handle.as_ptr(), &mut count) };
+ if value.is_null() {
+ return Err(());
+ }
+ Ok(unsafe { Array::new(value, count, ()) })
+ }
+
+ /// Retrieves a specific user in the project by their ID.
+ ///
+ /// NOTE: If users have not been pulled, they will be pulled upon calling this.
+ ///
+ /// NOTE: This function is only available to accounts with admin status on the Remote
+ ///
+ /// # Arguments
+ ///
+ /// * `id` - The identifier of the user to retrieve.
+ pub fn get_user_by_id<S: BnStrCompatible>(&self, id: S) -> Result<Option<Ref<RemoteUser>>, ()> {
+ if !self.has_pulled_users() {
+ self.pull_users()?;
+ }
+ let id = id.into_bytes_with_nul();
+ let value = unsafe {
+ BNRemoteGetUserById(self.handle.as_ptr(), id.as_ref().as_ptr() as *const c_char)
+ };
+ Ok(NonNull::new(value).map(|handle| unsafe { RemoteUser::ref_from_raw(handle) }))
+ }
+
+ /// Retrieves a specific user in the project by their username.
+ ///
+ /// NOTE: If users have not been pulled, they will be pulled upon calling this.
+ ///
+ /// NOTE: This function is only available to accounts with admin status on the Remote
+ ///
+ /// # Arguments
+ ///
+ /// * `username` - The username of the user to retrieve.
+ pub fn get_user_by_username<S: BnStrCompatible>(
+ &self,
+ username: S,
+ ) -> Result<Option<Ref<RemoteUser>>, ()> {
+ if !self.has_pulled_users() {
+ self.pull_users()?;
+ }
+ let username = username.into_bytes_with_nul();
+ let value = unsafe {
+ BNRemoteGetUserByUsername(
+ self.handle.as_ptr(),
+ username.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ Ok(NonNull::new(value).map(|handle| unsafe { RemoteUser::ref_from_raw(handle) }))
+ }
+
+ /// Retrieves the user object for the currently connected user.
+ ///
+ /// NOTE: If users have not been pulled, they will be pulled upon calling this.
+ ///
+ /// NOTE: This function is only available to accounts with admin status on the Remote
+ pub fn current_user(&self) -> Result<Option<Ref<RemoteUser>>, ()> {
+ if !self.has_pulled_users() {
+ self.pull_users()?;
+ }
+ let value = unsafe { BNRemoteGetCurrentUser(self.handle.as_ptr()) };
+ Ok(NonNull::new(value).map(|handle| unsafe { RemoteUser::ref_from_raw(handle) }))
+ }
+
+ /// Searches for users in the project with a given prefix.
+ ///
+ /// # Arguments
+ ///
+ /// * `prefix` - The prefix to search for in usernames.
+ pub fn search_users<S: BnStrCompatible>(
+ &self,
+ prefix: S,
+ ) -> Result<(Array<BnString>, Array<BnString>), ()> {
+ let prefix = prefix.into_bytes_with_nul();
+ let mut count = 0;
+ let mut user_ids = std::ptr::null_mut();
+ let mut usernames = std::ptr::null_mut();
+ let success = unsafe {
+ BNRemoteSearchUsers(
+ self.handle.as_ptr(),
+ prefix.as_ref().as_ptr() as *const c_char,
+ &mut user_ids,
+ &mut usernames,
+ &mut count,
+ )
+ };
+
+ if !success {
+ return Err(());
+ }
+ assert!(!user_ids.is_null());
+ assert!(!usernames.is_null());
+ Ok(unsafe {
+ (
+ Array::new(user_ids, count, ()),
+ Array::new(usernames, count, ()),
+ )
+ })
+ }
+
+ /// Pulls the list of users from the remote.
+ ///
+ /// NOTE: This function is only available to accounts with admin status on the Remote.
+ /// Non-admin accounts attempting to call this function will pull an empty list of users.
+ pub fn pull_users(&self) -> Result<(), ()> {
+ self.pull_users_with_progress(NoProgressCallback)
+ }
+
+ /// Pulls the list of users from the remote.
+ ///
+ /// NOTE: This function is only available to accounts with admin status on the Remote.
+ /// Non-admin accounts attempting to call this function will pull an empty list of users.
+ ///
+ /// # Arguments
+ ///
+ /// * `progress` - Closure called to report progress. Takes current and total progress counts.
+ pub fn pull_users_with_progress<P: ProgressCallback>(&self, mut progress: P) -> Result<(), ()> {
+ let success = unsafe {
+ BNRemotePullUsers(
+ self.handle.as_ptr(),
+ Some(P::cb_progress_callback),
+ &mut progress as *mut P as *mut c_void,
+ )
+ };
+ success.then_some(()).ok_or(())
+ }
+
+ /// Creates a new user on the remote and returns a reference to the created user.
+ ///
+ /// NOTE: This function is only available to accounts with admin status on the Remote
+ ///
+ /// # Arguments
+ ///
+ /// * Various details about the new user to be created.
+ pub fn create_user<U: BnStrCompatible, E: BnStrCompatible, P: BnStrCompatible>(
+ &self,
+ username: U,
+ email: E,
+ is_active: bool,
+ password: P,
+ group_ids: &[u64],
+ user_permission_ids: &[u64],
+ ) -> Result<Ref<RemoteUser>, ()> {
+ let username = username.into_bytes_with_nul();
+ let email = email.into_bytes_with_nul();
+ let password = password.into_bytes_with_nul();
+
+ let value = unsafe {
+ BNRemoteCreateUser(
+ self.handle.as_ptr(),
+ username.as_ref().as_ptr() as *const c_char,
+ email.as_ref().as_ptr() as *const c_char,
+ is_active,
+ password.as_ref().as_ptr() as *const c_char,
+ group_ids.as_ptr(),
+ group_ids.len(),
+ user_permission_ids.as_ptr(),
+ user_permission_ids.len(),
+ )
+ };
+ NonNull::new(value)
+ .map(|handle| unsafe { RemoteUser::ref_from_raw(handle) })
+ .ok_or(())
+ }
+
+ /// Pushes updates to the specified user on the remote.
+ ///
+ /// NOTE: This function is only available to accounts with admin status on the Remote
+ ///
+ /// # Arguments
+ ///
+ /// * `user` - Reference to the `RemoteUser` object to push.
+ /// * `extra_fields` - Optional extra fields to send with the update.
+ pub fn push_user<I, K, V>(&self, user: &RemoteUser, extra_fields: I) -> Result<(), ()>
+ where
+ I: Iterator<Item = (K, V)>,
+ K: BnStrCompatible,
+ V: BnStrCompatible,
+ {
+ let (keys, values): (Vec<_>, Vec<_>) = extra_fields
+ .into_iter()
+ .map(|(k, v)| (k.into_bytes_with_nul(), v.into_bytes_with_nul()))
+ .unzip();
+ let mut keys_raw: Vec<_> = keys
+ .iter()
+ .map(|s| s.as_ref().as_ptr() as *const c_char)
+ .collect();
+ let mut values_raw: Vec<_> = values
+ .iter()
+ .map(|s| s.as_ref().as_ptr() as *const c_char)
+ .collect();
+ let success = unsafe {
+ BNRemotePushUser(
+ self.handle.as_ptr(),
+ user.handle.as_ptr(),
+ keys_raw.as_mut_ptr(),
+ values_raw.as_mut_ptr(),
+ keys_raw.len(),
+ )
+ };
+ success.then_some(()).ok_or(())
+ }
+
+ // TODO identify the request and ret type of this function, it seems to use a C++ implementation of
+ // HTTP requests, composed mostly of `std:vector`.
+ //pub fn request(&self) {
+ // unsafe { BNRemoteRequest(self.handle.as_ptr(), todo!(), todo!()) }
+ //}
+}
+
+impl PartialEq for Remote {
+ fn eq(&self, other: &Self) -> bool {
+ // don't pull metadata if we hand't yet
+ if !self.has_loaded_metadata() || other.has_loaded_metadata() {
+ self.address() == other.address()
+ } else if let Some((slf, oth)) = self.unique_id().ok().zip(other.unique_id().ok()) {
+ slf == oth
+ } else {
+ // falback to comparing address
+ self.address() == other.address()
+ }
+ }
+}
+impl Eq for Remote {}
+
+impl ToOwned for Remote {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for Remote {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: NonNull::new(BNNewRemoteReference(handle.handle.as_ptr())).unwrap(),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeRemote(handle.handle.as_ptr());
+ }
+}
+
+impl CoreArrayProvider for Remote {
+ type Raw = *mut BNRemote;
+ type Context = ();
+ type Wrapped<'a> = Guard<'a, Self>;
+}
+
+unsafe impl CoreArrayProviderInner for Remote {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeRemoteList(raw, count)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
+ let raw_ptr = NonNull::new(*raw).unwrap();
+ Guard::new(Self::from_raw(raw_ptr), context)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct ConnectionOptions {
+ pub username: String,
+ /// Provide this if you want to authenticate with a password.
+ pub password: Option<String>,
+ /// Provide this if you want to authenticate with a token.
+ ///
+ /// If you do not have a token you can use [ConnectionOptions::self].
+ pub token: Option<String>,
+}
+
+impl ConnectionOptions {
+ pub fn new_with_token(username: String, token: String) -> Self {
+ Self {
+ username,
+ token: Some(token),
+ password: None,
+ }
+ }
+
+ pub fn new_with_password(username: String, password: String) -> Self {
+ Self {
+ username,
+ token: None,
+ password: Some(password),
+ }
+ }
+
+ pub fn with_token(self, token: String) -> Self {
+ Self {
+ token: Some(token),
+ ..self
+ }
+ }
+
+ pub fn with_password(self, token: String) -> Self {
+ Self {
+ token: Some(token),
+ ..self
+ }
+ }
+
+ pub fn from_enterprise() -> Result<Self, ()> {
+ // TODO: Check if enterprise is initialized and error if not.
+ let username = enterprise::server_username();
+ let token = enterprise::server_token();
+ Ok(Self::new_with_token(
+ username.to_string(),
+ token.to_string(),
+ ))
+ }
+
+ // TODO: from_secrets_provider
+}
diff --git a/rust/src/collaboration/snapshot.rs b/rust/src/collaboration/snapshot.rs
new file mode 100644
index 00000000..9f8f3693
--- /dev/null
+++ b/rust/src/collaboration/snapshot.rs
@@ -0,0 +1,368 @@
+use std::ffi::{c_char, c_void};
+use std::ptr::NonNull;
+use std::time::SystemTime;
+
+use super::{sync, Remote, RemoteFile, RemoteProject};
+use crate::binary_view::{BinaryView, BinaryViewExt};
+use crate::collaboration::undo::{RemoteUndoEntry, RemoteUndoEntryId};
+use crate::database::snapshot::Snapshot;
+use crate::progress::{NoProgressCallback, ProgressCallback};
+use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
+use crate::string::{BnStrCompatible, BnString};
+use binaryninjacore_sys::*;
+
+// TODO: RemoteSnapshotId ?
+
+#[repr(transparent)]
+pub struct RemoteSnapshot {
+ pub(crate) handle: NonNull<BNCollaborationSnapshot>,
+}
+
+impl RemoteSnapshot {
+ pub(crate) unsafe fn from_raw(handle: NonNull<BNCollaborationSnapshot>) -> Self {
+ Self { handle }
+ }
+
+ pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNCollaborationSnapshot>) -> Ref<Self> {
+ Ref::new(Self { handle })
+ }
+
+ /// Get the remote snapshot associated with a local snapshot (if it exists)
+ pub fn get_for_local_snapshot(snapshot: &Snapshot) -> Result<Option<Ref<RemoteSnapshot>>, ()> {
+ sync::get_remote_snapshot_from_local(snapshot)
+ }
+
+ /// Owning File
+ pub fn file(&self) -> Result<Ref<RemoteFile>, ()> {
+ let result = unsafe { BNCollaborationSnapshotGetFile(self.handle.as_ptr()) };
+ let raw = NonNull::new(result).ok_or(())?;
+ Ok(unsafe { RemoteFile::ref_from_raw(raw) })
+ }
+
+ /// Owning Project
+ pub fn project(&self) -> Result<Ref<RemoteProject>, ()> {
+ let result = unsafe { BNCollaborationSnapshotGetProject(self.handle.as_ptr()) };
+ let raw = NonNull::new(result).ok_or(())?;
+ Ok(unsafe { RemoteProject::ref_from_raw(raw) })
+ }
+
+ /// Owning Remote
+ pub fn remote(&self) -> Result<Ref<Remote>, ()> {
+ let result = unsafe { BNCollaborationSnapshotGetRemote(self.handle.as_ptr()) };
+ let raw = NonNull::new(result).ok_or(())?;
+ Ok(unsafe { Remote::ref_from_raw(raw) })
+ }
+
+ /// Web api endpoint url
+ pub fn url(&self) -> BnString {
+ let value = unsafe { BNCollaborationSnapshotGetUrl(self.handle.as_ptr()) };
+ assert!(!value.is_null());
+ unsafe { BnString::from_raw(value) }
+ }
+
+ /// Unique id
+ pub fn id(&self) -> BnString {
+ let value = unsafe { BNCollaborationSnapshotGetId(self.handle.as_ptr()) };
+ assert!(!value.is_null());
+ unsafe { BnString::from_raw(value) }
+ }
+
+ /// Name of snapshot
+ pub fn name(&self) -> BnString {
+ let value = unsafe { BNCollaborationSnapshotGetName(self.handle.as_ptr()) };
+ assert!(!value.is_null());
+ unsafe { BnString::from_raw(value) }
+ }
+
+ /// Get the title of a snapshot: the first line of its name
+ pub fn title(&self) -> BnString {
+ let value = unsafe { BNCollaborationSnapshotGetTitle(self.handle.as_ptr()) };
+ assert!(!value.is_null());
+ unsafe { BnString::from_raw(value) }
+ }
+
+ /// Get the description of a snapshot: the lines of its name after the first line
+ pub fn description(&self) -> BnString {
+ let value = unsafe { BNCollaborationSnapshotGetDescription(self.handle.as_ptr()) };
+ assert!(!value.is_null());
+ unsafe { BnString::from_raw(value) }
+ }
+
+ /// Get the user id of the author of a snapshot
+ pub fn author(&self) -> BnString {
+ let value = unsafe { BNCollaborationSnapshotGetAuthor(self.handle.as_ptr()) };
+ assert!(!value.is_null());
+ unsafe { BnString::from_raw(value) }
+ }
+
+ /// Get the username of the author of a snapshot, if possible (vs author which is user id)
+ pub fn author_username(&self) -> BnString {
+ let value = unsafe { BNCollaborationSnapshotGetAuthorUsername(self.handle.as_ptr()) };
+ assert!(!value.is_null());
+ unsafe { BnString::from_raw(value) }
+ }
+
+ /// Created date of Snapshot
+ pub fn created(&self) -> SystemTime {
+ let timestamp = unsafe { BNCollaborationSnapshotGetCreated(self.handle.as_ptr()) };
+ crate::ffi::time_from_bn(timestamp.try_into().unwrap())
+ }
+
+ /// Date of last modification to the snapshot
+ pub fn last_modified(&self) -> SystemTime {
+ let timestamp = unsafe { BNCollaborationSnapshotGetLastModified(self.handle.as_ptr()) };
+ crate::ffi::time_from_bn(timestamp.try_into().unwrap())
+ }
+
+ /// Hash of snapshot data (analysis and markup, etc)
+ /// No specific hash algorithm is guaranteed
+ pub fn hash(&self) -> BnString {
+ let value = unsafe { BNCollaborationSnapshotGetHash(self.handle.as_ptr()) };
+ assert!(!value.is_null());
+ unsafe { BnString::from_raw(value) }
+ }
+
+ /// Hash of file contents in snapshot
+ /// No specific hash algorithm is guaranteed
+ pub fn snapshot_file_hash(&self) -> BnString {
+ let value = unsafe { BNCollaborationSnapshotGetSnapshotFileHash(self.handle.as_ptr()) };
+ assert!(!value.is_null());
+ unsafe { BnString::from_raw(value) }
+ }
+
+ /// If the snapshot has pulled undo entries yet
+ pub fn has_pulled_undo_entries(&self) -> bool {
+ unsafe { BNCollaborationSnapshotHasPulledUndoEntries(self.handle.as_ptr()) }
+ }
+
+ /// If the snapshot has been finalized on the server and is no longer editable
+ pub fn is_finalized(&self) -> bool {
+ unsafe { BNCollaborationSnapshotIsFinalized(self.handle.as_ptr()) }
+ }
+
+ /// List of ids of all remote parent Snapshots
+ pub fn parent_ids(&self) -> Result<Array<BnString>, ()> {
+ let mut count = 0;
+ let raw = unsafe { BNCollaborationSnapshotGetParentIds(self.handle.as_ptr(), &mut count) };
+ (!raw.is_null())
+ .then(|| unsafe { Array::new(raw, count, ()) })
+ .ok_or(())
+ }
+
+ /// List of ids of all remote child Snapshots
+ pub fn child_ids(&self) -> Result<Array<BnString>, ()> {
+ let mut count = 0;
+ let raw = unsafe { BNCollaborationSnapshotGetChildIds(self.handle.as_ptr(), &mut count) };
+ (!raw.is_null())
+ .then(|| unsafe { Array::new(raw, count, ()) })
+ .ok_or(())
+ }
+
+ /// List of all parent Snapshot objects
+ pub fn parents(&self) -> Result<Array<RemoteSnapshot>, ()> {
+ let mut count = 0;
+ let raw = unsafe { BNCollaborationSnapshotGetParents(self.handle.as_ptr(), &mut count) };
+ (!raw.is_null())
+ .then(|| unsafe { Array::new(raw, count, ()) })
+ .ok_or(())
+ }
+
+ /// List of all child Snapshot objects
+ pub fn children(&self) -> Result<Array<RemoteSnapshot>, ()> {
+ let mut count = 0;
+ let raw = unsafe { BNCollaborationSnapshotGetChildren(self.handle.as_ptr(), &mut count) };
+ (!raw.is_null())
+ .then(|| unsafe { Array::new(raw, count, ()) })
+ .ok_or(())
+ }
+
+ /// Get the list of undo entries stored in this snapshot.
+ ///
+ /// NOTE: If undo entries have not been pulled, they will be pulled upon calling this.
+ pub fn undo_entries(&self) -> Result<Array<RemoteUndoEntry>, ()> {
+ if !self.has_pulled_undo_entries() {
+ self.pull_undo_entries()?;
+ }
+ let mut count = 0;
+ let raw =
+ unsafe { BNCollaborationSnapshotGetUndoEntries(self.handle.as_ptr(), &mut count) };
+ (!raw.is_null())
+ .then(|| unsafe { Array::new(raw, count, ()) })
+ .ok_or(())
+ }
+
+ /// Get a specific Undo Entry in the Snapshot by its id
+ ///
+ /// NOTE: If undo entries have not been pulled, they will be pulled upon calling this.
+ pub fn get_undo_entry_by_id(
+ &self,
+ id: RemoteUndoEntryId,
+ ) -> Result<Option<Ref<RemoteUndoEntry>>, ()> {
+ if !self.has_pulled_undo_entries() {
+ self.pull_undo_entries()?;
+ }
+ let raw = unsafe { BNCollaborationSnapshotGetUndoEntryById(self.handle.as_ptr(), id.0) };
+ Ok(NonNull::new(raw).map(|handle| unsafe { RemoteUndoEntry::ref_from_raw(handle) }))
+ }
+
+ /// Pull the list of Undo Entries from the Remote.
+ pub fn pull_undo_entries(&self) -> Result<(), ()> {
+ self.pull_undo_entries_with_progress(NoProgressCallback)
+ }
+
+ /// Pull the list of Undo Entries from the Remote.
+ pub fn pull_undo_entries_with_progress<P: ProgressCallback>(
+ &self,
+ mut progress: P,
+ ) -> Result<(), ()> {
+ let success = unsafe {
+ BNCollaborationSnapshotPullUndoEntries(
+ self.handle.as_ptr(),
+ Some(P::cb_progress_callback),
+ &mut progress as *mut P as *mut c_void,
+ )
+ };
+ success.then_some(()).ok_or(())
+ }
+
+ /// Create a new Undo Entry in this snapshot.
+ pub fn create_undo_entry<S: BnStrCompatible>(
+ &self,
+ parent: Option<u64>,
+ data: S,
+ ) -> Result<Ref<RemoteUndoEntry>, ()> {
+ let data = data.into_bytes_with_nul();
+ let value = unsafe {
+ BNCollaborationSnapshotCreateUndoEntry(
+ self.handle.as_ptr(),
+ parent.is_some(),
+ parent.unwrap_or(0),
+ data.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ let handle = NonNull::new(value).ok_or(())?;
+ Ok(unsafe { RemoteUndoEntry::ref_from_raw(handle) })
+ }
+
+ /// Mark a snapshot as Finalized, committing it to the Remote, preventing future updates,
+ /// and allowing snapshots to be children of it.
+ pub fn finalize(&self) -> Result<(), ()> {
+ let success = unsafe { BNCollaborationSnapshotFinalize(self.handle.as_ptr()) };
+ success.then_some(()).ok_or(())
+ }
+
+ // TODO what kind of struct is this and how to free it?
+ ///// Download the contents of the file in the Snapshot.
+ //pub fn download_snapshot_file<P: ProgressCallback>(
+ // &self,
+ // mut progress: P,
+ //) -> Result<BnData, ()> {
+ // let mut data = ptr::null_mut();
+ // let mut count = 0;
+ // let success = unsafe {
+ // BNCollaborationSnapshotDownloadSnapshotFile(
+ // self.handle.as_ptr(),
+ // Some(P::cb_progress_callback),
+ // &mut progress as *mut P as *mut ffi::c_void,
+ // &mut data,
+ // &mut count,
+ // )
+ // };
+ // todo!();
+ //}
+ //
+ ///// Download the snapshot fields blob, compatible with KeyValueStore.
+ //pub fn download<P: ProgressCallback>(
+ // &self,
+ // mut progress: P,
+ //) -> Result<BnData, ()> {
+ // let mut data = ptr::null_mut();
+ // let mut count = 0;
+ // let success = unsafe {
+ // BNCollaborationSnapshotDownload(
+ // self.handle.as_ptr(),
+ // Some(P::cb_progress_callback),
+ // &mut progress as *mut P as *mut ffi::c_void,
+ // &mut data,
+ // &mut count,
+ // )
+ // };
+ // todo!();
+ //}
+ //
+ ///// Download the analysis cache fields blob, compatible with KeyValueStore.
+ //pub fn download_analysis_cache<P: ProgressCallback>(
+ // &self,
+ // mut progress: P,
+ //) -> Result<BnData, ()> {
+ // let mut data = ptr::null_mut();
+ // let mut count = 0;
+ // let success = unsafe {
+ // BNCollaborationSnapshotDownloadAnalysisCache(
+ // self.handle.as_ptr(),
+ // Some(P::cb_progress_callback),
+ // &mut progress as *mut P as *mut ffi::c_void,
+ // &mut data,
+ // &mut count,
+ // )
+ // };
+ // todo!();
+ //}
+
+ /// Get the local snapshot associated with a remote snapshot (if it exists)
+ pub fn get_local_snapshot(&self, bv: &BinaryView) -> Result<Option<Ref<Snapshot>>, ()> {
+ let Some(db) = bv.file().database() else {
+ return Ok(None);
+ };
+ sync::get_local_snapshot_for_remote(self, &db)
+ }
+
+ pub fn analysis_cache_build_id(&self) -> u64 {
+ unsafe { BNCollaborationSnapshotGetAnalysisCacheBuildId(self.handle.as_ptr()) }
+ }
+}
+
+impl PartialEq for RemoteSnapshot {
+ fn eq(&self, other: &Self) -> bool {
+ self.id() == other.id()
+ }
+}
+impl Eq for RemoteSnapshot {}
+
+impl ToOwned for RemoteSnapshot {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for RemoteSnapshot {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: NonNull::new(BNNewCollaborationSnapshotReference(handle.handle.as_ptr()))
+ .unwrap(),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeCollaborationSnapshot(handle.handle.as_ptr());
+ }
+}
+
+impl CoreArrayProvider for RemoteSnapshot {
+ type Raw = *mut BNCollaborationSnapshot;
+ type Context = ();
+ type Wrapped<'a> = Guard<'a, RemoteSnapshot>;
+}
+
+unsafe impl CoreArrayProviderInner for RemoteSnapshot {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeCollaborationSnapshotList(raw, count)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
+ let raw_ptr = NonNull::new(*raw).unwrap();
+ Guard::new(Self::from_raw(raw_ptr), context)
+ }
+}
diff --git a/rust/src/collaboration/sync.rs b/rust/src/collaboration/sync.rs
new file mode 100644
index 00000000..14e18856
--- /dev/null
+++ b/rust/src/collaboration/sync.rs
@@ -0,0 +1,941 @@
+use super::{
+ Changeset, MergeConflict, Remote, RemoteFile, RemoteFolder, RemoteProject, RemoteSnapshot,
+};
+use binaryninjacore_sys::*;
+use std::ffi::{c_char, c_void};
+use std::mem::ManuallyDrop;
+use std::ptr::NonNull;
+
+use crate::binary_view::{BinaryView, BinaryViewExt};
+use crate::database::{snapshot::Snapshot, Database};
+use crate::file_metadata::FileMetadata;
+use crate::progress::{NoProgressCallback, ProgressCallback};
+use crate::project::file::ProjectFile;
+use crate::rc::Ref;
+use crate::string::{BnStrCompatible, BnString};
+use crate::type_archive::{TypeArchive, TypeArchiveMergeConflict};
+
+// TODO: PathBuf
+/// Get the default directory path for a remote Project. This is based off the Setting for
+/// collaboration.directory, the project's id, and the project's remote's id.
+pub fn default_project_path(project: &RemoteProject) -> Result<BnString, ()> {
+ let result = unsafe { BNCollaborationDefaultProjectPath(project.handle.as_ptr()) };
+ let success = !result.is_null();
+ success
+ .then(|| unsafe { BnString::from_raw(result) })
+ .ok_or(())
+}
+
+// TODO: PathBuf
+// Get the default filepath for a remote File. This is based off the Setting for
+// collaboration.directory, the file's id, the file's project's id, and the file's
+// remote's id.
+pub fn default_file_path(file: &RemoteFile) -> Result<BnString, ()> {
+ let result = unsafe { BNCollaborationDefaultFilePath(file.handle.as_ptr()) };
+ let success = !result.is_null();
+ success
+ .then(|| unsafe { BnString::from_raw(result) })
+ .ok_or(())
+}
+
+// TODO: AsRef<Path>
+/// Download a file from its remote, saving all snapshots to a database in the
+/// specified location. Returns a FileContext for opening the file later.
+///
+/// * `file` - Remote File to download and open
+/// * `db_path` - File path for saved database
+pub fn download_file<S: BnStrCompatible>(
+ file: &RemoteFile,
+ db_path: S,
+) -> Result<Ref<FileMetadata>, ()> {
+ download_file_with_progress(file, db_path, NoProgressCallback)
+}
+
+// TODO: AsRef<Path>
+/// Download a file from its remote, saving all snapshots to a database in the
+/// specified location. Returns a FileContext for opening the file later.
+///
+/// * `file` - Remote File to download and open
+/// * `db_path` - File path for saved database
+/// * `progress` - Function to call for progress updates
+pub fn download_file_with_progress<S: BnStrCompatible, F: ProgressCallback>(
+ file: &RemoteFile,
+ db_path: S,
+ mut progress: F,
+) -> Result<Ref<FileMetadata>, ()> {
+ let db_path = db_path.into_bytes_with_nul();
+ let result = unsafe {
+ BNCollaborationDownloadFile(
+ file.handle.as_ptr(),
+ db_path.as_ref().as_ptr() as *const c_char,
+ Some(F::cb_progress_callback),
+ &mut progress as *mut F as *mut c_void,
+ )
+ };
+ let success = !result.is_null();
+ success
+ .then(|| unsafe { Ref::new(FileMetadata::from_raw(result)) })
+ .ok_or(())
+}
+
+/// Upload a file, with database, to the remote under the given project
+///
+/// * `project` - Remote project under which to place the new file
+/// * `parent_folder` - Optional parent folder in which to place this file
+/// * `metadata` - Local file with database
+/// * `name_changeset` - Function to call for naming a pushed changeset, if necessary
+pub fn upload_database<N: NameChangeset>(
+ project: &RemoteProject,
+ parent_folder: Option<&RemoteFolder>,
+ metadata: &FileMetadata,
+ name_changeset: N,
+) -> Result<Ref<RemoteFile>, ()> {
+ upload_database_with_progress(
+ project,
+ parent_folder,
+ metadata,
+ name_changeset,
+ NoProgressCallback,
+ )
+}
+
+/// Upload a file, with database, to the remote under the given project
+///
+/// * `metadata` - Local file with database
+/// * `project` - Remote project under which to place the new file
+/// * `parent_folder` - Optional parent folder in which to place this file
+/// * `name_changeset` - Function to call for naming a pushed changeset, if necessary
+/// * `progress` - Function to call for progress updates
+pub fn upload_database_with_progress<P: ProgressCallback, N: NameChangeset>(
+ project: &RemoteProject,
+ parent_folder: Option<&RemoteFolder>,
+ metadata: &FileMetadata,
+ mut name_changeset: N,
+ mut progress: P,
+) -> Result<Ref<RemoteFile>, ()> {
+ let folder_raw = parent_folder.map_or(std::ptr::null_mut(), |h| h.handle.as_ptr());
+ let result = unsafe {
+ BNCollaborationUploadDatabase(
+ metadata.handle,
+ project.handle.as_ptr(),
+ folder_raw,
+ Some(P::cb_progress_callback),
+ &mut progress as *mut P as *mut c_void,
+ Some(N::cb_name_changeset),
+ &mut name_changeset as *mut N as *mut c_void,
+ )
+ };
+ NonNull::new(result)
+ .map(|raw| unsafe { RemoteFile::ref_from_raw(raw) })
+ .ok_or(())
+}
+
+/// Test if a database is valid for use in collaboration
+pub fn is_collaboration_database(database: &Database) -> bool {
+ unsafe { BNCollaborationIsCollaborationDatabase(database.handle.as_ptr()) }
+}
+
+/// Get the Remote for a Database
+pub fn get_remote_for_local_database(database: &Database) -> Result<Option<Ref<Remote>>, ()> {
+ let mut value = std::ptr::null_mut();
+ let success =
+ unsafe { BNCollaborationGetRemoteForLocalDatabase(database.handle.as_ptr(), &mut value) };
+ success
+ .then(|| NonNull::new(value).map(|handle| unsafe { Remote::ref_from_raw(handle) }))
+ .ok_or(())
+}
+
+/// Get the Remote for a BinaryView
+pub fn get_remote_for_binary_view(bv: &BinaryView) -> Result<Option<Ref<Remote>>, ()> {
+ let Some(db) = bv.file().database() else {
+ return Ok(None);
+ };
+ get_remote_for_local_database(&db)
+}
+
+/// Get the Remote Project for a Database, returning the Remote project from one of the
+/// connected remotes, or None if not found or if projects are not pulled
+pub fn get_remote_project_for_local_database(
+ database: &Database,
+) -> Result<Option<Ref<RemoteProject>>, ()> {
+ let mut value = std::ptr::null_mut();
+ let success = unsafe {
+ BNCollaborationGetRemoteProjectForLocalDatabase(database.handle.as_ptr(), &mut value)
+ };
+ success
+ .then(|| NonNull::new(value).map(|handle| unsafe { RemoteProject::ref_from_raw(handle) }))
+ .ok_or(())
+}
+
+/// Get the Remote File for a Database
+pub fn get_remote_file_for_local_database(
+ database: &Database,
+) -> Result<Option<Ref<RemoteFile>>, ()> {
+ let mut value = std::ptr::null_mut();
+ let success = unsafe {
+ BNCollaborationGetRemoteFileForLocalDatabase(database.handle.as_ptr(), &mut value)
+ };
+ success
+ .then(|| NonNull::new(value).map(|handle| unsafe { RemoteFile::ref_from_raw(handle) }))
+ .ok_or(())
+}
+
+/// Add a snapshot to the id map in a database
+pub fn assign_snapshot_map(
+ local_snapshot: &Snapshot,
+ remote_snapshot: &RemoteSnapshot,
+) -> Result<(), ()> {
+ let success = unsafe {
+ BNCollaborationAssignSnapshotMap(
+ local_snapshot.handle.as_ptr(),
+ remote_snapshot.handle.as_ptr(),
+ )
+ };
+ success.then_some(()).ok_or(())
+}
+
+/// Get the remote snapshot associated with a local snapshot (if it exists)
+pub fn get_remote_snapshot_from_local(snap: &Snapshot) -> Result<Option<Ref<RemoteSnapshot>>, ()> {
+ let mut value = std::ptr::null_mut();
+ let success =
+ unsafe { BNCollaborationGetRemoteSnapshotFromLocal(snap.handle.as_ptr(), &mut value) };
+ success
+ .then(|| NonNull::new(value).map(|handle| unsafe { RemoteSnapshot::ref_from_raw(handle) }))
+ .ok_or(())
+}
+
+/// Get the local snapshot associated with a remote snapshot (if it exists)
+pub fn get_local_snapshot_for_remote(
+ snapshot: &RemoteSnapshot,
+ database: &Database,
+) -> Result<Option<Ref<Snapshot>>, ()> {
+ let mut value = std::ptr::null_mut();
+ let success = unsafe {
+ BNCollaborationGetLocalSnapshotFromRemote(
+ snapshot.handle.as_ptr(),
+ database.handle.as_ptr(),
+ &mut value,
+ )
+ };
+ success
+ .then(|| NonNull::new(value).map(|handle| unsafe { Snapshot::ref_from_raw(handle) }))
+ .ok_or(())
+}
+
+pub fn download_database<S>(file: &RemoteFile, location: S, force: bool) -> Result<(), ()>
+where
+ S: BnStrCompatible,
+{
+ download_database_with_progress(file, location, force, NoProgressCallback)
+}
+
+pub fn download_database_with_progress<S, F>(
+ file: &RemoteFile,
+ location: S,
+ force: bool,
+ mut progress: F,
+) -> Result<(), ()>
+where
+ S: BnStrCompatible,
+ F: ProgressCallback,
+{
+ let db_path = location.into_bytes_with_nul();
+ let success = unsafe {
+ BNCollaborationDownloadDatabaseForFile(
+ file.handle.as_ptr(),
+ db_path.as_ref().as_ptr() as *const c_char,
+ force,
+ Some(F::cb_progress_callback),
+ &mut progress as *mut _ as *mut c_void,
+ )
+ };
+ success.then_some(()).ok_or(())
+}
+
+/// Completely sync a database, pushing/pulling/merging/applying changes
+///
+/// * `database` - Database to sync
+/// * `file` - File to sync with
+/// * `conflict_handler` - Function to call to resolve snapshot conflicts
+/// * `name_changeset` - Function to call for naming a pushed changeset, if necessary
+pub fn sync_database<C: DatabaseConflictHandler, N: NameChangeset>(
+ database: &Database,
+ file: &RemoteFile,
+ conflict_handler: C,
+ name_changeset: N,
+) -> Result<(), ()> {
+ sync_database_with_progress(
+ database,
+ file,
+ conflict_handler,
+ name_changeset,
+ NoProgressCallback,
+ )
+}
+
+/// Completely sync a database, pushing/pulling/merging/applying changes
+///
+/// * `database` - Database to sync
+/// * `file` - File to sync with
+/// * `conflict_handler` - Function to call to resolve snapshot conflicts
+/// * `name_changeset` - Function to call for naming a pushed changeset, if necessary
+/// * `progress` - Function to call for progress updates
+pub fn sync_database_with_progress<
+ C: DatabaseConflictHandler,
+ P: ProgressCallback,
+ N: NameChangeset,
+>(
+ database: &Database,
+ file: &RemoteFile,
+ mut conflict_handler: C,
+ mut name_changeset: N,
+ mut progress: P,
+) -> Result<(), ()> {
+ let success = unsafe {
+ BNCollaborationSyncDatabase(
+ database.handle.as_ptr(),
+ file.handle.as_ptr(),
+ Some(C::cb_handle_conflict),
+ &mut conflict_handler as *mut C as *mut c_void,
+ Some(P::cb_progress_callback),
+ &mut progress as *mut P as *mut c_void,
+ Some(N::cb_name_changeset),
+ &mut name_changeset as *mut N as *mut c_void,
+ )
+ };
+ success.then_some(()).ok_or(())
+}
+
+/// Pull updated snapshots from the remote. Merge local changes with remote changes and
+/// potentially create a new snapshot for unsaved changes, named via name_changeset.
+///
+/// * `database` - Database to pull
+/// * `file` - Remote File to pull to
+/// * `conflict_handler` - Function to call to resolve snapshot conflicts
+/// * `name_changeset` - Function to call for naming a pushed changeset, if necessary
+pub fn pull_database<C: DatabaseConflictHandler, N: NameChangeset>(
+ database: &Database,
+ file: &RemoteFile,
+ conflict_handler: C,
+ name_changeset: N,
+) -> Result<usize, ()> {
+ pull_database_with_progress(
+ database,
+ file,
+ conflict_handler,
+ name_changeset,
+ NoProgressCallback,
+ )
+}
+
+/// Pull updated snapshots from the remote. Merge local changes with remote changes and
+/// potentially create a new snapshot for unsaved changes, named via name_changeset.
+///
+/// * `database` - Database to pull
+/// * `file` - Remote File to pull to
+/// * `conflict_handler` - Function to call to resolve snapshot conflicts
+/// * `name_changeset` - Function to call for naming a pushed changeset, if necessary
+/// * `progress` - Function to call for progress updates
+pub fn pull_database_with_progress<
+ C: DatabaseConflictHandler,
+ P: ProgressCallback,
+ N: NameChangeset,
+>(
+ database: &Database,
+ file: &RemoteFile,
+ mut conflict_handler: C,
+ mut name_changeset: N,
+ mut progress: P,
+) -> Result<usize, ()> {
+ let mut count = 0;
+ let success = unsafe {
+ BNCollaborationPullDatabase(
+ database.handle.as_ptr(),
+ file.handle.as_ptr(),
+ &mut count,
+ Some(C::cb_handle_conflict),
+ &mut conflict_handler as *mut C as *mut c_void,
+ Some(P::cb_progress_callback),
+ &mut progress as *mut P as *mut c_void,
+ Some(N::cb_name_changeset),
+ &mut name_changeset as *mut N as *mut c_void,
+ )
+ };
+ success.then_some(count).ok_or(())
+}
+
+/// Merge all leaf snapshots in a database down to a single leaf snapshot.
+///
+/// * `database` - Database to merge
+/// * `conflict_handler` - Function to call for progress updates
+pub fn merge_database<C: DatabaseConflictHandler>(
+ database: &Database,
+ conflict_handler: C,
+) -> Result<(), ()> {
+ merge_database_with_progress(database, conflict_handler, NoProgressCallback)
+}
+
+/// Merge all leaf snapshots in a database down to a single leaf snapshot.
+///
+/// * `database` - Database to merge
+/// * `conflict_handler` - Function to call for progress updates
+/// * `progress` - Function to call to resolve snapshot conflicts
+pub fn merge_database_with_progress<C: DatabaseConflictHandler, P: ProgressCallback>(
+ database: &Database,
+ mut conflict_handler: C,
+ mut progress: P,
+) -> Result<(), ()> {
+ let success = unsafe {
+ BNCollaborationMergeDatabase(
+ database.handle.as_ptr(),
+ Some(C::cb_handle_conflict),
+ &mut conflict_handler as *mut C as *mut c_void,
+ Some(P::cb_progress_callback),
+ &mut progress as *mut P as *mut c_void,
+ )
+ };
+ success.then_some(()).ok_or(())
+}
+
+/// Push locally added snapshots to the remote
+///
+/// * `database` - Database to push
+/// * `file` - Remote File to push to
+pub fn push_database(database: &Database, file: &RemoteFile) -> Result<usize, ()> {
+ push_database_with_progress(database, file, NoProgressCallback)
+}
+
+/// Push locally added snapshots to the remote
+///
+/// * `database` - Database to push
+/// * `file` - Remote File to push to
+/// * `progress` - Function to call for progress updates
+pub fn push_database_with_progress<P: ProgressCallback>(
+ database: &Database,
+ file: &RemoteFile,
+ mut progress: P,
+) -> Result<usize, ()> {
+ let mut count = 0;
+ let success = unsafe {
+ BNCollaborationPushDatabase(
+ database.handle.as_ptr(),
+ file.handle.as_ptr(),
+ &mut count,
+ Some(P::cb_progress_callback),
+ &mut progress as *mut P as *mut c_void,
+ )
+ };
+ success.then_some(count).ok_or(())
+}
+
+/// Print debug information about a database to stdout
+pub fn dump_database(database: &Database) -> Result<(), ()> {
+ let success = unsafe { BNCollaborationDumpDatabase(database.handle.as_ptr()) };
+ success.then_some(()).ok_or(())
+}
+
+/// Ignore a snapshot from database syncing operations
+///
+/// * `database` - Parent database
+/// * `snapshot` - Snapshot to ignore
+pub fn ignore_snapshot(database: &Database, snapshot: &Snapshot) -> Result<(), ()> {
+ let success = unsafe {
+ BNCollaborationIgnoreSnapshot(database.handle.as_ptr(), snapshot.handle.as_ptr())
+ };
+ success.then_some(()).ok_or(())
+}
+
+/// Test if a snapshot is ignored from the database
+///
+/// * `database` - Parent database
+/// * `snapshot` - Snapshot to test
+pub fn is_snapshot_ignored(database: &Database, snapshot: &Snapshot) -> bool {
+ unsafe { BNCollaborationIsSnapshotIgnored(database.handle.as_ptr(), snapshot.handle.as_ptr()) }
+}
+
+/// Get the remote author of a local snapshot
+///
+/// * `database` - Parent database
+/// * `snapshot` - Snapshot to query
+pub fn get_snapshot_author(
+ database: &Database,
+ snapshot: &Snapshot,
+) -> Result<Option<BnString>, ()> {
+ let mut value = std::ptr::null_mut();
+ let success = unsafe {
+ BNCollaborationGetSnapshotAuthor(
+ database.handle.as_ptr(),
+ snapshot.handle.as_ptr(),
+ &mut value,
+ )
+ };
+ success
+ .then(|| (!value.is_null()).then(|| unsafe { BnString::from_raw(value) }))
+ .ok_or(())
+}
+
+/// Set the remote author of a local snapshot (does not upload)
+///
+/// * `database` - Parent database
+/// * `snapshot` - Snapshot to edit
+/// * `author` - Target author
+pub fn set_snapshot_author<S: BnStrCompatible>(
+ database: &Database,
+ snapshot: &Snapshot,
+ author: S,
+) -> Result<(), ()> {
+ let author = author.into_bytes_with_nul();
+ let success = unsafe {
+ BNCollaborationSetSnapshotAuthor(
+ database.handle.as_ptr(),
+ snapshot.handle.as_ptr(),
+ author.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ success.then_some(()).ok_or(())
+}
+
+// TODO: this needs to be removed imo
+pub(crate) fn pull_projects(database: &Database) -> Result<bool, ()> {
+ let Some(remote) = get_remote_for_local_database(database)? else {
+ return Ok(false);
+ };
+ remote.pull_projects()?;
+ Ok(true)
+}
+
+// TODO: This needs to be removed imo
+pub(crate) fn pull_files(database: &Database) -> Result<bool, ()> {
+ if !pull_projects(database)? {
+ return Ok(false);
+ }
+ let Some(project) = get_remote_project_for_local_database(database)? else {
+ return Ok(false);
+ };
+ project.pull_files()?;
+ Ok(true)
+}
+
+/// Completely sync a type archive, pushing/pulling/merging/applying changes
+///
+/// * `type_archive` - TypeArchive to sync
+/// * `file` - File to sync with
+/// * `conflict_handler` - Function to call to resolve snapshot conflicts
+pub fn sync_type_archive<C: TypeArchiveConflictHandler>(
+ type_archive: &TypeArchive,
+ file: &RemoteFile,
+ conflict_handler: C,
+) -> Result<(), ()> {
+ sync_type_archive_with_progress(type_archive, file, conflict_handler, NoProgressCallback)
+}
+
+/// Completely sync a type archive, pushing/pulling/merging/applying changes
+///
+/// * `type_archive` - TypeArchive to sync
+/// * `file` - File to sync with
+/// * `conflict_handler` - Function to call to resolve snapshot conflicts
+/// * `progress` - Function to call for progress updates
+pub fn sync_type_archive_with_progress<C: TypeArchiveConflictHandler, P: ProgressCallback>(
+ type_archive: &TypeArchive,
+ file: &RemoteFile,
+ mut conflict_handler: C,
+ mut progress: P,
+) -> Result<(), ()> {
+ let success = unsafe {
+ BNCollaborationSyncTypeArchive(
+ type_archive.handle.as_ptr(),
+ file.handle.as_ptr(),
+ Some(C::cb_handle_conflict),
+ &mut conflict_handler as *mut C as *mut c_void,
+ Some(P::cb_progress_callback),
+ &mut progress as *mut P as *mut c_void,
+ )
+ };
+ success.then_some(()).ok_or(())
+}
+
+/// Push locally added snapshots to the remote
+///
+/// * `type_archive` - TypeArchive to push
+/// * `file` - Remote File to push to
+pub fn push_type_archive(type_archive: &TypeArchive, file: &RemoteFile) -> Result<usize, ()> {
+ push_type_archive_with_progress(type_archive, file, NoProgressCallback)
+}
+
+/// Push locally added snapshots to the remote
+///
+/// * `type_archive` - TypeArchive to push
+/// * `file` - Remote File to push to
+/// * `progress` - Function to call for progress updates
+pub fn push_type_archive_with_progress<P: ProgressCallback>(
+ type_archive: &TypeArchive,
+ file: &RemoteFile,
+ mut progress: P,
+) -> Result<usize, ()> {
+ let mut count = 0;
+ let success = unsafe {
+ BNCollaborationPushTypeArchive(
+ type_archive.handle.as_ptr(),
+ file.handle.as_ptr(),
+ &mut count,
+ Some(P::cb_progress_callback),
+ &mut progress as *mut P as *mut c_void,
+ )
+ };
+ success.then_some(count).ok_or(())
+}
+
+/// Pull updated type archives from the remote.
+///
+/// * `type_archive` - TypeArchive to pull
+/// * `file` - Remote File to pull to
+/// * `conflict_handler` - Function to call to resolve snapshot conflicts
+pub fn pull_type_archive<C: TypeArchiveConflictHandler>(
+ type_archive: &TypeArchive,
+ file: &RemoteFile,
+ conflict_handler: C,
+) -> Result<usize, ()> {
+ pull_type_archive_with_progress(type_archive, file, conflict_handler, NoProgressCallback)
+}
+
+/// Pull updated type archives from the remote.
+///
+/// * `type_archive` - TypeArchive to pull
+/// * `file` - Remote File to pull to
+/// * `conflict_handler` - Function to call to resolve snapshot conflicts
+/// * `progress` - Function to call for progress updates
+pub fn pull_type_archive_with_progress<C: TypeArchiveConflictHandler, P: ProgressCallback>(
+ type_archive: &TypeArchive,
+ file: &RemoteFile,
+ mut conflict_handler: C,
+ mut progress: P,
+) -> Result<usize, ()> {
+ let mut count = 0;
+ let success = unsafe {
+ BNCollaborationPullTypeArchive(
+ type_archive.handle.as_ptr(),
+ file.handle.as_ptr(),
+ &mut count,
+ Some(C::cb_handle_conflict),
+ &mut conflict_handler as *mut C as *mut c_void,
+ Some(P::cb_progress_callback),
+ &mut progress as *mut P as *mut c_void,
+ )
+ };
+ success.then_some(count).ok_or(())
+}
+
+/// Test if a type archive is valid for use in collaboration
+pub fn is_collaboration_type_archive(type_archive: &TypeArchive) -> bool {
+ unsafe { BNCollaborationIsCollaborationTypeArchive(type_archive.handle.as_ptr()) }
+}
+
+/// Get the Remote for a Type Archive
+pub fn get_remote_for_local_type_archive(type_archive: &TypeArchive) -> Option<Ref<Remote>> {
+ let value =
+ unsafe { BNCollaborationGetRemoteForLocalTypeArchive(type_archive.handle.as_ptr()) };
+ NonNull::new(value).map(|handle| unsafe { Remote::ref_from_raw(handle) })
+}
+
+/// Get the Remote Project for a Type Archive
+pub fn get_remote_project_for_local_type_archive(
+ database: &TypeArchive,
+) -> Option<Ref<RemoteProject>> {
+ let value =
+ unsafe { BNCollaborationGetRemoteProjectForLocalTypeArchive(database.handle.as_ptr()) };
+ NonNull::new(value).map(|handle| unsafe { RemoteProject::ref_from_raw(handle) })
+}
+
+/// Get the Remote File for a Type Archive
+pub fn get_remote_file_for_local_type_archive(database: &TypeArchive) -> Option<Ref<RemoteFile>> {
+ let value =
+ unsafe { BNCollaborationGetRemoteFileForLocalTypeArchive(database.handle.as_ptr()) };
+ NonNull::new(value).map(|handle| unsafe { RemoteFile::ref_from_raw(handle) })
+}
+
+/// Get the remote snapshot associated with a local snapshot (if it exists) in a Type Archive
+pub fn get_remote_snapshot_from_local_type_archive<S: BnStrCompatible>(
+ type_archive: &TypeArchive,
+ snapshot_id: S,
+) -> Option<Ref<RemoteSnapshot>> {
+ let snapshot_id = snapshot_id.into_bytes_with_nul();
+ let value = unsafe {
+ BNCollaborationGetRemoteSnapshotFromLocalTypeArchive(
+ type_archive.handle.as_ptr(),
+ snapshot_id.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ NonNull::new(value).map(|handle| unsafe { RemoteSnapshot::ref_from_raw(handle) })
+}
+
+/// Get the local snapshot associated with a remote snapshot (if it exists) in a Type Archive
+pub fn get_local_snapshot_from_remote_type_archive(
+ snapshot: &RemoteSnapshot,
+ type_archive: &TypeArchive,
+) -> Option<BnString> {
+ let value = unsafe {
+ BNCollaborationGetLocalSnapshotFromRemoteTypeArchive(
+ snapshot.handle.as_ptr(),
+ type_archive.handle.as_ptr(),
+ )
+ };
+ (!value.is_null()).then(|| unsafe { BnString::from_raw(value) })
+}
+
+/// Test if a snapshot is ignored from the archive
+pub fn is_type_archive_snapshot_ignored<S: BnStrCompatible>(
+ type_archive: &TypeArchive,
+ snapshot_id: S,
+) -> bool {
+ let snapshot_id = snapshot_id.into_bytes_with_nul();
+ unsafe {
+ BNCollaborationIsTypeArchiveSnapshotIgnored(
+ type_archive.handle.as_ptr(),
+ snapshot_id.as_ref().as_ptr() as *const c_char,
+ )
+ }
+}
+
+/// Download a type archive from its remote, saving all snapshots to an archive in the
+/// specified `location`. Returns a [`TypeArchive`] for using later.
+pub fn download_type_archive<S: BnStrCompatible>(
+ file: &RemoteFile,
+ location: S,
+) -> Result<Option<TypeArchive>, ()> {
+ download_type_archive_with_progress(file, location, NoProgressCallback)
+}
+
+/// Download a type archive from its remote, saving all snapshots to an archive in the
+/// specified `location`. Returns a [`TypeArchive`] for using later.
+pub fn download_type_archive_with_progress<S: BnStrCompatible, F: ProgressCallback>(
+ file: &RemoteFile,
+ location: S,
+ mut progress: F,
+) -> Result<Option<TypeArchive>, ()> {
+ let mut value = std::ptr::null_mut();
+ let db_path = location.into_bytes_with_nul();
+ let success = unsafe {
+ BNCollaborationDownloadTypeArchive(
+ file.handle.as_ptr(),
+ db_path.as_ref().as_ptr() as *const c_char,
+ Some(F::cb_progress_callback),
+ &mut progress as *mut F as *mut c_void,
+ &mut value,
+ )
+ };
+ success
+ .then(|| NonNull::new(value).map(|handle| unsafe { TypeArchive::from_raw(handle) }))
+ .ok_or(())
+}
+
+/// Upload a type archive
+pub fn upload_type_archive(
+ archive: &TypeArchive,
+ project: &RemoteProject,
+ // TODO: Is this required?
+ folder: &RemoteFolder,
+ core_file: &ProjectFile,
+) -> Result<Ref<RemoteFile>, ()> {
+ upload_type_archive_with_progress(archive, project, folder, core_file, NoProgressCallback)
+}
+
+/// Upload a type archive
+pub fn upload_type_archive_with_progress<P: ProgressCallback>(
+ archive: &TypeArchive,
+ project: &RemoteProject,
+ // TODO: Is this required?
+ folder: &RemoteFolder,
+ // TODO: I dislike the word "core" just say local?
+ core_file: &ProjectFile,
+ mut progress: P,
+) -> Result<Ref<RemoteFile>, ()> {
+ let mut value = std::ptr::null_mut();
+ let success = unsafe {
+ BNCollaborationUploadTypeArchive(
+ archive.handle.as_ptr(),
+ project.handle.as_ptr(),
+ folder.handle.as_ptr(),
+ Some(P::cb_progress_callback),
+ &mut progress as *const P as *mut c_void,
+ core_file.handle.as_ptr(),
+ &mut value,
+ )
+ };
+ success
+ .then(|| {
+ NonNull::new(value)
+ .map(|handle| unsafe { RemoteFile::ref_from_raw(handle) })
+ .unwrap()
+ })
+ .ok_or(())
+}
+
+/// Merge a pair of snapshots and create a new snapshot with the result.
+pub fn merge_snapshots<C: DatabaseConflictHandler>(
+ first: &Snapshot,
+ second: &Snapshot,
+ conflict_handler: C,
+) -> Result<Snapshot, ()> {
+ merge_snapshots_with_progress(first, second, conflict_handler, NoProgressCallback)
+}
+
+/// Merge a pair of snapshots and create a new snapshot with the result.
+pub fn merge_snapshots_with_progress<C: DatabaseConflictHandler, P: ProgressCallback>(
+ first: &Snapshot,
+ second: &Snapshot,
+ mut conflict_handler: C,
+ mut progress: P,
+) -> Result<Snapshot, ()> {
+ let value = unsafe {
+ BNCollaborationMergeSnapshots(
+ first.handle.as_ptr(),
+ second.handle.as_ptr(),
+ Some(C::cb_handle_conflict),
+ &mut conflict_handler as *mut C as *mut c_void,
+ Some(P::cb_progress_callback),
+ &mut progress as *mut P as *mut c_void,
+ )
+ };
+ NonNull::new(value)
+ .map(|handle| unsafe { Snapshot::from_raw(handle) })
+ .ok_or(())
+}
+
+pub trait NameChangeset: Sized {
+ fn name_changeset(&mut self, changeset: &Changeset) -> bool;
+
+ unsafe extern "C" fn cb_name_changeset(
+ ctxt: *mut ::std::os::raw::c_void,
+ changeset: *mut BNCollaborationChangeset,
+ ) -> bool {
+ let ctxt: &mut Self = &mut *(ctxt as *mut Self);
+ let raw_changeset_ptr = NonNull::new(changeset).unwrap();
+ // TODO: Do we take ownership with a ref here or not?
+ let changeset = Changeset::from_raw(raw_changeset_ptr);
+ ctxt.name_changeset(&changeset)
+ }
+}
+
+impl<F> NameChangeset for F
+where
+ F: for<'a> FnMut(&'a Changeset) -> bool,
+{
+ fn name_changeset(&mut self, changeset: &Changeset) -> bool {
+ self(changeset)
+ }
+}
+
+pub struct NoNameChangeset;
+
+impl NameChangeset for NoNameChangeset {
+ fn name_changeset(&mut self, _changeset: &Changeset) -> bool {
+ unreachable!()
+ }
+
+ unsafe extern "C" fn cb_name_changeset(
+ _ctxt: *mut std::os::raw::c_void,
+ _changeset: *mut BNCollaborationChangeset,
+ ) -> bool {
+ true
+ }
+}
+
+/// Helper trait that resolves conflicts
+pub trait DatabaseConflictHandler: Sized {
+ /// Handle any merge conflicts by calling their success() function with a merged value
+ ///
+ /// * `conflicts` - conflicts ids to conflicts structures
+ ///
+ /// Return true if all conflicts were successfully merged
+ fn handle_conflict(&mut self, keys: &str, conflicts: &MergeConflict) -> bool;
+
+ unsafe extern "C" fn cb_handle_conflict(
+ ctxt: *mut c_void,
+ keys: *mut *const c_char,
+ conflicts: *mut *mut BNAnalysisMergeConflict,
+ conflict_count: usize,
+ ) -> bool {
+ let ctxt: &mut Self = &mut *(ctxt as *mut Self);
+ let keys = core::slice::from_raw_parts(keys, conflict_count);
+ let conflicts = core::slice::from_raw_parts(conflicts, conflict_count);
+ keys.iter().zip(conflicts.iter()).all(|(key, conflict)| {
+ // NOTE this is a reference, not owned, so ManuallyDrop is required, or just implement `ref_from_raw`
+ // TODO: Replace with raw_to_string
+ let key = ManuallyDrop::new(BnString::from_raw(*key as *mut _));
+ // TODO I guess dont drop here?
+ let raw_ptr = NonNull::new(*conflict).unwrap();
+ let conflict = MergeConflict::from_raw(raw_ptr);
+ ctxt.handle_conflict(key.as_str(), &conflict)
+ })
+ }
+}
+
+impl<F> DatabaseConflictHandler for F
+where
+ F: for<'a> FnMut(&'a str, &'a MergeConflict) -> bool,
+{
+ fn handle_conflict(&mut self, keys: &str, conflicts: &MergeConflict) -> bool {
+ self(keys, conflicts)
+ }
+}
+
+pub struct DatabaseConflictHandlerFail;
+impl DatabaseConflictHandler for DatabaseConflictHandlerFail {
+ fn handle_conflict(&mut self, _keys: &str, _conflicts: &MergeConflict) -> bool {
+ unreachable!()
+ }
+
+ unsafe extern "C" fn cb_handle_conflict(
+ _ctxt: *mut c_void,
+ _keys: *mut *const c_char,
+ _conflicts: *mut *mut BNAnalysisMergeConflict,
+ conflict_count: usize,
+ ) -> bool {
+ // Fail if we have any conflicts.
+ conflict_count > 0
+ }
+}
+
+pub trait TypeArchiveConflictHandler: Sized {
+ fn handle_conflict(&mut self, conflicts: &TypeArchiveMergeConflict) -> bool;
+ unsafe extern "C" fn cb_handle_conflict(
+ ctxt: *mut ::std::os::raw::c_void,
+ conflicts: *mut *mut BNTypeArchiveMergeConflict,
+ conflict_count: usize,
+ ) -> bool {
+ let ctx: &mut Self = &mut *(ctxt as *mut Self);
+ // TODO: Verify that we dont own the merge conflict, or this list passed to us.
+ let conflicts_raw = core::slice::from_raw_parts(conflicts, conflict_count);
+ conflicts_raw
+ .iter()
+ .map(|t| NonNull::new_unchecked(*t))
+ .map(|t| TypeArchiveMergeConflict::from_raw(t))
+ .all(|conflict| ctx.handle_conflict(&conflict))
+ }
+}
+
+impl<F> TypeArchiveConflictHandler for F
+where
+ F: for<'a> FnMut(&'a TypeArchiveMergeConflict) -> bool,
+{
+ fn handle_conflict(&mut self, conflicts: &TypeArchiveMergeConflict) -> bool {
+ self(conflicts)
+ }
+}
+
+pub struct TypeArchiveConflictHandlerFail;
+impl TypeArchiveConflictHandler for TypeArchiveConflictHandlerFail {
+ fn handle_conflict(&mut self, _conflicts: &TypeArchiveMergeConflict) -> bool {
+ unreachable!()
+ }
+
+ unsafe extern "C" fn cb_handle_conflict(
+ _ctxt: *mut c_void,
+ _conflicts: *mut *mut BNTypeArchiveMergeConflict,
+ _conflict_count: usize,
+ ) -> bool {
+ // TODO only fail if _conflict_count is greater then 0?
+ //_conflict_count > 0
+ false
+ }
+}
diff --git a/rust/src/collaboration/undo.rs b/rust/src/collaboration/undo.rs
new file mode 100644
index 00000000..9f1cc5f0
--- /dev/null
+++ b/rust/src/collaboration/undo.rs
@@ -0,0 +1,166 @@
+use crate::collaboration::{Remote, RemoteFile, RemoteProject, RemoteSnapshot};
+use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
+use crate::string::BnString;
+use binaryninjacore_sys::{
+ BNCollaborationFreeIdList, BNCollaborationUndoEntry, BNCollaborationUndoEntryGetData,
+ BNCollaborationUndoEntryGetFile, BNCollaborationUndoEntryGetId,
+ BNCollaborationUndoEntryGetParent, BNCollaborationUndoEntryGetParentId,
+ BNCollaborationUndoEntryGetProject, BNCollaborationUndoEntryGetRemote,
+ BNCollaborationUndoEntryGetSnapshot, BNCollaborationUndoEntryGetUrl,
+ BNFreeCollaborationUndoEntry, BNFreeCollaborationUndoEntryList,
+ BNNewCollaborationUndoEntryReference,
+};
+use std::fmt;
+use std::fmt::{Display, Formatter};
+use std::ptr::NonNull;
+
+#[repr(transparent)]
+pub struct RemoteUndoEntry {
+ handle: NonNull<BNCollaborationUndoEntry>,
+}
+
+impl RemoteUndoEntry {
+ pub(crate) unsafe fn from_raw(handle: NonNull<BNCollaborationUndoEntry>) -> Self {
+ Self { handle }
+ }
+
+ pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNCollaborationUndoEntry>) -> Ref<Self> {
+ Ref::new(Self { handle })
+ }
+
+ /// Owning Snapshot
+ pub fn snapshot(&self) -> Result<Ref<RemoteSnapshot>, ()> {
+ let value = unsafe { BNCollaborationUndoEntryGetSnapshot(self.handle.as_ptr()) };
+ let handle = NonNull::new(value).ok_or(())?;
+ Ok(unsafe { RemoteSnapshot::ref_from_raw(handle) })
+ }
+
+ /// Owning File
+ pub fn file(&self) -> Result<Ref<RemoteFile>, ()> {
+ let value = unsafe { BNCollaborationUndoEntryGetFile(self.handle.as_ptr()) };
+ let handle = NonNull::new(value).ok_or(())?;
+ Ok(unsafe { RemoteFile::ref_from_raw(handle) })
+ }
+
+ /// Owning Project
+ pub fn project(&self) -> Result<Ref<RemoteProject>, ()> {
+ let value = unsafe { BNCollaborationUndoEntryGetProject(self.handle.as_ptr()) };
+ let handle = NonNull::new(value).ok_or(())?;
+ Ok(unsafe { RemoteProject::ref_from_raw(handle) })
+ }
+
+ /// Owning Remote
+ pub fn remote(&self) -> Result<Ref<Remote>, ()> {
+ let value = unsafe { BNCollaborationUndoEntryGetRemote(self.handle.as_ptr()) };
+ let handle = NonNull::new(value).ok_or(())?;
+ Ok(unsafe { Remote::ref_from_raw(handle) })
+ }
+
+ /// Web api endpoint url
+ pub fn url(&self) -> BnString {
+ let value = unsafe { BNCollaborationUndoEntryGetUrl(self.handle.as_ptr()) };
+ assert!(!value.is_null());
+ unsafe { BnString::from_raw(value) }
+ }
+
+ /// Unique id
+ pub fn id(&self) -> RemoteUndoEntryId {
+ RemoteUndoEntryId(unsafe { BNCollaborationUndoEntryGetId(self.handle.as_ptr()) })
+ }
+
+ /// Id of parent undo entry
+ pub fn parent_id(&self) -> Option<RemoteUndoEntryId> {
+ let mut value = 0;
+ let success =
+ unsafe { BNCollaborationUndoEntryGetParentId(self.handle.as_ptr(), &mut value) };
+ success.then_some(RemoteUndoEntryId(value))
+ }
+
+ /// Undo entry contents data
+ pub fn data(&self) -> Result<BnString, ()> {
+ let mut value = std::ptr::null_mut();
+ let success = unsafe { BNCollaborationUndoEntryGetData(self.handle.as_ptr(), &mut value) };
+ if !success {
+ return Err(());
+ }
+ assert!(!value.is_null());
+ Ok(unsafe { BnString::from_raw(value) })
+ }
+
+ /// Parent Undo Entry object
+ pub fn parent(&self) -> Option<Ref<RemoteUndoEntry>> {
+ let value = unsafe { BNCollaborationUndoEntryGetParent(self.handle.as_ptr()) };
+ NonNull::new(value).map(|handle| unsafe { RemoteUndoEntry::ref_from_raw(handle) })
+ }
+}
+
+impl PartialEq for RemoteUndoEntry {
+ fn eq(&self, other: &Self) -> bool {
+ self.id() == other.id()
+ }
+}
+impl Eq for RemoteUndoEntry {}
+
+impl ToOwned for RemoteUndoEntry {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for RemoteUndoEntry {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: NonNull::new(BNNewCollaborationUndoEntryReference(handle.handle.as_ptr()))
+ .unwrap(),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeCollaborationUndoEntry(handle.handle.as_ptr());
+ }
+}
+
+impl CoreArrayProvider for RemoteUndoEntry {
+ type Raw = *mut BNCollaborationUndoEntry;
+ type Context = ();
+ type Wrapped<'a> = Guard<'a, Self>;
+}
+
+unsafe impl CoreArrayProviderInner for RemoteUndoEntry {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeCollaborationUndoEntryList(raw, count)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
+ let raw_ptr = NonNull::new(*raw).unwrap();
+ Guard::new(Self::from_raw(raw_ptr), context)
+ }
+}
+
+#[repr(transparent)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct RemoteUndoEntryId(pub u64);
+
+impl Display for RemoteUndoEntryId {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_fmt(format_args!("{}", self.0))
+ }
+}
+
+impl CoreArrayProvider for RemoteUndoEntryId {
+ type Raw = u64;
+ type Context = ();
+ type Wrapped<'a> = RemoteUndoEntryId;
+}
+
+unsafe impl CoreArrayProviderInner for RemoteUndoEntryId {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNCollaborationFreeIdList(raw, count)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ RemoteUndoEntryId(*raw)
+ }
+}
diff --git a/rust/src/collaboration/user.rs b/rust/src/collaboration/user.rs
new file mode 100644
index 00000000..0e3433d3
--- /dev/null
+++ b/rust/src/collaboration/user.rs
@@ -0,0 +1,154 @@
+use super::Remote;
+use binaryninjacore_sys::*;
+use std::ffi::c_char;
+use std::ptr::NonNull;
+
+use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
+use crate::string::{BnStrCompatible, BnString};
+
+#[repr(transparent)]
+pub struct RemoteUser {
+ pub(crate) handle: NonNull<BNCollaborationUser>,
+}
+
+impl RemoteUser {
+ pub(crate) unsafe fn from_raw(handle: NonNull<BNCollaborationUser>) -> Self {
+ Self { handle }
+ }
+
+ pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNCollaborationUser>) -> Ref<Self> {
+ Ref::new(Self { handle })
+ }
+
+ /// Owning Remote
+ pub fn remote(&self) -> Result<Ref<Remote>, ()> {
+ let value = unsafe { BNCollaborationUserGetRemote(self.handle.as_ptr()) };
+ let handle = NonNull::new(value).ok_or(())?;
+ Ok(unsafe { Remote::ref_from_raw(handle) })
+ }
+
+ /// Web api endpoint url
+ pub fn url(&self) -> BnString {
+ let value = unsafe { BNCollaborationUserGetUrl(self.handle.as_ptr()) };
+ assert!(!value.is_null());
+ unsafe { BnString::from_raw(value) }
+ }
+
+ /// Unique id
+ pub fn id(&self) -> BnString {
+ let value = unsafe { BNCollaborationUserGetId(self.handle.as_ptr()) };
+ assert!(!value.is_null());
+ unsafe { BnString::from_raw(value) }
+ }
+
+ /// User's login username
+ pub fn username(&self) -> BnString {
+ let value = unsafe { BNCollaborationUserGetUsername(self.handle.as_ptr()) };
+ assert!(!value.is_null());
+ unsafe { BnString::from_raw(value) }
+ }
+
+ /// Set user's username. You will need to push the user to update the Remote
+ pub fn set_username<U: BnStrCompatible>(&self, username: U) -> Result<(), ()> {
+ let username = username.into_bytes_with_nul();
+ let result = unsafe {
+ BNCollaborationUserSetUsername(
+ self.handle.as_ptr(),
+ username.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ if result {
+ Ok(())
+ } else {
+ Err(())
+ }
+ }
+
+ /// User's email address
+ pub fn email(&self) -> BnString {
+ let value = unsafe { BNCollaborationUserGetEmail(self.handle.as_ptr()) };
+ assert!(!value.is_null());
+ unsafe { BnString::from_raw(value) }
+ }
+
+ /// Set user's email. You will need to push the user to update the Remote
+ pub fn set_email<U: BnStrCompatible>(&self, email: U) -> Result<(), ()> {
+ let username = email.into_bytes_with_nul();
+ let result = unsafe {
+ BNCollaborationUserSetEmail(
+ self.handle.as_ptr(),
+ username.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ if result {
+ Ok(())
+ } else {
+ Err(())
+ }
+ }
+
+ /// String representing the last date the user logged in
+ pub fn last_login(&self) -> BnString {
+ let value = unsafe { BNCollaborationUserGetLastLogin(self.handle.as_ptr()) };
+ assert!(!value.is_null());
+ unsafe { BnString::from_raw(value) }
+ }
+
+ /// If the user account is active and can log in
+ pub fn is_active(&self) -> bool {
+ unsafe { BNCollaborationUserIsActive(self.handle.as_ptr()) }
+ }
+
+ /// Enable/disable a user account. You will need to push the user to update the Remote
+ pub fn set_is_active(&self, value: bool) -> Result<(), ()> {
+ if unsafe { BNCollaborationUserSetIsActive(self.handle.as_ptr(), value) } {
+ Ok(())
+ } else {
+ Err(())
+ }
+ }
+}
+
+impl PartialEq for RemoteUser {
+ fn eq(&self, other: &Self) -> bool {
+ self.id() == other.id()
+ }
+}
+impl Eq for RemoteUser {}
+
+impl ToOwned for RemoteUser {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for RemoteUser {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: NonNull::new(BNNewCollaborationUserReference(handle.handle.as_ptr())).unwrap(),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeCollaborationUser(handle.handle.as_ptr());
+ }
+}
+
+impl CoreArrayProvider for RemoteUser {
+ type Raw = *mut BNCollaborationUser;
+ type Context = ();
+ type Wrapped<'a> = Guard<'a, RemoteUser>;
+}
+
+unsafe impl CoreArrayProviderInner for RemoteUser {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeCollaborationUserList(raw, count)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
+ let raw_ptr = NonNull::new(*raw).unwrap();
+ Guard::new(Self::from_raw(raw_ptr), context)
+ }
+}
diff --git a/rust/src/command.rs b/rust/src/command.rs
index e682baee..4695d17e 100644
--- a/rust/src/command.rs
+++ b/rust/src/command.rs
@@ -40,11 +40,11 @@ use binaryninjacore_sys::{
use std::ops::Range;
use std::os::raw::c_void;
-use crate::binaryview::BinaryView;
+use crate::binary_view::BinaryView;
use crate::function::Function;
use crate::string::BnStrCompatible;
-/// The trait required for generic commands. See [register] for example usage.
+/// The trait required for generic commands. See [register_command] for example usage.
pub trait Command: 'static + Sync {
fn action(&self, view: &BinaryView);
fn valid(&self, view: &BinaryView) -> bool;
@@ -68,7 +68,7 @@ where
/// # Example
/// ```no_run
/// # use binaryninja::command::Command;
-/// # use binaryninja::binaryview::BinaryView;
+/// # use binaryninja::binary_view::BinaryView;
/// struct MyCommand;
///
/// impl Command for MyCommand {
@@ -82,10 +82,10 @@ where
/// }
/// }
///
-/// # use binaryninja::command::register;
+/// # use binaryninja::command::register_command;
/// #[no_mangle]
/// pub extern "C" fn CorePluginInit() -> bool {
-/// register(
+/// register_command(
/// "My Plugin Command",
/// "A description of my command",
/// MyCommand {},
@@ -93,7 +93,7 @@ where
/// true
/// }
/// ```
-pub fn register<S, C>(name: S, desc: S, command: C)
+pub fn register_command<S, C>(name: S, desc: S, command: C)
where
S: BnStrCompatible,
C: Command,
@@ -145,7 +145,7 @@ where
}
}
-/// The trait required for address-associated commands. See [register_for_address] for example usage.
+/// The trait required for address-associated commands. See [register_command_for_address] for example usage.
pub trait AddressCommand: 'static + Sync {
fn action(&self, view: &BinaryView, addr: u64);
fn valid(&self, view: &BinaryView, addr: u64) -> bool;
@@ -169,7 +169,7 @@ where
/// # Example
/// ```no_run
/// # use binaryninja::command::AddressCommand;
-/// # use binaryninja::binaryview::BinaryView;
+/// # use binaryninja::binary_view::BinaryView;
/// struct MyCommand;
///
/// impl AddressCommand for MyCommand {
@@ -183,10 +183,10 @@ where
/// }
/// }
///
-/// # use binaryninja::command::register_for_address;
+/// # use binaryninja::command::register_command_for_address;
/// #[no_mangle]
/// pub extern "C" fn CorePluginInit() -> bool {
-/// register_for_address(
+/// register_command_for_address(
/// "My Plugin Command",
/// "A description of my command",
/// MyCommand {},
@@ -194,7 +194,7 @@ where
/// true
/// }
/// ```
-pub fn register_for_address<S, C>(name: S, desc: S, command: C)
+pub fn register_command_for_address<S, C>(name: S, desc: S, command: C)
where
S: BnStrCompatible,
C: AddressCommand,
@@ -246,7 +246,7 @@ where
}
}
-/// The trait required for range-associated commands. See [register_for_range] for example usage.
+/// The trait required for range-associated commands. See [register_command_for_range] for example usage.
pub trait RangeCommand: 'static + Sync {
fn action(&self, view: &BinaryView, range: Range<u64>);
fn valid(&self, view: &BinaryView, range: Range<u64>) -> bool;
@@ -271,7 +271,7 @@ where
/// ```no_run
/// # use std::ops::Range;
/// # use binaryninja::command::RangeCommand;
-/// # use binaryninja::binaryview::BinaryView;
+/// # use binaryninja::binary_view::BinaryView;
/// struct MyCommand;
///
/// impl RangeCommand for MyCommand {
@@ -285,10 +285,10 @@ where
/// }
/// }
///
-/// # use binaryninja::command::register_for_range;
+/// # use binaryninja::command::register_command_for_range;
/// #[no_mangle]
/// pub extern "C" fn CorePluginInit() -> bool {
-/// register_for_range(
+/// register_command_for_range(
/// "My Plugin Command",
/// "A description of my command",
/// MyCommand {},
@@ -296,7 +296,7 @@ where
/// true
/// }
/// ```
-pub fn register_for_range<S, C>(name: S, desc: S, command: C)
+pub fn register_command_for_range<S, C>(name: S, desc: S, command: C)
where
S: BnStrCompatible,
C: RangeCommand,
@@ -353,7 +353,7 @@ where
}
}
-/// The trait required for function-associated commands. See [register_for_function] for example usage.
+/// The trait required for function-associated commands. See [register_command_for_function] for example usage.
pub trait FunctionCommand: 'static + Sync {
fn action(&self, view: &BinaryView, func: &Function);
fn valid(&self, view: &BinaryView, func: &Function) -> bool;
@@ -377,9 +377,9 @@ where
/// # Example
/// ```no_run
/// # use binaryninja::command::FunctionCommand;
-/// # use binaryninja::binaryview::BinaryView;
+/// # use binaryninja::binary_view::BinaryView;
/// # use binaryninja::function::Function;
-/// # use binaryninja::command::register_for_function;
+/// # use binaryninja::command::register_command_for_function;
/// struct MyCommand;
///
/// impl FunctionCommand for MyCommand {
@@ -395,7 +395,7 @@ where
///
/// #[no_mangle]
/// pub extern "C" fn CorePluginInit() -> bool {
-/// register_for_function(
+/// register_command_for_function(
/// "My Plugin Command",
/// "A description of my command",
/// MyCommand {},
@@ -403,7 +403,7 @@ where
/// true
/// }
/// ```
-pub fn register_for_function<S, C>(name: S, desc: S, command: C)
+pub fn register_command_for_function<S, C>(name: S, desc: S, command: C)
where
S: BnStrCompatible,
C: FunctionCommand,
diff --git a/rust/src/component.rs b/rust/src/component.rs
index ec235043..497c1f57 100644
--- a/rust/src/component.rs
+++ b/rust/src/component.rs
@@ -1,55 +1,68 @@
-use core::{ffi, mem, ptr};
-
-use crate::binaryview::{BinaryView, BinaryViewBase, BinaryViewExt};
+use crate::binary_view::{BinaryView, BinaryViewExt};
use crate::function::Function;
-use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref};
+use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
use crate::string::{BnStrCompatible, BnString};
-use crate::types::{ComponentReferencedTypes, DataVariable};
+use crate::types::ComponentReferencedType;
+use std::ffi::c_char;
+use std::fmt::Debug;
+use std::ptr::NonNull;
+use crate::variable::DataVariable;
use binaryninjacore_sys::*;
pub struct ComponentBuilder {
- bv: *mut BNBinaryView,
- parent: Option<BnString>,
- name: Option<BnString>,
+ view: Ref<BinaryView>,
+ parent: Option<String>,
+ name: Option<String>,
}
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 {
+ pub fn new(view: Ref<BinaryView>) -> Self {
Self {
- bv: bv.as_ref().handle,
+ view,
parent: None,
name: None,
}
}
- pub fn parent<G: IntoComponentGuid>(mut self, parent: G) -> Self {
- self.parent = Some(parent.component_guid());
+ pub fn parent(mut self, parent_guid: impl Into<String>) -> Self {
+ self.parent = Some(parent_guid.into());
self
}
- pub fn name<S: BnStrCompatible>(mut self, name: S) -> Self {
- self.name = Some(BnString::new(name));
+ pub fn name(mut self, name: impl Into<String>) -> Self {
+ self.name = Some(name.into());
self
}
- pub fn finalize(self) -> Component {
+ pub fn finalize(self) -> Ref<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())
- },
+ (None, None) => unsafe { BNCreateComponent(self.view.handle) },
+ (None, Some(name)) => {
+ let name_raw = name.into_bytes_with_nul();
+ unsafe {
+ BNCreateComponentWithName(self.view.handle, name_raw.as_ptr() as *mut c_char)
+ }
+ }
+ (Some(guid), None) => {
+ let guid_raw = guid.into_bytes_with_nul();
+ unsafe {
+ BNCreateComponentWithParent(self.view.handle, guid_raw.as_ptr() as *mut c_char)
+ }
+ }
+ (Some(guid), Some(name)) => {
+ let guid_raw = guid.into_bytes_with_nul();
+ let name_raw = name.into_bytes_with_nul();
+ unsafe {
+ BNCreateComponentWithParentAndName(
+ self.view.handle,
+ guid_raw.as_ptr() as *mut c_char,
+ name_raw.as_ptr() as *mut c_char,
+ )
+ }
+ }
};
- unsafe { Component::from_raw(ptr::NonNull::new(result).unwrap()) }
+ unsafe { Component::ref_from_raw(NonNull::new(result).unwrap()) }
}
}
@@ -61,53 +74,47 @@ impl ComponentBuilder {
/// 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>,
+ pub(crate) handle: 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 {
+ pub(crate) unsafe fn from_raw(handle: NonNull<BNComponent>) -> Self {
Self { handle }
}
- pub(crate) unsafe fn ref_from_raw(handle: &*mut BNComponent) -> &Self {
- assert!(!handle.is_null());
- mem::transmute(handle)
+ pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNComponent>) -> Ref<Self> {
+ Ref::new(Self { handle })
}
pub fn guid(&self) -> BnString {
- let result = unsafe { BNComponentGetGuid(self.as_raw()) };
+ let result = unsafe { BNComponentGetGuid(self.handle.as_ptr()) };
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) }
+ unsafe { BNComponentAddFunctionReference(self.handle.as_ptr(), func.handle) }
}
/// Check whether this component contains a function.
pub fn contains_function(&self, func: &Function) -> bool {
- unsafe { BNComponentContainsFunction(self.as_raw(), func.handle) }
+ unsafe { BNComponentContainsFunction(self.handle.as_ptr(), func.handle) }
}
/// Remove function from this component.
pub fn remove_function(&self, func: &Function) -> bool {
- unsafe { BNComponentRemoveFunctionReference(self.as_raw(), func.handle) }
+ unsafe { BNComponentRemoveFunctionReference(self.handle.as_ptr(), 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()) }
+ unsafe { BNComponentAddComponent(self.handle.as_ptr(), component.handle.as_ptr()) }
}
/// Check whether this component contains a component.
pub fn contains_component(&self, component: &Component) -> bool {
- unsafe { BNComponentContainsComponent(self.as_raw(), component.as_raw()) }
+ unsafe { BNComponentContainsComponent(self.handle.as_ptr(), component.handle.as_ptr()) }
}
/// Remove a component from the current component, moving it to the root.
@@ -124,62 +131,64 @@ impl 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()) }
+ unsafe { BNComponentAddDataVariable(self.handle.as_ptr(), 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()) }
+ unsafe { BNComponentContainsDataVariable(self.handle.as_ptr(), 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()) }
+ unsafe { BNComponentRemoveDataVariable(self.handle.as_ptr(), data_variable.address) }
}
/// Original name of the component
pub fn display_name(&self) -> BnString {
- let result = unsafe { BNComponentGetDisplayName(self.as_raw()) };
+ let result = unsafe { BNComponentGetDisplayName(self.handle.as_ptr()) };
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()) };
+ let result = unsafe { BNComponentGetOriginalName(self.handle.as_ptr()) };
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) }
+ unsafe {
+ BNComponentSetName(
+ self.handle.as_ptr(),
+ name.as_ref().as_ptr() as *const 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 parent(&self) -> Option<Ref<Component>> {
+ let result = unsafe { BNComponentGetParent(self.handle.as_ptr()) };
+ NonNull::new(result).map(|h| unsafe { Self::ref_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) })
+ let result = unsafe { BNComponentGetView(self.handle.as_ptr()) };
+ (!result.is_null()).then(|| unsafe { BinaryView::ref_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) };
+ let result = unsafe { BNComponentGetContainedComponents(self.handle.as_ptr(), &mut count) };
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
@@ -187,7 +196,7 @@ impl Component {
/// 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) };
+ let result = unsafe { BNComponentGetContainedFunctions(self.handle.as_ptr(), &mut count) };
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
@@ -195,7 +204,8 @@ impl Component {
/// 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) };
+ let result =
+ unsafe { BNComponentGetContainedDataVariables(self.handle.as_ptr(), &mut count) };
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
@@ -203,12 +213,14 @@ impl Component {
/// 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> {
+ pub fn referenced_data_variables(&self, recursive: bool) -> Array<DataVariable> {
let mut count = 0;
let result = if recursive {
- unsafe { BNComponentGetReferencedDataVariablesRecursive(self.as_raw(), &mut count) }
+ unsafe {
+ BNComponentGetReferencedDataVariablesRecursive(self.handle.as_ptr(), &mut count)
+ }
} else {
- unsafe { BNComponentGetReferencedDataVariables(self.as_raw(), &mut count) }
+ unsafe { BNComponentGetReferencedDataVariables(self.handle.as_ptr(), &mut count) }
};
unsafe { Array::new(result, count, ()) }
}
@@ -216,56 +228,75 @@ impl Component {
/// 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> {
+ pub fn referenced_types(&self, recursive: bool) -> Array<ComponentReferencedType> {
let mut count = 0;
let result = if recursive {
- unsafe { BNComponentGetReferencedTypesRecursive(self.as_raw(), &mut count) }
+ unsafe { BNComponentGetReferencedTypesRecursive(self.handle.as_ptr(), &mut count) }
} else {
- unsafe { BNComponentGetReferencedTypes(self.as_raw(), &mut count) }
+ unsafe { BNComponentGetReferencedTypes(self.handle.as_ptr(), &mut count) }
};
unsafe { Array::new(result, count, ()) }
}
pub fn remove_all_functions(&self) {
- unsafe { BNComponentRemoveAllFunctions(self.as_raw()) }
+ unsafe { BNComponentRemoveAllFunctions(self.handle.as_ptr()) }
}
pub fn add_all_members_from(&self, component: &Component) {
- unsafe { BNComponentAddAllMembersFromComponent(self.as_raw(), component.as_raw()) }
+ unsafe {
+ BNComponentAddAllMembersFromComponent(self.handle.as_ptr(), component.handle.as_ptr())
+ }
+ }
+}
+
+impl Debug for Component {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("Component")
+ .field("guid", &self.guid())
+ .field("display_name", &self.display_name())
+ .field("name", &self.name())
+ .field("components", &self.components().to_vec())
+ .finish()
}
}
impl PartialEq for Component {
fn eq(&self, other: &Self) -> bool {
- unsafe { BNComponentsEqual(self.as_raw(), other.as_raw()) }
+ unsafe { BNComponentsEqual(self.handle.as_ptr(), other.handle.as_ptr()) }
}
#[allow(clippy::partialeq_ne_impl)]
fn ne(&self, other: &Self) -> bool {
- unsafe { BNComponentsNotEqual(self.as_raw(), other.as_raw()) }
+ unsafe { BNComponentsNotEqual(self.handle.as_ptr(), other.handle.as_ptr()) }
}
}
impl Eq for Component {}
-impl Drop for Component {
- fn drop(&mut self) {
- unsafe { BNFreeComponent(self.as_raw()) }
+impl ToOwned for Component {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
}
}
-impl Clone for Component {
- fn clone(&self) -> Self {
- unsafe {
- Self::from_raw(ptr::NonNull::new(BNNewComponentReference(self.as_raw())).unwrap())
- }
+unsafe impl RefCountable for Component {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: NonNull::new(BNNewComponentReference(handle.handle.as_ptr())).unwrap(),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeComponent(handle.handle.as_ptr());
}
}
impl CoreArrayProvider for Component {
type Raw = *mut BNComponent;
type Context = ();
- type Wrapped<'a> = &'a Self;
+ type Wrapped<'a> = Guard<'a, Self>;
}
unsafe impl CoreArrayProviderInner for Component {
@@ -273,11 +304,13 @@ unsafe impl CoreArrayProviderInner for Component {
BNFreeComponents(raw, count)
}
- unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
- Self::ref_from_raw(raw)
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
+ let raw_ptr = NonNull::new(*raw).unwrap();
+ Guard::new(Self::from_raw(raw_ptr), context)
}
}
+// TODO: Should we keep this?
pub trait IntoComponentGuid {
fn component_guid(self) -> BnString;
}
diff --git a/rust/src/confidence.rs b/rust/src/confidence.rs
new file mode 100644
index 00000000..fd0b5532
--- /dev/null
+++ b/rust/src/confidence.rs
@@ -0,0 +1,299 @@
+#![allow(unused)]
+
+use crate::architecture::{Architecture, CoreArchitecture};
+use crate::calling_convention::CoreCallingConvention;
+use crate::rc::{Ref, RefCountable};
+use crate::types::Type;
+use binaryninjacore_sys::{
+ BNBoolWithConfidence, BNCallingConventionWithConfidence, BNGetCallingConventionArchitecture,
+ BNOffsetWithConfidence, BNTypeWithConfidence,
+};
+use std::fmt;
+use std::fmt::{Debug, Display, Formatter};
+use std::hash::{Hash, Hasher};
+
+/// The minimum allowed confidence of any given [`Type`].
+pub const MIN_CONFIDENCE: u8 = u8::MIN;
+
+/// The maximum allowed confidence of any given [`Type`].
+pub const MAX_CONFIDENCE: u8 = u8::MAX;
+
+/// Compatible with the `BNType*WithConfidence` types
+pub struct Conf<T> {
+ pub contents: T,
+ pub confidence: u8,
+}
+
+pub trait ConfMergeable<T, O> {
+ type Result;
+ /// Merge two confidence types' values depending on whichever has higher confidence
+ /// In the event of a tie, the LHS (caller's) value is used.
+ fn merge(self, other: O) -> Self::Result;
+}
+
+impl<T> Conf<T> {
+ pub fn new(contents: T, confidence: u8) -> Self {
+ Self {
+ contents,
+ confidence,
+ }
+ }
+
+ pub fn map<U, F>(self, f: F) -> Conf<U>
+ where
+ F: FnOnce(T) -> U,
+ {
+ Conf::new(f(self.contents), self.confidence)
+ }
+
+ pub fn as_ref<U>(&self) -> Conf<&U>
+ where
+ T: AsRef<U>,
+ {
+ Conf::new(self.contents.as_ref(), self.confidence)
+ }
+}
+
+/// Returns best value or LHS on tie
+///
+/// `Conf<T>` + `Conf<T>` → `Conf<T>`
+impl<T> ConfMergeable<T, Conf<T>> for Conf<T> {
+ type Result = Conf<T>;
+ fn merge(self, other: Conf<T>) -> Conf<T> {
+ if other.confidence > self.confidence {
+ other
+ } else {
+ self
+ }
+ }
+}
+
+/// Returns LHS if RHS is None
+///
+/// `Conf<T>` + `Option<Conf<T>>` → `Conf<T>`
+impl<T> ConfMergeable<T, Option<Conf<T>>> for Conf<T> {
+ type Result = Conf<T>;
+ fn merge(self, other: Option<Conf<T>>) -> Conf<T> {
+ match other {
+ Some(c @ Conf { confidence, .. }) if confidence > self.confidence => c,
+ _ => self,
+ }
+ }
+}
+
+/// Returns RHS if LHS is None
+///
+/// `Option<Conf<T>>` + `Conf<T>` → `Conf<T>`
+impl<T> ConfMergeable<T, Conf<T>> for Option<Conf<T>> {
+ type Result = Conf<T>;
+ fn merge(self, other: Conf<T>) -> Conf<T> {
+ match self {
+ Some(c @ Conf { confidence, .. }) if confidence >= other.confidence => c,
+ _ => other,
+ }
+ }
+}
+
+/// Returns best non-None value or None
+///
+/// `Option<Conf<T>>` + `Option<Conf<T>>` → `Option<Conf<T>>`
+impl<T> ConfMergeable<T, Option<Conf<T>>> for Option<Conf<T>> {
+ type Result = Option<Conf<T>>;
+ fn merge(self, other: Option<Conf<T>>) -> Option<Conf<T>> {
+ match (self, other) {
+ (
+ Some(
+ this @ Conf {
+ confidence: this_confidence,
+ ..
+ },
+ ),
+ Some(
+ other @ Conf {
+ confidence: other_confidence,
+ ..
+ },
+ ),
+ ) => {
+ if this_confidence >= other_confidence {
+ Some(this)
+ } else {
+ Some(other)
+ }
+ }
+ (None, Some(c)) => Some(c),
+ (Some(c), None) => Some(c),
+ (None, None) => None,
+ }
+ }
+}
+
+impl<T: Debug> Debug for Conf<T> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ write!(f, "{:?} ({} confidence)", self.contents, self.confidence)
+ }
+}
+
+impl<T: Display> Display for Conf<T> {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ write!(f, "{} ({} confidence)", self.contents, self.confidence)
+ }
+}
+
+impl<T: PartialEq> PartialEq for Conf<T> {
+ fn eq(&self, other: &Self) -> bool {
+ self.contents.eq(&other.contents)
+ }
+}
+
+impl<T: Eq> Eq for Conf<T> {}
+
+impl<T: Hash> Hash for Conf<T> {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.contents.hash(state);
+ }
+}
+
+impl<'a, T> From<&'a Conf<T>> for Conf<&'a T> {
+ fn from(c: &'a Conf<T>) -> Self {
+ Conf::new(&c.contents, c.confidence)
+ }
+}
+
+impl<'a, T: RefCountable> From<&'a Conf<Ref<T>>> for Conf<&'a T> {
+ fn from(c: &'a Conf<Ref<T>>) -> Self {
+ Conf::new(c.contents.as_ref(), c.confidence)
+ }
+}
+
+impl<'a, T: RefCountable> From<&'a Ref<T>> for Conf<&'a T> {
+ fn from(r: &'a Ref<T>) -> Self {
+ r.as_ref().into()
+ }
+}
+
+impl<T: Clone> Clone for Conf<T> {
+ fn clone(&self) -> Self {
+ Self {
+ contents: self.contents.clone(),
+ confidence: self.confidence,
+ }
+ }
+}
+
+impl<T: Copy> Copy for Conf<T> {}
+
+impl<T> From<T> for Conf<T> {
+ fn from(contents: T) -> Self {
+ Self::new(contents, MAX_CONFIDENCE)
+ }
+}
+
+impl Conf<&'_ Type> {
+ pub(crate) fn into_raw(value: Self) -> BNTypeWithConfidence {
+ BNTypeWithConfidence {
+ type_: value.contents.handle,
+ confidence: value.confidence,
+ }
+ }
+}
+
+impl Conf<Ref<Type>> {
+ pub(crate) fn from_raw(value: &BNTypeWithConfidence) -> Self {
+ Self::new(
+ unsafe { Type::from_raw(value.type_) }.to_owned(),
+ value.confidence,
+ )
+ }
+
+ pub(crate) fn from_owned_raw(value: BNTypeWithConfidence) -> Self {
+ let owned = Self::from_raw(&value);
+ Self::free_raw(value);
+ owned
+ }
+
+ pub(crate) fn into_raw(value: Self) -> BNTypeWithConfidence {
+ BNTypeWithConfidence {
+ type_: unsafe { Ref::into_raw(value.contents) }.handle,
+ confidence: value.confidence,
+ }
+ }
+
+ pub(crate) fn free_raw(value: BNTypeWithConfidence) {
+ let _ = unsafe { Type::ref_from_raw(value.type_) };
+ }
+}
+
+impl Conf<Ref<CoreCallingConvention>> {
+ pub(crate) fn from_raw(value: &BNCallingConventionWithConfidence) -> Self {
+ let arch = unsafe {
+ CoreArchitecture::from_raw(BNGetCallingConventionArchitecture(value.convention))
+ };
+ Self::new(
+ unsafe { CoreCallingConvention::from_raw(value.convention, arch).to_owned() },
+ value.confidence,
+ )
+ }
+
+ pub(crate) fn from_owned_raw(value: BNCallingConventionWithConfidence) -> Self {
+ let owned = Self::from_raw(&value);
+ Self::free_raw(value);
+ owned
+ }
+}
+
+impl Conf<Ref<CoreCallingConvention>> {
+ pub(crate) fn into_raw(value: Self) -> BNCallingConventionWithConfidence {
+ BNCallingConventionWithConfidence {
+ convention: unsafe { Ref::into_raw(value.contents) }.handle,
+ confidence: value.confidence,
+ }
+ }
+
+ pub(crate) fn into_owned_raw(value: &Self) -> BNCallingConventionWithConfidence {
+ BNCallingConventionWithConfidence {
+ convention: value.contents.handle,
+ confidence: value.confidence,
+ }
+ }
+
+ pub(crate) fn free_raw(value: BNCallingConventionWithConfidence) {
+ let arch = unsafe {
+ CoreArchitecture::from_raw(BNGetCallingConventionArchitecture(value.convention))
+ };
+ let _ = unsafe { CoreCallingConvention::ref_from_raw(value.convention, arch) };
+ }
+}
+
+impl From<BNBoolWithConfidence> for Conf<bool> {
+ fn from(bool_with_confidence: BNBoolWithConfidence) -> Self {
+ Self::new(bool_with_confidence.value, bool_with_confidence.confidence)
+ }
+}
+
+impl From<BNOffsetWithConfidence> for Conf<i64> {
+ fn from(offset_with_confidence: BNOffsetWithConfidence) -> Self {
+ Self::new(
+ offset_with_confidence.value,
+ offset_with_confidence.confidence,
+ )
+ }
+}
+
+impl From<Conf<bool>> for BNBoolWithConfidence {
+ fn from(conf: Conf<bool>) -> Self {
+ Self {
+ value: conf.contents,
+ confidence: conf.confidence,
+ }
+ }
+}
+
+impl From<Conf<i64>> for BNOffsetWithConfidence {
+ fn from(conf: Conf<i64>) -> Self {
+ Self {
+ value: conf.contents,
+ confidence: conf.confidence,
+ }
+ }
+}
diff --git a/rust/src/custombinaryview.rs b/rust/src/custom_binary_view.rs
index 2383ce1e..d3258a2b 100644
--- a/rust/src/custombinaryview.rs
+++ b/rust/src/custom_binary_view.rs
@@ -19,14 +19,13 @@ use binaryninjacore_sys::*;
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;
use crate::architecture::Architecture;
-use crate::binaryview::{BinaryView, BinaryViewBase, BinaryViewExt, Result};
+use crate::binary_view::{BinaryView, BinaryViewBase, BinaryViewExt, Result};
use crate::platform::Platform;
use crate::settings::Settings;
use crate::Endianness;
@@ -52,7 +51,7 @@ where
{
ffi_wrap!("BinaryViewTypeBase::is_valid_for", unsafe {
let view_type = &*(ctxt as *mut T);
- let data = BinaryView::from_raw(BNNewViewReference(data));
+ let data = BinaryView::ref_from_raw(BNNewViewReference(data));
view_type.is_valid_for(&data)
})
@@ -84,7 +83,7 @@ where
{
ffi_wrap!("BinaryViewTypeBase::create", unsafe {
let view_type = &*(ctxt as *mut T);
- let data = BinaryView::from_raw(BNNewViewReference(data));
+ let data = BinaryView::ref_from_raw(BNNewViewReference(data));
let builder = CustomViewBuilder {
view_type,
@@ -97,37 +96,37 @@ where
// in the refcount going to 0 in the process of returning it
// to the core -- we're transferring ownership of the Ref here
Ref::into_raw(bv.handle).handle
- },
+ }
Err(_) => {
- error!("CustomBinaryViewType::create_custom_view returned Err");
+ log::error!("CustomBinaryViewType::create_custom_view returned Err");
ptr::null_mut()
}
}
})
}
-
+
extern "C" fn cb_parse<T>(ctxt: *mut c_void, data: *mut BNBinaryView) -> *mut BNBinaryView
where
T: CustomBinaryViewType,
{
ffi_wrap!("BinaryViewTypeBase::parse", unsafe {
let view_type = &*(ctxt as *mut T);
- let data = BinaryView::from_raw(BNNewViewReference(data));
+ let data = BinaryView::ref_from_raw(BNNewViewReference(data));
let builder = CustomViewBuilder {
view_type,
actual_parent: &data,
};
-
+
match view_type.parse_custom_view(&data, builder) {
Ok(bv) => {
// force a leak of the Ref; failure to do this would result
// in the refcount going to 0 in the process of returning it
// to the core -- we're transferring ownership of the Ref here
Ref::into_raw(bv.handle).handle
- },
+ }
Err(_) => {
- error!("CustomBinaryViewType::parse returned Err");
+ log::error!("CustomBinaryViewType::parse returned Err");
ptr::null_mut()
}
}
@@ -140,7 +139,7 @@ where
{
ffi_wrap!("BinaryViewTypeBase::load_settings", unsafe {
let view_type = &*(ctxt as *mut T);
- let data = BinaryView::from_raw(BNNewViewReference(data));
+ let data = BinaryView::ref_from_raw(BNNewViewReference(data));
match view_type.load_settings_for_data(&data) {
Some(settings) => Ref::into_raw(settings).handle,
@@ -168,9 +167,8 @@ where
};
unsafe {
- let res = BNRegisterBinaryViewType(name_ptr, long_name_ptr, &mut bn_obj as *mut _);
-
- if res.is_null() {
+ let handle = BNRegisterBinaryViewType(name_ptr, long_name_ptr, &mut bn_obj as *mut _);
+ if handle.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) as
@@ -180,8 +178,7 @@ where
panic!("bvt registration failed");
}
- ctxt.write(constructor(BinaryViewType(res)));
-
+ ctxt.write(constructor(BinaryViewType { handle }));
ctxt.assume_init_mut()
}
}
@@ -199,7 +196,7 @@ pub trait BinaryViewTypeBase: AsRef<BinaryViewType> {
fn default_load_settings_for_data(&self, data: &BinaryView) -> Option<Ref<Settings>> {
let settings_handle =
- unsafe { BNGetBinaryViewDefaultLoadSettingsForData(self.as_ref().0, data.handle) };
+ unsafe { BNGetBinaryViewDefaultLoadSettingsForData(self.as_ref().handle, data.handle) };
if settings_handle.is_null() {
None
@@ -207,7 +204,7 @@ pub trait BinaryViewTypeBase: AsRef<BinaryViewType> {
unsafe { Some(Settings::from_raw(settings_handle)) }
}
}
-
+
fn load_settings_for_data(&self, _data: &BinaryView) -> Option<Ref<Settings>> {
None
}
@@ -215,16 +212,21 @@ pub trait BinaryViewTypeBase: AsRef<BinaryViewType> {
pub trait BinaryViewTypeExt: BinaryViewTypeBase {
fn name(&self) -> BnString {
- unsafe { BnString::from_raw(BNGetBinaryViewTypeName(self.as_ref().0)) }
+ unsafe { BnString::from_raw(BNGetBinaryViewTypeName(self.as_ref().handle)) }
}
fn long_name(&self) -> BnString {
- unsafe { BnString::from_raw(BNGetBinaryViewTypeLongName(self.as_ref().0)) }
+ unsafe { BnString::from_raw(BNGetBinaryViewTypeLongName(self.as_ref().handle)) }
}
fn register_arch<A: Architecture>(&self, id: u32, endianness: Endianness, arch: &A) {
unsafe {
- BNRegisterArchitectureForViewType(self.as_ref().0, id, endianness, arch.as_ref().0);
+ BNRegisterArchitectureForViewType(
+ self.as_ref().handle,
+ id,
+ endianness,
+ arch.as_ref().handle,
+ );
}
}
@@ -232,50 +234,56 @@ pub trait BinaryViewTypeExt: BinaryViewTypeBase {
let arch = plat.arch();
unsafe {
- BNRegisterPlatformForViewType(self.as_ref().0, id, arch.0, plat.handle);
+ BNRegisterPlatformForViewType(self.as_ref().handle, id, arch.handle, plat.handle);
}
}
fn open(&self, data: &BinaryView) -> Result<Ref<BinaryView>> {
- let handle = unsafe { BNCreateBinaryViewOfType(self.as_ref().0, data.handle) };
+ let handle = unsafe { BNCreateBinaryViewOfType(self.as_ref().handle, data.handle) };
if handle.is_null() {
- error!(
+ log::error!(
"failed to create BinaryView of BinaryViewType '{}'",
self.name()
);
return Err(());
}
- unsafe { Ok(BinaryView::from_raw(handle)) }
+ unsafe { Ok(BinaryView::ref_from_raw(handle)) }
}
fn parse(&self, data: &BinaryView) -> Result<Ref<BinaryView>> {
- let handle = unsafe { BNParseBinaryViewOfType(self.as_ref().0, data.handle) };
+ let handle = unsafe { BNParseBinaryViewOfType(self.as_ref().handle, data.handle) };
if handle.is_null() {
- error!(
+ log::error!(
"failed to parse BinaryView of BinaryViewType '{}'",
self.name()
);
return Err(());
}
- unsafe { Ok(BinaryView::from_raw(handle)) }
+ unsafe { Ok(BinaryView::ref_from_raw(handle)) }
}
}
impl<T: BinaryViewTypeBase> BinaryViewTypeExt for T {}
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
-pub struct BinaryViewType(pub *mut BNBinaryViewType);
+pub struct BinaryViewType {
+ pub handle: *mut BNBinaryViewType,
+}
impl BinaryViewType {
+ pub(crate) unsafe fn from_raw(handle: *mut BNBinaryViewType) -> Self {
+ debug_assert!(!handle.is_null());
+ Self { handle }
+ }
+
pub fn list_all() -> Array<BinaryViewType> {
unsafe {
let mut count: usize = 0;
let types = BNGetBinaryViewTypes(&mut count as *mut _);
-
Array::new(types, count, ())
}
}
@@ -284,7 +292,6 @@ impl BinaryViewType {
unsafe {
let mut count: usize = 0;
let types = BNGetBinaryViewTypesForData(data.handle, &mut count as *mut _);
-
Array::new(types, count, ())
}
}
@@ -292,11 +299,9 @@ impl BinaryViewType {
/// Looks up a BinaryViewType by its short name
pub fn by_name<N: BnStrCompatible>(name: N) -> Result<Self> {
let bytes = name.into_bytes_with_nul();
-
- let res = unsafe { BNGetBinaryViewTypeByName(bytes.as_ref().as_ptr() as *const _) };
-
- match res.is_null() {
- false => Ok(BinaryViewType(res)),
+ let handle = unsafe { BNGetBinaryViewTypeByName(bytes.as_ref().as_ptr() as *const _) };
+ match handle.is_null() {
+ false => Ok(unsafe { BinaryViewType::from_raw(handle) }),
true => Err(()),
}
}
@@ -304,19 +309,20 @@ impl BinaryViewType {
impl BinaryViewTypeBase for BinaryViewType {
fn is_valid_for(&self, data: &BinaryView) -> bool {
- unsafe { BNIsBinaryViewTypeValidForData(self.0, data.handle) }
+ unsafe { BNIsBinaryViewTypeValidForData(self.handle, data.handle) }
}
fn is_deprecated(&self) -> bool {
- unsafe { BNIsBinaryViewTypeDeprecated(self.0) }
+ unsafe { BNIsBinaryViewTypeDeprecated(self.handle) }
}
fn is_force_loadable(&self) -> bool {
- unsafe { BNIsBinaryViewTypeForceLoadable(self.0) }
+ unsafe { BNIsBinaryViewTypeForceLoadable(self.handle) }
}
fn load_settings_for_data(&self, data: &BinaryView) -> Option<Ref<Settings>> {
- let settings_handle = unsafe { BNGetBinaryViewLoadSettingsForData(self.0, data.handle) };
+ let settings_handle =
+ unsafe { BNGetBinaryViewLoadSettingsForData(self.handle, data.handle) };
if settings_handle.is_null() {
None
@@ -336,8 +342,9 @@ unsafe impl CoreArrayProviderInner for BinaryViewType {
unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
BNFreeBinaryViewTypeList(raw);
}
+
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
- Guard::new(BinaryViewType(*raw), &())
+ Guard::new(BinaryViewType::from_raw(*raw), &())
}
}
@@ -423,7 +430,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
///
/// The `BinaryView` argument passed to the constructor function is the object that is expected
/// to be returned by the `AsRef<BinaryView>` implementation required by the `BinaryViewBase` trait.
- /// TODO FIXME welp this is broke going to need 2 init callbacks
+ /// TODO FIXME whelp this is broke going to need 2 init callbacks
pub fn create<V>(self, parent: &BinaryView, view_args: V::Args) -> Result<CustomView<'a>>
where
V: CustomBinaryView,
@@ -433,7 +440,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_str()) {
+ if let Some(bv) = file.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
@@ -443,7 +450,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
// even if we deal with it gracefully in cb_free_object,
// BNCreateBinaryViewOfType is still going to crash, so we're just
// going to try and stop this from happening in the first place.
- error!(
+ log::error!(
"attempt to create duplicate view of type '{}' (existing: {:?})",
view_name.as_str(),
bv.handle
@@ -452,17 +459,34 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
return Err(());
}
- // wildly unsafe struct representing the context of a BNCustomBinaryView
- // this type should *never* be allowed to drop as the fields are in varying
- // states of uninitialized/already consumed throughout the life of the object.
+ // struct representing the context of a BNCustomBinaryView. Can be safely
+ // dropped at any moment.
struct CustomViewContext<V>
where
V: CustomBinaryView,
{
- view: mem::MaybeUninit<V>,
raw_handle: *mut BNBinaryView,
- initialized: bool,
- args: V::Args,
+ state: CustomViewContextState<V>,
+ }
+
+ enum CustomViewContextState<V>
+ where
+ V: CustomBinaryView,
+ {
+ Uninitialized { args: V::Args },
+ Initialized { view: V },
+ // dummy state, used as a helper to change states, only happen if the
+ // `new` or `init` function fails.
+ None,
+ }
+
+ impl<V: CustomBinaryView> CustomViewContext<V> {
+ fn assume_init_ref(&self) -> &V {
+ let CustomViewContextState::Initialized { view } = &self.state else {
+ panic!("CustomViewContextState in invalid state");
+ };
+ view
+ }
}
extern "C" fn cb_init<V>(ctxt: *mut c_void) -> bool
@@ -471,27 +495,28 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
{
ffi_wrap!("BinaryViewBase::init", unsafe {
let context = &mut *(ctxt as *mut CustomViewContext<V>);
- let handle = BinaryView::from_raw(context.raw_handle);
-
- match V::new(handle.as_ref(), &context.args) {
- Ok(v) => {
- ptr::write(&mut context.view, mem::MaybeUninit::new(v));
- context.initialized = true;
+ let handle = BinaryView::ref_from_raw(context.raw_handle);
- match context
- .view
- .assume_init_mut()
- .init(ptr::read(&context.args))
- {
- Ok(_) => true,
- Err(_) => {
- error!("CustomBinaryView::init failed; custom view returned Err");
- false
- }
+ // take the uninitialized state and use the args to call init
+ let mut state = CustomViewContextState::None;
+ core::mem::swap(&mut context.state, &mut state);
+ let CustomViewContextState::Uninitialized { args } = state else {
+ panic!("CustomViewContextState in invalid state");
+ };
+ match V::new(handle.as_ref(), &args) {
+ Ok(mut view) => match view.init(args) {
+ Ok(_) => {
+ // put the initialized state
+ context.state = CustomViewContextState::Initialized { view };
+ true
}
- }
+ Err(_) => {
+ log::error!("CustomBinaryView::init failed; custom view returned Err");
+ false
+ }
+ },
Err(_) => {
- error!("CustomBinaryView::new failed; custom view returned Err");
+ log::error!("CustomBinaryView::new failed; custom view returned Err");
false
}
}
@@ -504,48 +529,43 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
{
ffi_wrap!("BinaryViewBase::freeObject", unsafe {
let context = ctxt as *mut CustomViewContext<V>;
- let context = *Box::from_raw(context);
-
- if context.initialized {
- mem::forget(context.args); // already consumed
- // mem::drop(context.view); // cb_init was called
- } else {
- mem::drop(context.args); // never consumed
- // mem::forget(context.view); // cb_init was not called, is uninit
+ let context = Box::from_raw(context);
- if context.raw_handle.is_null() {
- // being called here is essentially a guarantee that BNCreateBinaryViewOfType
- // is above above us on the call stack somewhere -- no matter what we do, a crash
- // is pretty much certain at this point.
- //
- // this has been observed when two views of the same BinaryViewType are created
- // against the same BNFileMetaData object, and one of the views gets freed while
- // the second one is being initialized -- somehow the partially initialized one
- // gets freed before BNCreateBinaryViewOfType returns.
- //
- // multiples views of the same BinaryViewType in a BNFileMetaData object are
- // prohibited, so an API contract was violated in order to get here.
- //
- // if we're here, it's too late to do anything about it, though we can at least not
- // run the destructor on the custom view since that memory is unitialized.
- error!(
- "BinaryViewBase::freeObject called on partially initialized object! crash imminent!"
- );
- } else if !context.initialized {
- // making it here means somebody went out of their way to leak a BinaryView
- // after calling BNCreateCustomView and never gave the BNBinaryView handle
- // to the core (which would have called cb_init)
- //
- // the result is a half-initialized BinaryView that the core will happily hand out
- // references to via BNGetFileViewofType even though it was never initialized
- // all the way.
- //
- // TODO update when this corner case gets fixed in the core?
- //
- // we can't do anything to prevent this, but we can at least have the crash
- // not be our fault.
- error!("BinaryViewBase::freeObject called on leaked/never initialized custom view!");
- }
+ if context.raw_handle.is_null() {
+ // being called here is essentially a guarantee that BNCreateBinaryViewOfType
+ // is above above us on the call stack somewhere -- no matter what we do, a crash
+ // is pretty much certain at this point.
+ //
+ // this has been observed when two views of the same BinaryViewType are created
+ // against the same BNFileMetaData object, and one of the views gets freed while
+ // the second one is being initialized -- somehow the partially initialized one
+ // gets freed before BNCreateBinaryViewOfType returns.
+ //
+ // multiples views of the same BinaryViewType in a BNFileMetaData object are
+ // prohibited, so an API contract was violated in order to get here.
+ //
+ // if we're here, it's too late to do anything about it, though we can at least not
+ // run the destructor on the custom view since that memory is uninitialized.
+ log::error!(
+ "BinaryViewBase::freeObject called on partially initialized object! crash imminent!"
+ );
+ } else if matches!(
+ &context.state,
+ CustomViewContextState::None | CustomViewContextState::Uninitialized { .. }
+ ) {
+ // making it here means somebody went out of their way to leak a BinaryView
+ // after calling BNCreateCustomView and never gave the BNBinaryView handle
+ // to the core (which would have called cb_init)
+ //
+ // the result is a half-initialized BinaryView that the core will happily hand out
+ // references to via BNGetFileViewofType even though it was never initialized
+ // all the way.
+ //
+ // TODO update when this corner case gets fixed in the core?
+ //
+ // we can't do anything to prevent this, but we can at least have the crash
+ // not be our fault.
+ log::error!("BinaryViewBase::freeObject called on leaked/never initialized custom view!");
}
})
}
@@ -562,8 +582,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
ffi_wrap!("BinaryViewBase::read", unsafe {
let context = &*(ctxt as *mut CustomViewContext<V>);
let dest = slice::from_raw_parts_mut(dest as *mut u8, len);
-
- context.view.assume_init_ref().read(dest, offset)
+ context.assume_init_ref().read(dest, offset)
})
}
@@ -579,8 +598,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
ffi_wrap!("BinaryViewBase::write", unsafe {
let context = &*(ctxt as *mut CustomViewContext<V>);
let src = slice::from_raw_parts(src as *const u8, len);
-
- context.view.assume_init_ref().write(offset, src)
+ context.assume_init_ref().write(offset, src)
})
}
@@ -596,8 +614,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
ffi_wrap!("BinaryViewBase::insert", unsafe {
let context = &*(ctxt as *mut CustomViewContext<V>);
let src = slice::from_raw_parts(src as *const u8, len);
-
- context.view.assume_init_ref().insert(offset, src)
+ context.assume_init_ref().insert(offset, src)
})
}
@@ -607,8 +624,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
{
ffi_wrap!("BinaryViewBase::remove", unsafe {
let context = &*(ctxt as *mut CustomViewContext<V>);
-
- context.view.assume_init_ref().remove(offset, len as usize)
+ context.assume_init_ref().remove(offset, len as usize)
})
}
@@ -618,8 +634,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
{
ffi_wrap!("BinaryViewBase::modification_status", unsafe {
let context = &*(ctxt as *mut CustomViewContext<V>);
-
- context.view.assume_init_ref().modification_status(offset)
+ context.assume_init_ref().modification_status(offset)
})
}
@@ -629,8 +644,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
{
ffi_wrap!("BinaryViewBase::offset_valid", unsafe {
let context = &*(ctxt as *mut CustomViewContext<V>);
-
- context.view.assume_init_ref().offset_valid(offset)
+ context.assume_init_ref().offset_valid(offset)
})
}
@@ -640,8 +654,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
{
ffi_wrap!("BinaryViewBase::readable", unsafe {
let context = &*(ctxt as *mut CustomViewContext<V>);
-
- context.view.assume_init_ref().offset_readable(offset)
+ context.assume_init_ref().offset_readable(offset)
})
}
@@ -651,8 +664,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
{
ffi_wrap!("BinaryViewBase::writable", unsafe {
let context = &*(ctxt as *mut CustomViewContext<V>);
-
- context.view.assume_init_ref().offset_writable(offset)
+ context.assume_init_ref().offset_writable(offset)
})
}
@@ -662,8 +674,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
{
ffi_wrap!("BinaryViewBase::offset_executable", unsafe {
let context = &*(ctxt as *mut CustomViewContext<V>);
-
- context.view.assume_init_ref().offset_executable(offset)
+ context.assume_init_ref().offset_executable(offset)
})
}
@@ -673,8 +684,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
{
ffi_wrap!("BinaryViewBase::offset_backed_by_file", unsafe {
let context = &*(ctxt as *mut CustomViewContext<V>);
-
- context.view.assume_init_ref().offset_backed_by_file(offset)
+ context.assume_init_ref().offset_backed_by_file(offset)
})
}
@@ -684,11 +694,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
{
ffi_wrap!("BinaryViewBase::next_valid_offset_after", unsafe {
let context = &*(ctxt as *mut CustomViewContext<V>);
-
- context
- .view
- .assume_init_ref()
- .next_valid_offset_after(offset)
+ context.assume_init_ref().next_valid_offset_after(offset)
})
}
@@ -698,8 +704,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
{
ffi_wrap!("BinaryViewBase::start", unsafe {
let context = &*(ctxt as *mut CustomViewContext<V>);
-
- context.view.assume_init_ref().start()
+ context.assume_init_ref().start()
})
}
@@ -709,8 +714,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
{
ffi_wrap!("BinaryViewBase::len", unsafe {
let context = &*(ctxt as *mut CustomViewContext<V>);
-
- context.view.assume_init_ref().len() as u64
+ context.assume_init_ref().len()
})
}
@@ -720,8 +724,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
{
ffi_wrap!("BinaryViewBase::entry_point", unsafe {
let context = &*(ctxt as *mut CustomViewContext<V>);
-
- context.view.assume_init_ref().entry_point()
+ context.assume_init_ref().entry_point()
})
}
@@ -731,8 +734,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
{
ffi_wrap!("BinaryViewBase::executable", unsafe {
let context = &*(ctxt as *mut CustomViewContext<V>);
-
- context.view.assume_init_ref().executable()
+ context.assume_init_ref().executable()
})
}
@@ -743,7 +745,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
ffi_wrap!("BinaryViewBase::default_endianness", unsafe {
let context = &*(ctxt as *mut CustomViewContext<V>);
- context.view.assume_init_ref().default_endianness()
+ context.assume_init_ref().default_endianness()
})
}
@@ -754,7 +756,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
ffi_wrap!("BinaryViewBase::relocatable", unsafe {
let context = &*(ctxt as *mut CustomViewContext<V>);
- context.view.assume_init_ref().relocatable()
+ context.assume_init_ref().relocatable()
})
}
@@ -765,7 +767,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
ffi_wrap!("BinaryViewBase::address_size", unsafe {
let context = &*(ctxt as *mut CustomViewContext<V>);
- context.view.assume_init_ref().address_size()
+ context.assume_init_ref().address_size()
})
}
@@ -780,10 +782,8 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
}
let ctxt = Box::new(CustomViewContext::<V> {
- view: mem::MaybeUninit::uninit(),
raw_handle: ptr::null_mut(),
- initialized: false,
- args: view_args,
+ state: CustomViewContextState::Uninitialized { args: view_args },
});
let ctxt = Box::into_raw(ctxt);
@@ -837,7 +837,7 @@ impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
(*ctxt).raw_handle = res;
Ok(CustomView {
- handle: BinaryView::from_raw(res),
+ handle: BinaryView::ref_from_raw(res),
_builder: PhantomData,
})
}
diff --git a/rust/src/databuffer.rs b/rust/src/data_buffer.rs
index 29d7636b..5b4d6515 100644
--- a/rust/src/databuffer.rs
+++ b/rust/src/data_buffer.rs
@@ -27,6 +27,7 @@ impl DataBuffer {
pub(crate) fn from_raw(raw: *mut BNDataBuffer) -> Self {
DataBuffer(raw)
}
+
pub(crate) fn as_raw(&self) -> *mut BNDataBuffer {
self.0
}
@@ -110,11 +111,17 @@ impl DataBuffer {
}
pub fn to_escaped_string(&self, null_terminates: bool, escape_printable: bool) -> BnString {
- unsafe { BnString::from_raw(BNDataBufferToEscapedString(self.0, null_terminates, escape_printable)) }
+ unsafe {
+ BnString::from_raw(BNDataBufferToEscapedString(
+ self.0,
+ null_terminates,
+ escape_printable,
+ ))
+ }
}
pub fn from_escaped_string(value: &BnString) -> Self {
- Self(unsafe { BNDecodeEscapedString(value.as_raw()) })
+ Self(unsafe { BNDecodeEscapedString(value.as_ptr()) })
}
pub fn to_base64(&self) -> BnString {
@@ -122,7 +129,7 @@ impl DataBuffer {
}
pub fn from_base64(value: &BnString) -> Self {
- Self(unsafe { BNDecodeBase64(value.as_raw()) })
+ Self(unsafe { BNDecodeBase64(value.as_ptr()) })
}
pub fn zlib_compress(&self) -> Self {
@@ -165,7 +172,7 @@ impl DataBuffer {
impl Default for DataBuffer {
fn default() -> Self {
- Self(unsafe { BNCreateDataBuffer([].as_ptr() as *const c_void, 0) })
+ Self(unsafe { BNCreateDataBuffer([].as_ptr(), 0) })
}
}
@@ -239,95 +246,3 @@ impl Ord for DataBuffer {
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);
- }
-}
diff --git a/rust/src/database.rs b/rust/src/database.rs
index 3274c9c0..7174ebe8 100644
--- a/rust/src/database.rs
+++ b/rust/src/database.rs
@@ -1,115 +1,123 @@
-use std::collections::HashMap;
-use std::time::{Duration, SystemTime, UNIX_EPOCH};
-use std::{ffi, mem, ptr};
+pub mod kvs;
+pub mod snapshot;
+pub mod undo;
use binaryninjacore_sys::*;
+use std::collections::HashMap;
+use std::ffi::{c_char, c_void};
+use std::fmt::Debug;
+use std::ptr::NonNull;
-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::binary_view::BinaryView;
+use crate::data_buffer::DataBuffer;
+use crate::database::kvs::KeyValueStore;
+use crate::database::snapshot::{Snapshot, SnapshotId};
+use crate::file_metadata::FileMetadata;
+use crate::progress::{NoProgressCallback, ProgressCallback};
+use crate::rc::{Array, Ref, RefCountable};
use crate::string::{BnStrCompatible, BnString};
-#[repr(transparent)]
pub struct Database {
- handle: ptr::NonNull<BNDatabase>,
+ pub(crate) handle: NonNull<BNDatabase>,
}
impl Database {
- pub(crate) unsafe fn from_raw(handle: ptr::NonNull<BNDatabase>) -> Self {
+ pub(crate) unsafe fn from_raw(handle: NonNull<BNDatabase>) -> Self {
Self { handle }
}
- #[allow(clippy::mut_from_ref)]
- pub(crate) unsafe fn as_raw(&self) -> &mut BNDatabase {
- &mut *self.handle.as_ptr()
+ pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNDatabase>) -> Ref<Self> {
+ Ref::new(Self { handle })
}
/// 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) })
+ pub fn snapshot_by_id(&self, id: SnapshotId) -> Option<Ref<Snapshot>> {
+ let result = unsafe { BNGetDatabaseSnapshot(self.handle.as_ptr(), id.0) };
+ NonNull::new(result).map(|handle| unsafe { Snapshot::ref_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) };
+ let result = unsafe { BNGetDatabaseSnapshots(self.handle.as_ptr(), &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 current_snapshot(&self) -> Option<Ref<Snapshot>> {
+ let result = unsafe { BNGetDatabaseCurrentSnapshot(self.handle.as_ptr()) };
+ NonNull::new(result).map(|handle| unsafe { Snapshot::ref_from_raw(handle) })
}
+ /// Equivalent to [`Self::set_current_snapshot_id`].
pub fn set_current_snapshot(&self, value: &Snapshot) {
- unsafe { BNSetDatabaseCurrentSnapshot(self.as_raw(), value.id()) }
+ self.set_current_snapshot_id(value.id())
+ }
+
+ /// Sets the current snapshot to the [`SnapshotId`].
+ ///
+ /// **No** validation is done to ensure that the id is valid.
+ pub fn set_current_snapshot_id(&self, id: SnapshotId) {
+ unsafe { BNSetDatabaseCurrentSnapshot(self.handle.as_ptr(), id.0) }
}
pub fn write_snapshot_data<N: BnStrCompatible>(
&self,
- parents: &[i64],
+ parents: &[SnapshotId],
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),
- )
- }
+ ) -> SnapshotId {
+ self.write_snapshot_data_with_progress(
+ parents,
+ file,
+ name,
+ data,
+ auto_save,
+ NoProgressCallback,
+ )
}
- pub fn write_snapshot_data_with_progress<N, F>(
+ pub fn write_snapshot_data_with_progress<N, P>(
&self,
- parents: &[i64],
+ parents: &[SnapshotId],
file: &BinaryView,
name: N,
data: &KeyValueStore,
auto_save: bool,
- mut progress: F,
- ) -> i64
+ mut progress: P,
+ ) -> SnapshotId
where
N: BnStrCompatible,
- F: FnMut(usize, usize) -> bool,
+ P: ProgressCallback,
{
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 {
+ let name_ptr = name_raw.as_ref().as_ptr() as *const c_char;
+
+ let new_id = unsafe {
BNWriteDatabaseSnapshotData(
- self.as_raw(),
+ self.handle.as_ptr(),
+ // SAFETY: SnapshotId is just i64
parents.as_ptr() as *mut _,
parents.len(),
file.handle,
name_ptr,
- data.as_raw(),
+ data.handle.as_ptr(),
auto_save,
- ctxt,
- Some(cb_progress::<F>),
+ &mut progress as *mut P as *mut c_void,
+ Some(P::cb_progress_callback),
)
- }
+ };
+
+ SnapshotId(new_id)
}
/// 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) } {
+ pub fn trim_snapshot(&self, id: SnapshotId) -> Result<(), ()> {
+ if unsafe { BNTrimDatabaseSnapshot(self.handle.as_ptr(), id.0) } {
Ok(())
} else {
Err(())
@@ -118,8 +126,8 @@ impl Database {
/// 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) } {
+ pub fn remove_snapshot(&self, id: SnapshotId) -> Result<(), ()> {
+ if unsafe { BNRemoveDatabaseSnapshot(self.handle.as_ptr(), id.0) } {
Ok(())
} else {
Err(())
@@ -127,14 +135,14 @@ impl Database {
}
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 }
+ let key_ptr = key_raw.as_ref().as_ptr() as *const c_char;
+ unsafe { BNDatabaseHasGlobal(self.handle.as_ptr(), 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) };
+ let result = unsafe { BNGetDatabaseGlobalKeys(self.handle.as_ptr(), &mut count) };
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
@@ -150,505 +158,92 @@ impl Database {
/// 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)) }
+ let key_ptr = key_raw.as_ref().as_ptr() as *const c_char;
+ let result = unsafe { BNReadDatabaseGlobal(self.handle.as_ptr(), key_ptr) };
+ unsafe { 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 key_ptr = key_raw.as_ref().as_ptr() as *const 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) }
+ let value_ptr = value_raw.as_ref().as_ptr() as *const c_char;
+ unsafe { BNWriteDatabaseGlobal(self.handle.as_ptr(), 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))
+ let key_ptr = key_raw.as_ref().as_ptr() as *const c_char;
+ let result = unsafe { BNReadDatabaseGlobalData(self.handle.as_ptr(), key_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()) }
+ let key_ptr = key_raw.as_ref().as_ptr() as *const c_char;
+ unsafe { BNWriteDatabaseGlobalData(self.handle.as_ptr(), key_ptr, value.as_raw()) }
}
/// Get the owning FileMetadata
pub fn file(&self) -> Ref<FileMetadata> {
- let result = unsafe { BNGetDatabaseFile(self.as_raw()) };
+ let result = unsafe { BNGetDatabaseFile(self.handle.as_ptr()) };
assert!(!result.is_null());
- unsafe { Ref::new(FileMetadata::from_raw(result)) }
+ FileMetadata::ref_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 analysis_cache(&self) -> Ref<KeyValueStore> {
+ let result = unsafe { BNReadDatabaseAnalysisCache(self.handle.as_ptr()) };
+ unsafe { KeyValueStore::ref_from_raw(NonNull::new(result).unwrap()) }
}
pub fn reload_connection(&self) {
- unsafe { BNDatabaseReloadConnection(self.as_raw()) }
+ unsafe { BNDatabaseReloadConnection(self.handle.as_ptr()) }
}
pub fn write_analysis_cache(&self, val: &KeyValueStore) -> Result<(), ()> {
- if unsafe { BNWriteDatabaseAnalysisCache(self.as_raw(), val.as_raw()) } {
+ if unsafe { BNWriteDatabaseAnalysisCache(self.handle.as_ptr(), val.handle.as_ptr()) } {
Ok(())
} else {
Err(())
}
}
- pub fn snapshot_has_data(&self, id: i64) -> bool {
- unsafe { BNSnapshotHasData(self.as_raw(), id) }
+ pub fn snapshot_has_data(&self, id: SnapshotId) -> bool {
+ unsafe { BNSnapshotHasData(self.handle.as_ptr(), id.0) }
}
}
-impl Clone for Database {
- fn clone(&self) -> Self {
- unsafe { Self::from_raw(ptr::NonNull::new(BNNewDatabaseReference(self.as_raw())).unwrap()) }
+impl Debug for Database {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("Database")
+ .field("current_snapshot", &self.current_snapshot())
+ .field("snapshot_count", &self.snapshots().len())
+ .field("globals", &self.globals())
+ .field("analysis_cache", &self.analysis_cache())
+ .finish()
}
}
-impl Drop for Database {
- fn drop(&mut self) {
- unsafe { BNFreeDatabase(self.as_raw()) }
- }
-}
-
-#[repr(transparent)]
-pub struct Snapshot {
- handle: ptr::NonNull<BNSnapshot>,
-}
+impl ToOwned for Database {
+ type Owned = Ref<Self>;
-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()) }
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
}
}
-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 impl RefCountable for Database {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: NonNull::new(BNNewDatabaseReference(handle.handle.as_ptr())).unwrap(),
+ })
}
- unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
- Self::ref_from_raw(raw)
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeDatabase(handle.handle.as_ptr());
}
}
-
-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
-}
diff --git a/rust/src/database/kvs.rs b/rust/src/database/kvs.rs
new file mode 100644
index 00000000..4b77bbdb
--- /dev/null
+++ b/rust/src/database/kvs.rs
@@ -0,0 +1,135 @@
+use crate::data_buffer::DataBuffer;
+use crate::rc::{Array, Ref, RefCountable};
+use crate::string::{BnStrCompatible, BnString};
+use binaryninjacore_sys::{
+ BNBeginKeyValueStoreNamespace, BNEndKeyValueStoreNamespace, BNFreeKeyValueStore,
+ BNGetKeyValueStoreBuffer, BNGetKeyValueStoreDataSize, BNGetKeyValueStoreKeys,
+ BNGetKeyValueStoreNamespaceSize, BNGetKeyValueStoreSerializedData, BNGetKeyValueStoreValueSize,
+ BNGetKeyValueStoreValueStorageSize, BNIsKeyValueStoreEmpty, BNKeyValueStore,
+ BNNewKeyValueStoreReference, BNSetKeyValueStoreBuffer,
+};
+use std::collections::HashMap;
+use std::ffi::c_char;
+use std::fmt::Debug;
+use std::ptr::NonNull;
+
+#[repr(transparent)]
+pub struct KeyValueStore {
+ pub(crate) handle: NonNull<BNKeyValueStore>,
+}
+
+impl KeyValueStore {
+ pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNKeyValueStore>) -> Ref<Self> {
+ Ref::new(Self { handle })
+ }
+
+ pub fn to_hashmap(&self) -> HashMap<String, DataBuffer> {
+ let mut hashmap = HashMap::with_capacity(self.keys().len());
+ for key in self.keys().iter() {
+ if let Some(value) = self.value(key) {
+ hashmap.insert(key.to_string(), value);
+ }
+ }
+ hashmap
+ }
+
+ /// 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.handle.as_ptr(), &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 c_char;
+ let result = unsafe { BNGetKeyValueStoreBuffer(self.handle.as_ptr(), key_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 c_char;
+ unsafe { BNSetKeyValueStoreBuffer(self.handle.as_ptr(), key_ptr, value.as_raw()) }
+ }
+
+ /// Get the stored representation of the kvs
+ pub fn serialized_data(&self) -> DataBuffer {
+ let result = unsafe { BNGetKeyValueStoreSerializedData(self.handle.as_ptr()) };
+ 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 c_char;
+ unsafe { BNBeginKeyValueStoreNamespace(self.handle.as_ptr(), name_ptr) }
+ }
+
+ /// End storing new keys into a namespace
+ pub fn end_namespace(&self) {
+ unsafe { BNEndKeyValueStoreNamespace(self.handle.as_ptr()) }
+ }
+
+ /// If the kvs is empty
+ pub fn is_empty(&self) -> bool {
+ unsafe { BNIsKeyValueStoreEmpty(self.handle.as_ptr()) }
+ }
+
+ /// Number of values in the kvs
+ pub fn value_size(&self) -> usize {
+ unsafe { BNGetKeyValueStoreValueSize(self.handle.as_ptr()) }
+ }
+
+ /// Length of serialized data
+ pub fn data_size(&self) -> usize {
+ unsafe { BNGetKeyValueStoreDataSize(self.handle.as_ptr()) }
+ }
+
+ /// Size of all data in storage
+ pub fn value_storage_size(&self) -> usize {
+ unsafe { BNGetKeyValueStoreValueStorageSize(self.handle.as_ptr()) }
+ }
+
+ /// Number of namespaces pushed with begin_namespace
+ pub fn namespace_size(&self) -> usize {
+ unsafe { BNGetKeyValueStoreNamespaceSize(self.handle.as_ptr()) }
+ }
+}
+
+impl ToOwned for KeyValueStore {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for KeyValueStore {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: NonNull::new(BNNewKeyValueStoreReference(handle.handle.as_ptr())).unwrap(),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeKeyValueStore(handle.handle.as_ptr());
+ }
+}
+
+impl Debug for KeyValueStore {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("KeyValueStore")
+ .field("keys", &self.keys().to_vec())
+ .field("is_empty", &self.is_empty())
+ .field("value_size", &self.value_size())
+ .field("data_size", &self.data_size())
+ .field("value_storage_size", &self.value_storage_size())
+ .field("namespace_size", &self.namespace_size())
+ .finish()
+ }
+}
diff --git a/rust/src/database/snapshot.rs b/rust/src/database/snapshot.rs
new file mode 100644
index 00000000..35b0dc46
--- /dev/null
+++ b/rust/src/database/snapshot.rs
@@ -0,0 +1,277 @@
+use crate::data_buffer::DataBuffer;
+use crate::database::kvs::KeyValueStore;
+use crate::database::undo::UndoEntry;
+use crate::database::Database;
+use crate::progress::ProgressCallback;
+use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
+use crate::string::{BnStrCompatible, BnString};
+use binaryninjacore_sys::{
+ BNCollaborationFreeSnapshotIdList, BNFreeSnapshot, BNFreeSnapshotList, BNGetSnapshotChildren,
+ BNGetSnapshotDatabase, BNGetSnapshotFileContents, BNGetSnapshotFileContentsHash,
+ BNGetSnapshotFirstParent, BNGetSnapshotId, BNGetSnapshotName, BNGetSnapshotParents,
+ BNGetSnapshotUndoData, BNGetSnapshotUndoEntries, BNGetSnapshotUndoEntriesWithProgress,
+ BNIsSnapshotAutoSave, BNNewSnapshotReference, BNReadSnapshotData,
+ BNReadSnapshotDataWithProgress, BNSetSnapshotName, BNSnapshot, BNSnapshotHasAncestor,
+ BNSnapshotHasContents, BNSnapshotHasUndo, BNSnapshotStoreData,
+};
+use std::ffi::{c_char, c_void};
+use std::fmt;
+use std::fmt::{Debug, Display, Formatter};
+use std::ptr::NonNull;
+
+pub struct Snapshot {
+ pub(crate) handle: NonNull<BNSnapshot>,
+}
+
+impl Snapshot {
+ pub(crate) unsafe fn from_raw(handle: NonNull<BNSnapshot>) -> Self {
+ Self { handle }
+ }
+
+ pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNSnapshot>) -> Ref<Self> {
+ Ref::new(Self { handle })
+ }
+
+ /// Get the owning database
+ pub fn database(&self) -> Database {
+ unsafe {
+ Database::from_raw(NonNull::new(BNGetSnapshotDatabase(self.handle.as_ptr())).unwrap())
+ }
+ }
+
+ /// Get the numerical id
+ pub fn id(&self) -> SnapshotId {
+ SnapshotId(unsafe { BNGetSnapshotId(self.handle.as_ptr()) })
+ }
+
+ /// Get the displayed snapshot name
+ pub fn name(&self) -> BnString {
+ unsafe { BnString::from_raw(BNGetSnapshotName(self.handle.as_ptr())) }
+ }
+
+ /// 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 c_char;
+ unsafe { BNSetSnapshotName(self.handle.as_ptr(), value_ptr) }
+ }
+
+ /// If the snapshot was the result of an auto-save
+ pub fn is_auto_save(&self) -> bool {
+ unsafe { BNIsSnapshotAutoSave(self.handle.as_ptr()) }
+ }
+
+ /// If the snapshot has contents, and has not been trimmed
+ pub fn has_contents(&self) -> bool {
+ unsafe { BNSnapshotHasContents(self.handle.as_ptr()) }
+ }
+
+ /// If the snapshot has undo data
+ pub fn has_undo(&self) -> bool {
+ unsafe { BNSnapshotHasUndo(self.handle.as_ptr()) }
+ }
+
+ /// 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.handle.as_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.handle.as_ptr(), &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.handle.as_ptr(), &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.handle.as_ptr());
+ 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.handle.as_ptr());
+ 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.handle.as_ptr(), &mut count) };
+ assert!(!result.is_null());
+ unsafe { Array::new(result, count, ()) }
+ }
+
+ pub fn undo_entries_with_progress<P: ProgressCallback>(
+ &self,
+ mut progress: P,
+ ) -> Array<UndoEntry> {
+ assert!(self.has_undo());
+ let mut count = 0;
+
+ let result = unsafe {
+ BNGetSnapshotUndoEntriesWithProgress(
+ self.handle.as_ptr(),
+ &mut progress as *mut P as *mut c_void,
+ Some(P::cb_progress_callback),
+ &mut count,
+ )
+ };
+
+ assert!(!result.is_null());
+ unsafe { Array::new(result, count, ()) }
+ }
+
+ /// Get the backing kvs data with snapshot fields
+ pub fn read_data(&self) -> Ref<KeyValueStore> {
+ let result = unsafe { BNReadSnapshotData(self.handle.as_ptr()) };
+ unsafe { KeyValueStore::ref_from_raw(NonNull::new(result).unwrap()) }
+ }
+
+ pub fn read_data_with_progress<P: ProgressCallback>(
+ &self,
+ mut progress: P,
+ ) -> Ref<KeyValueStore> {
+ let result = unsafe {
+ BNReadSnapshotDataWithProgress(
+ self.handle.as_ptr(),
+ &mut progress as *mut P as *mut c_void,
+ Some(P::cb_progress_callback),
+ )
+ };
+
+ unsafe { KeyValueStore::ref_from_raw(NonNull::new(result).unwrap()) }
+ }
+
+ pub fn undo_data(&self) -> DataBuffer {
+ let result = unsafe { BNGetSnapshotUndoData(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ DataBuffer::from_raw(result)
+ }
+
+ pub fn store_data(&self, data: &KeyValueStore) -> bool {
+ unsafe {
+ BNSnapshotStoreData(
+ self.handle.as_ptr(),
+ data.handle.as_ptr(),
+ std::ptr::null_mut(),
+ None,
+ )
+ }
+ }
+
+ pub fn store_data_with_progress<P: ProgressCallback>(
+ &self,
+ data: &KeyValueStore,
+ mut progress: P,
+ ) -> bool {
+ unsafe {
+ BNSnapshotStoreData(
+ self.handle.as_ptr(),
+ data.handle.as_ptr(),
+ &mut progress as *mut P as *mut c_void,
+ Some(P::cb_progress_callback),
+ )
+ }
+ }
+
+ /// Determine if this snapshot has another as an ancestor
+ pub fn has_ancestor(self, other: &Snapshot) -> bool {
+ unsafe { BNSnapshotHasAncestor(self.handle.as_ptr(), other.handle.as_ptr()) }
+ }
+}
+
+impl Debug for Snapshot {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("Snapshot")
+ .field("id", &self.id())
+ .field("name", &self.name())
+ .field("is_auto_save", &self.is_auto_save())
+ .field("has_contents", &self.has_contents())
+ .field("has_undo", &self.has_undo())
+ // TODO: This might be too much.
+ .field("children", &self.children().to_vec())
+ .finish()
+ }
+}
+
+impl ToOwned for Snapshot {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for Snapshot {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: NonNull::new(BNNewSnapshotReference(handle.handle.as_ptr())).unwrap(),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeSnapshot(handle.handle.as_ptr());
+ }
+}
+
+impl CoreArrayProvider for Snapshot {
+ type Raw = *mut BNSnapshot;
+ type Context = ();
+ type Wrapped<'a> = Guard<'a, Snapshot>;
+}
+
+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> {
+ let raw_ptr = NonNull::new(*raw).unwrap();
+ Guard::new(Self::from_raw(raw_ptr), context)
+ }
+}
+
+#[repr(transparent)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct SnapshotId(pub i64);
+
+impl Display for SnapshotId {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_fmt(format_args!("{}", self.0))
+ }
+}
+
+impl CoreArrayProvider for SnapshotId {
+ type Raw = i64;
+ type Context = ();
+ type Wrapped<'a> = SnapshotId;
+}
+
+unsafe impl CoreArrayProviderInner for SnapshotId {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNCollaborationFreeSnapshotIdList(raw, count)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ SnapshotId(*raw)
+ }
+}
diff --git a/rust/src/database/undo.rs b/rust/src/database/undo.rs
new file mode 100644
index 00000000..727a777b
--- /dev/null
+++ b/rust/src/database/undo.rs
@@ -0,0 +1,168 @@
+use crate::disassembly::InstructionTextToken;
+use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
+use crate::string::BnString;
+use binaryninjacore_sys::{
+ BNFreeUndoAction, BNFreeUndoActionList, BNFreeUndoEntry, BNFreeUndoEntryList,
+ BNNewUndoActionReference, BNNewUndoEntryReference, BNUndoAction, BNUndoActionGetSummary,
+ BNUndoActionGetSummaryText, BNUndoEntry, BNUndoEntryGetActions, BNUndoEntryGetId,
+ BNUndoEntryGetTimestamp,
+};
+use std::fmt::Debug;
+use std::ptr::NonNull;
+use std::time::{Duration, SystemTime, UNIX_EPOCH};
+
+#[repr(transparent)]
+pub struct UndoEntry {
+ handle: NonNull<BNUndoEntry>,
+}
+
+impl UndoEntry {
+ pub(crate) unsafe fn from_raw(handle: NonNull<BNUndoEntry>) -> Self {
+ Self { handle }
+ }
+
+ #[allow(dead_code)]
+ pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNUndoEntry>) -> Ref<Self> {
+ Ref::new(Self { handle })
+ }
+
+ pub fn id(&self) -> BnString {
+ let result = unsafe { BNUndoEntryGetId(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ pub fn actions(&self) -> Array<UndoAction> {
+ let mut count = 0;
+ let result = unsafe { BNUndoEntryGetActions(self.handle.as_ptr(), &mut count) };
+ assert!(!result.is_null());
+ unsafe { Array::new(result, count, ()) }
+ }
+
+ pub fn time(&self) -> SystemTime {
+ let m = Duration::from_secs(unsafe { BNUndoEntryGetTimestamp(self.handle.as_ptr()) });
+ UNIX_EPOCH + m
+ }
+}
+
+impl Debug for UndoEntry {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("UndoEntry")
+ .field("id", &self.id())
+ .field("time", &self.time())
+ .field("actions", &self.actions().to_vec())
+ .finish()
+ }
+}
+
+impl ToOwned for UndoEntry {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for UndoEntry {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: NonNull::new(BNNewUndoEntryReference(handle.handle.as_ptr())).unwrap(),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeUndoEntry(handle.handle.as_ptr());
+ }
+}
+
+impl CoreArrayProvider for UndoEntry {
+ type Raw = *mut BNUndoEntry;
+ type Context = ();
+ type Wrapped<'a> = Guard<'a, UndoEntry>;
+}
+
+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> {
+ let raw_ptr = NonNull::new(*raw).unwrap();
+ Guard::new(Self::from_raw(raw_ptr), context)
+ }
+}
+
+#[repr(transparent)]
+pub struct UndoAction {
+ handle: NonNull<BNUndoAction>,
+}
+
+impl UndoAction {
+ pub(crate) unsafe fn from_raw(handle: NonNull<BNUndoAction>) -> Self {
+ Self { handle }
+ }
+
+ #[allow(dead_code)]
+ pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNUndoAction>) -> Ref<Self> {
+ Ref::new(Self { handle })
+ }
+
+ pub fn summary(&self) -> Array<InstructionTextToken> {
+ let mut count = 0;
+ let result = unsafe { BNUndoActionGetSummary(self.handle.as_ptr(), &mut count) };
+ assert!(!result.is_null());
+ unsafe { Array::new(result, count, ()) }
+ }
+
+ /// Gets the [`UndoAction`] summary as text rather than [`InstructionTextToken`]'s.
+ pub fn summary_as_string(&self) -> BnString {
+ let result = unsafe { BNUndoActionGetSummaryText(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+}
+
+impl Debug for UndoAction {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("UndoAction")
+ .field("summary", &self.summary_as_string())
+ .finish()
+ }
+}
+
+impl ToOwned for UndoAction {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for UndoAction {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: NonNull::new(BNNewUndoActionReference(handle.handle.as_ptr())).unwrap(),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeUndoAction(handle.handle.as_ptr());
+ }
+}
+
+impl CoreArrayProvider for UndoAction {
+ type Raw = *mut BNUndoAction;
+ type Context = ();
+ type Wrapped<'a> = Guard<'a, UndoAction>;
+}
+
+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> {
+ let raw_ptr = NonNull::new(*raw).unwrap();
+ Guard::new(Self::from_raw(raw_ptr), context)
+ }
+}
diff --git a/rust/src/debuginfo.rs b/rust/src/debuginfo.rs
index 2b43c9cc..72d5fdab 100644
--- a/rust/src/debuginfo.rs
+++ b/rust/src/debuginfo.rs
@@ -29,7 +29,7 @@
//! Here's a minimal, complete example boilerplate-plugin:
//! ```no_run
//! use binaryninja::{
-//! binaryview::BinaryView,
+//! binary_view::BinaryView,
//! debuginfo::{CustomDebugInfoParser, DebugInfo, DebugInfoParser},
//! };
//!
@@ -40,7 +40,13 @@
//! true
//! }
//!
-//! fn parse_info(&self, _debug_info: &mut DebugInfo, _view: &BinaryView, _debug_file: &BinaryView, _progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>) -> 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
//! }
@@ -56,11 +62,11 @@
//! `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:
//! ```no_run
//! # use binaryninja::debuginfo::DebugInfoParser;
-//! # use binaryninja::binaryview::BinaryViewExt;
+//! # use binaryninja::binary_view::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();
+//! let debug_info = parser.parse_debug_info(&bv, &bv, None).unwrap();
//! bv.apply_debug_info(&debug_info);
//! ```
//!
@@ -69,21 +75,30 @@
//! wish to set the debug info for a binary view without applying it as well, you can call `binaryninja::binaryview::BinaryView::set_debug_info`.
use binaryninjacore_sys::*;
+use std::ffi::c_void;
+use crate::progress::{NoProgressCallback, ProgressCallback};
+use crate::variable::{NamedDataVariableWithType, NamedVariableWithType};
use crate::{
- binaryview::BinaryView,
+ binary_view::BinaryView,
platform::Platform,
rc::*,
string::{raw_to_string, BnStrCompatible, BnString},
- types::{DataVariableAndName, NameAndType, NamedTypedVariable, Type},
+ types::{NameAndType, Type},
};
-use std::{hash::Hash, os::raw::c_void, ptr, slice};
-
-struct ProgressContext(Option<Box<dyn Fn(usize, usize) -> Result<(), ()>>>);
+/// Implement this trait to implement a debug info parser. See `DebugInfoParser` for more details.
+pub trait CustomDebugInfoParser: 'static + Sync {
+ fn is_valid(&self, view: &BinaryView) -> bool;
-//////////////////////
-// DebugInfoParser
+ fn parse_info(
+ &self,
+ debug_info: &mut DebugInfo,
+ view: &BinaryView,
+ debug_file: &BinaryView,
+ progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
+ ) -> bool;
+}
/// Represents the registered parsers and providers of debug information to Binary Ninja.
/// See `binaryninja::debuginfo` for more information
@@ -135,25 +150,33 @@ impl DebugInfoParser {
unsafe { BNIsDebugInfoParserValidForView(self.handle, view.handle) }
}
- extern "C" fn cb_progress(ctxt: *mut c_void, cur: usize, max: usize) -> bool {
- ffi_wrap!("DebugInfoParser::cb_progress", unsafe {
- let progress = ctxt as *mut ProgressContext;
- match &(*progress).0 {
- Some(func) => (func)(cur, max).is_ok(),
- None => true,
- }
- })
+ /// Returns [`DebugInfo`] populated with debug info by this debug-info parser.
+ ///
+ /// Only provide a `DebugInfo` object if you wish to append to the existing debug info
+ pub fn parse_debug_info(
+ &self,
+ view: &BinaryView,
+ debug_file: &BinaryView,
+ existing_debug_info: Option<&DebugInfo>,
+ ) -> Option<Ref<DebugInfo>> {
+ self.parse_debug_info_with_progress(
+ view,
+ debug_file,
+ existing_debug_info,
+ NoProgressCallback,
+ )
}
- /// Returns a `DebugInfo` object populated with debug info by this debug-info parser. Only provide a `DebugInfo` object if you wish to append to the existing debug info
- pub fn parse_debug_info(
+ /// Returns [`DebugInfo`] populated with debug info by this debug-info parser.
+ ///
+ /// Only provide a `DebugInfo` object if you wish to append to the existing debug info
+ pub fn parse_debug_info_with_progress<P: ProgressCallback>(
&self,
view: &BinaryView,
debug_file: &BinaryView,
existing_debug_info: Option<&DebugInfo>,
- progress: Option<Box<dyn Fn(usize, usize) -> Result<(), ()>>>,
+ mut progress: P,
) -> Option<Ref<DebugInfo>> {
- let mut progress_raw = ProgressContext(progress);
let info: *mut BNDebugInfo = match existing_debug_info {
Some(debug_info) => unsafe {
BNParseDebugInfo(
@@ -161,8 +184,8 @@ impl DebugInfoParser {
view.handle,
debug_file.handle,
debug_info.handle,
- Some(Self::cb_progress),
- &mut progress_raw as *mut _ as *mut c_void,
+ Some(P::cb_progress_callback),
+ &mut progress as *mut P as *mut c_void,
)
},
None => unsafe {
@@ -170,16 +193,17 @@ impl DebugInfoParser {
self.handle,
view.handle,
debug_file.handle,
- ptr::null_mut(),
- Some(Self::cb_progress),
- &mut progress_raw as *mut _ as *mut c_void,
+ std::ptr::null_mut(),
+ Some(P::cb_progress_callback),
+ &mut progress as *mut P as *mut c_void,
)
},
};
+
if info.is_null() {
return None;
}
- Some(unsafe { DebugInfo::from_raw(info) })
+ Some(unsafe { DebugInfo::ref_from_raw(info) })
}
// Registers a DebugInfoParser. See `binaryninja::debuginfo::DebugInfoParser` for more details.
@@ -194,7 +218,7 @@ impl DebugInfoParser {
{
ffi_wrap!("CustomDebugInfoParser::is_valid", unsafe {
let cmd = &*(ctxt as *const C);
- let view = BinaryView::from_raw(view);
+ let view = BinaryView::ref_from_raw(view);
cmd.is_valid(&view)
})
@@ -213,9 +237,9 @@ impl DebugInfoParser {
{
ffi_wrap!("CustomDebugInfoParser::parse_info", unsafe {
let cmd = &*(ctxt as *const C);
- let view = BinaryView::from_raw(view);
- let debug_file = BinaryView::from_raw(debug_file);
- let mut debug_info = DebugInfo::from_raw(debug_info);
+ let view = BinaryView::ref_from_raw(view);
+ let debug_file = BinaryView::ref_from_raw(debug_file);
+ let mut debug_info = DebugInfo::ref_from_raw(debug_info);
cmd.parse_info(
&mut debug_info,
@@ -280,6 +304,7 @@ 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)
}
@@ -301,39 +326,37 @@ pub struct DebugFunctionInfo {
address: u64,
platform: Option<Ref<Platform>>,
components: Vec<String>,
- local_variables: Vec<NamedTypedVariable>,
+ local_variables: Vec<NamedVariableWithType>,
}
-impl From<&BNDebugFunctionInfo> for DebugFunctionInfo {
- fn from(raw: &BNDebugFunctionInfo) -> Self {
- let components = unsafe { slice::from_raw_parts(raw.components, raw.componentN) }
+impl DebugFunctionInfo {
+ pub(crate) fn from_raw(value: &BNDebugFunctionInfo) -> Self {
+ let raw_components =
+ unsafe { std::slice::from_raw_parts(value.components, value.componentN) };
+ let components = raw_components
.iter()
- .map(|component| raw_to_string(*component as *const _).unwrap())
+ .filter_map(|&c| raw_to_string(c))
.collect();
-
- let local_variables: Vec<NamedTypedVariable> = unsafe { slice::from_raw_parts(raw.localVariables, raw.localVariableN) }
+ let raw_local_variables =
+ unsafe { std::slice::from_raw_parts(value.localVariables, value.localVariableN) };
+ let local_variables = raw_local_variables
.iter()
- .map(|local_variable| {
- unsafe {
- NamedTypedVariable::from_raw(local_variable)
- }
- })
+ .map(NamedVariableWithType::from_raw)
.collect();
-
Self {
- short_name: raw_to_string(raw.shortName),
- full_name: raw_to_string(raw.fullName),
- raw_name: raw_to_string(raw.rawName),
- type_: if raw.type_.is_null() {
+ short_name: raw_to_string(value.shortName),
+ full_name: raw_to_string(value.fullName),
+ raw_name: raw_to_string(value.rawName),
+ type_: if value.type_.is_null() {
None
} else {
- Some(unsafe { Type::ref_from_raw(raw.type_) })
+ Some(unsafe { Type::from_raw(value.type_) }.to_owned())
},
- address: raw.address,
- platform: if raw.platform.is_null() {
+ address: value.address,
+ platform: if value.platform.is_null() {
None
} else {
- Some(unsafe { Platform::ref_from_raw(raw.platform) })
+ Some(unsafe { Platform::from_raw(value.platform) }.to_owned())
},
components,
local_variables,
@@ -351,7 +374,7 @@ impl DebugFunctionInfo {
address: Option<u64>,
platform: Option<Ref<Platform>>,
components: Vec<String>,
- local_variables: Vec<NamedTypedVariable>,
+ local_variables: Vec<NamedVariableWithType>,
) -> Self {
Self {
short_name,
@@ -388,14 +411,13 @@ pub struct DebugInfo {
}
impl DebugInfo {
- pub(crate) unsafe fn from_raw(handle: *mut BNDebugInfo) -> Ref<Self> {
+ pub(crate) unsafe fn ref_from_raw(handle: *mut BNDebugInfo) -> Ref<Self> {
debug_assert!(!handle.is_null());
-
Ref::new(Self { handle })
}
- /// Returns a generator of all types provided by a named DebugInfoParser
- pub fn types_by_name<S: BnStrCompatible>(&self, parser_name: S) -> Vec<Ref<NameAndType>> {
+ /// Returns all types within the parser
+ pub fn types_by_name<S: BnStrCompatible>(&self, parser_name: S) -> Vec<NameAndType> {
let parser_name = parser_name.into_bytes_with_nul();
let mut count: usize = 0;
@@ -406,10 +428,10 @@ impl DebugInfo {
&mut count,
)
};
- let result: Vec<Ref<NameAndType>> = unsafe {
- slice::from_raw_parts_mut(debug_types_ptr, count)
+ let result: Vec<_> = unsafe {
+ std::slice::from_raw_parts_mut(debug_types_ptr, count)
.iter()
- .map(|x| NameAndType::from_raw(x).to_owned())
+ .map(NameAndType::from_raw)
.collect()
};
@@ -417,14 +439,14 @@ impl DebugInfo {
result
}
- /// A generator of all types provided by DebugInfoParsers
- pub fn types(&self) -> Vec<Ref<NameAndType>> {
+ pub fn types(&self) -> Vec<NameAndType> {
let mut count: usize = 0;
- let debug_types_ptr = unsafe { BNGetDebugTypes(self.handle, ptr::null_mut(), &mut count) };
- let result: Vec<Ref<NameAndType>> = unsafe {
- slice::from_raw_parts_mut(debug_types_ptr, count)
+ let debug_types_ptr =
+ unsafe { BNGetDebugTypes(self.handle, std::ptr::null_mut(), &mut count) };
+ let result: Vec<_> = unsafe {
+ std::slice::from_raw_parts_mut(debug_types_ptr, count)
.iter()
- .map(|x| NameAndType::from_raw(x).to_owned())
+ .map(NameAndType::from_raw)
.collect()
};
@@ -432,11 +454,8 @@ impl DebugInfo {
result
}
- /// Returns a generator of all functions provided by a named DebugInfoParser
- pub fn functions_by_name<S: BnStrCompatible>(
- &self,
- parser_name: S
- ) -> Vec<DebugFunctionInfo> {
+ /// Returns all functions within the parser
+ pub fn functions_by_name<S: BnStrCompatible>(&self, parser_name: S) -> Vec<DebugFunctionInfo> {
let parser_name = parser_name.into_bytes_with_nul();
let mut count: usize = 0;
@@ -449,9 +468,9 @@ impl DebugInfo {
};
let result: Vec<DebugFunctionInfo> = unsafe {
- slice::from_raw_parts_mut(functions_ptr, count)
+ std::slice::from_raw_parts_mut(functions_ptr, count)
.iter()
- .map(DebugFunctionInfo::from)
+ .map(DebugFunctionInfo::from_raw)
.collect()
};
@@ -459,16 +478,15 @@ impl DebugInfo {
result
}
- /// A generator of all functions provided by DebugInfoParsers
pub fn functions(&self) -> Vec<DebugFunctionInfo> {
let mut count: usize = 0;
let functions_ptr =
- unsafe { BNGetDebugFunctions(self.handle, ptr::null_mut(), &mut count) };
+ unsafe { BNGetDebugFunctions(self.handle, std::ptr::null_mut(), &mut count) };
let result: Vec<DebugFunctionInfo> = unsafe {
- slice::from_raw_parts_mut(functions_ptr, count)
+ std::slice::from_raw_parts_mut(functions_ptr, count)
.iter()
- .map(DebugFunctionInfo::from)
+ .map(DebugFunctionInfo::from_raw)
.collect()
};
@@ -476,11 +494,11 @@ impl DebugInfo {
result
}
- /// Returns a generator of all data variables provided by a named DebugInfoParser
+ /// Returns all data variables within the parser
pub fn data_variables_by_name<S: BnStrCompatible>(
&self,
parser_name: S,
- ) -> Vec<DataVariableAndName<String>> {
+ ) -> Vec<NamedDataVariableWithType> {
let parser_name = parser_name.into_bytes_with_nul();
let mut count: usize = 0;
@@ -492,10 +510,10 @@ impl DebugInfo {
)
};
- let result: Vec<DataVariableAndName<String>> = unsafe {
- slice::from_raw_parts_mut(data_variables_ptr, count)
+ let result: Vec<NamedDataVariableWithType> = unsafe {
+ std::slice::from_raw_parts_mut(data_variables_ptr, count)
.iter()
- .map(DataVariableAndName::<String>::from_raw)
+ .map(NamedDataVariableWithType::from_raw)
.collect()
};
@@ -503,16 +521,15 @@ impl DebugInfo {
result
}
- /// A generator of all data variables provided by DebugInfoParsers
- pub fn data_variables(&self) -> Vec<DataVariableAndName<String>> {
+ pub fn data_variables(&self) -> Vec<NamedDataVariableWithType> {
let mut count: usize = 0;
let data_variables_ptr =
- unsafe { BNGetDebugDataVariables(self.handle, ptr::null_mut(), &mut count) };
+ unsafe { BNGetDebugDataVariables(self.handle, std::ptr::null_mut(), &mut count) };
- let result: Vec<DataVariableAndName<String>> = unsafe {
- slice::from_raw_parts_mut(data_variables_ptr, count)
+ let result: Vec<NamedDataVariableWithType> = unsafe {
+ std::slice::from_raw_parts_mut(data_variables_ptr, count)
.iter()
- .map(DataVariableAndName::<String>::from_raw)
+ .map(NamedDataVariableWithType::from_raw)
.collect()
};
@@ -520,7 +537,6 @@ impl DebugInfo {
result
}
- /// May return nullptr
pub fn type_by_name<S: BnStrCompatible>(&self, parser_name: S, name: S) -> Option<Ref<Type>> {
let parser_name = parser_name.into_bytes_with_nul();
let name = name.into_bytes_with_nul();
@@ -543,11 +559,10 @@ impl DebugInfo {
&self,
parser_name: S,
name: S,
- ) -> Option<(u64, Ref<Type>)> {
+ ) -> Option<NamedDataVariableWithType> {
let parser_name = parser_name.into_bytes_with_nul();
let name = name.into_bytes_with_nul();
-
- let result = unsafe {
+ let raw_named_var = unsafe {
BNGetDebugDataVariableByName(
self.handle,
parser_name.as_ref().as_ptr() as *mut _,
@@ -555,9 +570,8 @@ impl DebugInfo {
)
};
- if !result.is_null() {
- unsafe { BNFreeString((*result).name) };
- Some(unsafe { ((*result).address, Type::ref_from_raw((*result).type_)) })
+ if !raw_named_var.is_null() {
+ Some(unsafe { NamedDataVariableWithType::from_ref_raw(raw_named_var) })
} else {
None
}
@@ -567,9 +581,9 @@ impl DebugInfo {
&self,
parser_name: S,
address: u64,
- ) -> Option<(String, Ref<Type>)> {
+ ) -> Option<NamedDataVariableWithType> {
let parser_name = parser_name.into_bytes_with_nul();
- let name_and_var = unsafe {
+ let raw_named_var = unsafe {
BNGetDebugDataVariableByAddress(
self.handle,
parser_name.as_ref().as_ptr() as *mut _,
@@ -577,45 +591,31 @@ impl DebugInfo {
)
};
- if !name_and_var.is_null() {
- let result = unsafe {
- (
- raw_to_string((*name_and_var).name).unwrap(),
- Type::ref_from_raw((*name_and_var).type_),
- )
- };
- unsafe { BNFreeString((*name_and_var).name) };
- Some(result)
+ if !raw_named_var.is_null() {
+ Some(unsafe { NamedDataVariableWithType::from_ref_raw(raw_named_var) })
} else {
None
}
}
- // The tuple is (DebugInfoParserName, type)
- pub fn get_types_by_name<S: BnStrCompatible>(&self, name: S) -> Vec<(String, Ref<Type>)> {
- let name = name.into_bytes_with_nul();
-
+ /// Returns a list of [`NameAndType`] where the `name` is the parser the type originates from.
+ pub fn get_types_by_name<S: BnStrCompatible>(&self, name: S) -> Vec<NameAndType> {
let mut count: usize = 0;
- let raw_names_and_types = unsafe {
+ let name = name.into_bytes_with_nul();
+ let raw_names_and_types_ptr = unsafe {
BNGetDebugTypesByName(self.handle, name.as_ref().as_ptr() as *mut _, &mut count)
};
- let names_and_types: &[*mut BNNameAndType] =
- unsafe { slice::from_raw_parts(raw_names_and_types as *mut _, count) };
+ let raw_names_and_types: &[BNNameAndType] =
+ unsafe { std::slice::from_raw_parts(raw_names_and_types_ptr, count) };
- let result = names_and_types
+ let names_and_types = raw_names_and_types
.iter()
- .take(count)
- .map(|&name_and_type| unsafe {
- (
- raw_to_string((*name_and_type).name).unwrap(),
- Type::ref_from_raw(BNNewTypeReference((*name_and_type).type_)),
- )
- })
+ .map(NameAndType::from_raw)
.collect();
- unsafe { BNFreeNameAndTypeList(raw_names_and_types, count) };
- result
+ unsafe { BNFreeNameAndTypeList(raw_names_and_types_ptr, count) };
+ names_and_types
}
// The tuple is (DebugInfoParserName, address, type)
@@ -631,7 +631,7 @@ impl DebugInfo {
};
let variables_and_names: &[*mut BNDataVariableAndName] =
- unsafe { slice::from_raw_parts(raw_variables_and_names as *mut _, count) };
+ unsafe { std::slice::from_raw_parts(raw_variables_and_names as *mut _, count) };
let result = variables_and_names
.iter()
@@ -640,7 +640,7 @@ impl DebugInfo {
(
raw_to_string((*variable_and_name).name).unwrap(),
(*variable_and_name).address,
- Type::ref_from_raw(BNNewTypeReference((*variable_and_name).type_)),
+ Type::from_raw((*variable_and_name).type_).to_owned(),
)
})
.collect();
@@ -656,7 +656,7 @@ impl DebugInfo {
unsafe { BNGetDebugDataVariablesByAddress(self.handle, address, &mut count) };
let variables_and_names: &[*mut BNDataVariableAndNameAndDebugParser] =
- unsafe { slice::from_raw_parts(raw_variables_and_names as *mut _, count) };
+ unsafe { std::slice::from_raw_parts(raw_variables_and_names as *mut _, count) };
let result = variables_and_names
.iter()
@@ -665,7 +665,7 @@ impl DebugInfo {
(
raw_to_string((*variable_and_name).parser).unwrap(),
raw_to_string((*variable_and_name).name).unwrap(),
- Type::ref_from_raw(BNNewTypeReference((*variable_and_name).type_)),
+ Type::from_raw((*variable_and_name).type_).to_owned(),
)
})
.collect();
@@ -754,11 +754,8 @@ impl DebugInfo {
new_type: &Type,
components: &[&str],
) -> bool {
- let mut components_array: Vec<*const ::std::os::raw::c_char> =
- Vec::with_capacity(components.len());
- for component in components {
- components_array.push(component.as_ptr() as _);
- }
+ // SAFETY: Lifetime of `components` will live long enough, so passing as_ptr is safe.
+ let raw_components: Vec<_> = components.iter().map(|&c| c.as_ptr()).collect();
let name = name.into_bytes_with_nul();
unsafe {
@@ -766,7 +763,7 @@ impl DebugInfo {
self.handle,
name.as_ref().as_ptr() as *mut _,
new_type.handle,
- components_array.as_ptr() as _,
+ raw_components.as_ptr() as *mut _,
components.len(),
)
}
@@ -777,38 +774,39 @@ 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(std::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(std::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(std::ptr::null_mut() as *mut _, |name| name.as_ptr() as _);
let mut components_array: Vec<*mut ::std::os::raw::c_char> =
Vec::with_capacity(new_func.components.len());
-
let mut local_variables_array: Vec<BNVariableNameAndType> =
Vec::with_capacity(new_func.local_variables.len());
unsafe {
for component in &new_func.components {
- components_array.push(BNAllocString(component.clone().into_bytes_with_nul().as_ptr() as _));
+ 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,
- }
- );
+ local_variables_array.push(BNVariableNameAndType {
+ var: local_variable.variable.into(),
+ 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(
@@ -820,11 +818,11 @@ impl DebugInfo {
address: new_func.address,
type_: match new_func.type_ {
Some(type_) => type_.handle,
- _ => ptr::null_mut(),
+ _ => std::ptr::null_mut(),
},
platform: match new_func.platform {
Some(platform) => platform.handle,
- _ => ptr::null_mut(),
+ _ => std::ptr::null_mut(),
},
components: components_array.as_ptr() as _,
componentN: new_func.components.len(),
@@ -877,7 +875,7 @@ impl DebugInfo {
self.handle,
address,
t.handle,
- ptr::null_mut(),
+ std::ptr::null_mut(),
components.as_ptr() as _,
components.len(),
)
@@ -885,20 +883,11 @@ impl DebugInfo {
}
}
- pub fn add_data_variable_info<S: BnStrCompatible>(&self, var: DataVariableAndName<S>) -> bool {
- let name = var.name.into_bytes_with_nul();
- unsafe {
- BNAddDebugDataVariableInfo(
- self.handle,
- &BNDataVariableAndName {
- address: var.address,
- type_: var.t.contents.handle,
- name: name.as_ref().as_ptr() as *mut _,
- autoDiscovered: var.auto_discovered,
- typeConfidence: var.t.confidence,
- },
- )
- }
+ pub fn add_data_variable_info(&self, var: NamedDataVariableWithType) -> bool {
+ let raw_data_var = NamedDataVariableWithType::into_raw(var);
+ let success = unsafe { BNAddDebugDataVariableInfo(self.handle, &raw_data_var) };
+ NamedDataVariableWithType::free_raw(raw_data_var);
+ success
}
}
@@ -921,18 +910,3 @@ impl ToOwned for DebugInfo {
unsafe { RefCountable::inc_ref(self) }
}
}
-
-////////////////////////////
-// CustomDebugInfoParser
-
-/// Implement this trait to implement a debug info parser. See `DebugInfoParser` for more details.
-pub trait CustomDebugInfoParser: 'static + Sync {
- fn is_valid(&self, view: &BinaryView) -> bool;
- fn parse_info(
- &self,
- debug_info: &mut DebugInfo,
- view: &BinaryView,
- debug_file: &BinaryView,
- progress: Box<dyn Fn(usize, usize) -> Result<(), ()>>,
- ) -> bool;
-}
diff --git a/rust/src/demangle.rs b/rust/src/demangle.rs
index 8fbc4316..6e303fac 100644
--- a/rust/src/demangle.rs
+++ b/rust/src/demangle.rs
@@ -15,84 +15,53 @@
//! Interfaces for demangling and simplifying mangled names in binaries.
use binaryninjacore_sys::*;
-use std::os::raw::c_char;
-use std::{ffi::CStr, result};
-use std::ffi::c_void;
+use std::ffi::{c_char, c_void};
use crate::architecture::CoreArchitecture;
-use crate::binaryview::BinaryView;
-use crate::string::{BnStrCompatible, BnString, raw_to_string};
+use crate::binary_view::BinaryView;
+use crate::string::{raw_to_string, BnStrCompatible, BnString};
use crate::types::{QualifiedName, Type};
use crate::rc::*;
-pub type Result<R> = result::Result<R, ()>;
+pub type Result<R> = std::result::Result<R, ()>;
pub fn demangle_generic<S: BnStrCompatible>(
arch: &CoreArchitecture,
mangled_name: S,
view: Option<&BinaryView>,
simplify: bool,
-) -> Result<(Option<Ref<Type>>, Vec<String>)> {
+) -> Option<(QualifiedName, Option<Ref<Type>>)> {
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 = std::ptr::null_mut();
- let mut out_name = BNQualifiedName {
- name: std::ptr::null_mut(),
- join: std::ptr::null_mut(),
- nameCount: 0,
- };
- let view_ptr = match view {
- Some(v) => v.handle,
- None => std::ptr::null_mut(),
- };
+ let mut out_name = BNQualifiedName::default();
let res = unsafe {
BNDemangleGeneric(
- arch.0,
+ arch.handle,
mangled_name_ptr.as_ptr() as *const c_char,
&mut out_type,
&mut out_name,
- view_ptr,
+ view.map(|v| v.handle).unwrap_or(std::ptr::null_mut()),
simplify,
)
};
- if !res {
- let cstr = match CStr::from_bytes_with_nul(mangled_name_ptr) {
- Ok(cstr) => cstr,
- Err(_) => {
- log::error!("demangle_generic: failed to parse mangled name");
- return Err(());
- }
+ if res {
+ let out_type = match out_type.is_null() {
+ true => None,
+ false => Some(unsafe { Type::ref_from_raw(out_type) }),
};
- return Ok((None, vec![cstr.to_string_lossy().into_owned()]));
+ Some((QualifiedName::from_owned_raw(out_name), out_type))
+ } else {
+ None
}
-
- let out_type = match out_type.is_null() {
- true => {
- log::debug!("demangle_generic: out_type is NULL");
- None
- }
- false => Some(unsafe { Type::ref_from_raw(out_type) }),
- };
-
- Ok((
- out_type,
- QualifiedName(out_name)
- .strings()
- .iter()
- .map(|str| str.to_string())
- .collect::<Vec<_>>()
- ))
}
-pub fn demangle_llvm<S: BnStrCompatible>(
- mangled_name: S,
- simplify: bool,
-) -> Result<Vec<String>> {
+pub fn demangle_llvm<S: BnStrCompatible>(mangled_name: S, simplify: bool) -> Option<QualifiedName> {
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_name: *mut *mut std::os::raw::c_char = std::ptr::null_mut();
let mut out_size: usize = 0;
let res = unsafe {
BNDemangleLLVM(
@@ -103,37 +72,26 @@ pub fn demangle_llvm<S: BnStrCompatible>(
)
};
- 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()]);
- }
+ match res {
+ true => {
+ assert!(!out_name.is_null());
+ let names: Vec<_> = unsafe { ArrayGuard::<BnString>::new(out_name, out_size, ()) }
+ .iter()
+ .map(str::to_string)
+ .collect();
+ unsafe { BNFreeDemangledName(&mut out_name, out_size) };
- if out_name.is_null() {
- log::error!("demangle_llvm: out_name is NULL");
- return Err(());
+ Some(names.into())
+ }
+ false => None,
}
-
- 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,
simplify: bool,
-) -> Result<(Option<Ref<Type>>, Vec<String>)> {
+) -> Option<(QualifiedName, Option<Ref<Type>>)> {
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 = std::ptr::null_mut();
@@ -141,7 +99,7 @@ pub fn demangle_gnu3<S: BnStrCompatible>(
let mut out_size: usize = 0;
let res = unsafe {
BNDemangleGNU3(
- arch.0,
+ arch.handle,
mangled_name_ptr.as_ptr() as *const c_char,
&mut out_type,
&mut out_name,
@@ -150,45 +108,31 @@ pub fn demangle_gnu3<S: BnStrCompatible>(
)
};
- if !res || out_size == 0 {
- let cstr = match CStr::from_bytes_with_nul(mangled_name_ptr) {
- Ok(cstr) => cstr,
- Err(_) => {
- log::error!("demangle_gnu3: failed to parse mangled name");
- return Err(());
- }
- };
- return Ok((None, vec![cstr.to_string_lossy().into_owned()]));
- }
-
- let out_type = match out_type.is_null() {
+ match res {
true => {
- log::debug!("demangle_gnu3: out_type is NULL");
- None
- }
- false => Some(unsafe { Type::ref_from_raw(out_type) }),
- };
-
- if out_name.is_null() {
- log::error!("demangle_gnu3: out_name is NULL");
- return Err(());
- }
-
- let names = unsafe { ArrayGuard::<BnString>::new(out_name, out_size, ()) }
- .iter()
- .map(str::to_string)
- .collect();
+ assert!(!out_name.is_null());
+ let names: Vec<_> = unsafe { ArrayGuard::<BnString>::new(out_name, out_size, ()) }
+ .iter()
+ .map(str::to_string)
+ .collect();
+ unsafe { BNFreeDemangledName(&mut out_name, out_size) };
- unsafe { BNFreeDemangledName(&mut out_name, out_size) };
+ let out_type = match out_type.is_null() {
+ true => None,
+ false => Some(unsafe { Type::ref_from_raw(out_type) }),
+ };
- Ok((out_type, names))
+ Some((names.into(), out_type))
+ }
+ false => None,
+ }
}
pub fn demangle_ms<S: BnStrCompatible>(
arch: &CoreArchitecture,
mangled_name: S,
simplify: bool,
-) -> Result<(Option<Ref<Type>>, Vec<String>)> {
+) -> Option<(QualifiedName, Option<Ref<Type>>)> {
let mangled_name_bwn = mangled_name.into_bytes_with_nul();
let mangled_name_ptr = mangled_name_bwn.as_ref();
@@ -197,7 +141,7 @@ pub fn demangle_ms<S: BnStrCompatible>(
let mut out_size: usize = 0;
let res = unsafe {
BNDemangleMS(
- arch.0,
+ arch.handle,
mangled_name_ptr.as_ptr() as *const c_char,
&mut out_type,
&mut out_name,
@@ -206,38 +150,24 @@ pub fn demangle_ms<S: BnStrCompatible>(
)
};
- if !res || out_size == 0 {
- let cstr = match CStr::from_bytes_with_nul(mangled_name_ptr) {
- Ok(cstr) => cstr,
- Err(_) => {
- log::error!("demangle_ms: failed to parse mangled name");
- return Err(());
- }
- };
- return Ok((None, vec![cstr.to_string_lossy().into_owned()]));
- }
-
- let out_type = match out_type.is_null() {
+ match res {
true => {
- log::debug!("demangle_ms: out_type is NULL");
- None
- }
- false => Some(unsafe { Type::ref_from_raw(out_type) }),
- };
-
- if out_name.is_null() {
- log::error!("demangle_ms: out_name is NULL");
- return Err(());
- }
+ assert!(!out_name.is_null());
+ let names: Vec<_> = unsafe { ArrayGuard::<BnString>::new(out_name, out_size, ()) }
+ .iter()
+ .map(str::to_string)
+ .collect();
+ unsafe { BNFreeDemangledName(&mut out_name, out_size) };
- let names = unsafe { ArrayGuard::<BnString>::new(out_name, out_size, ()) }
- .iter()
- .map(|name| name.to_string())
- .collect();
-
- unsafe { BNFreeDemangledName(&mut out_name, out_size) };
+ let out_type = match out_type.is_null() {
+ true => None,
+ false => Some(unsafe { Type::ref_from_raw(out_type) }),
+ };
- Ok((out_type, names))
+ Some((names.into(), out_type))
+ }
+ false => None,
+ }
}
#[derive(PartialEq, Eq, Hash)]
@@ -248,10 +178,15 @@ pub struct Demangler {
impl Demangler {
pub(crate) unsafe fn from_raw(handle: *mut BNDemangler) -> Self {
debug_assert!(!handle.is_null());
-
Self { handle }
}
+ pub fn list() -> Array<Self> {
+ let mut count: usize = 0;
+ let demanglers = unsafe { BNGetDemanglerList(&mut count) };
+ unsafe { Array::<Demangler>::new(demanglers, count, ()) }
+ }
+
pub fn is_mangled_string<S: BnStrCompatible>(&self, name: S) -> bool {
let bytes = name.into_bytes_with_nul();
unsafe { BNIsDemanglerMangledName(self.handle, bytes.as_ref().as_ptr() as *const _) }
@@ -262,34 +197,39 @@ impl Demangler {
arch: &CoreArchitecture,
name: S,
view: Option<&BinaryView>,
- ) -> Result<(Option<Ref<Type>>, QualifiedName)> {
+ ) -> Option<(QualifiedName, Option<Ref<Type>>)> {
let name_bytes = name.into_bytes_with_nul();
let mut out_type = std::ptr::null_mut();
- let mut out_var_name = BNQualifiedName {
- name: std::ptr::null_mut(),
- join: std::ptr::null_mut(),
- nameCount: 0,
- };
+ let mut out_var_name = BNQualifiedName::default();
let view_ptr = match view {
Some(v) => v.handle,
- None => std::ptr::null_mut()
+ None => std::ptr::null_mut(),
};
- if !unsafe { BNDemanglerDemangle(self.handle, arch.0, name_bytes.as_ref().as_ptr() as *const _, &mut out_type, &mut out_var_name, view_ptr) } {
- return Err(());
- }
+ let res = unsafe {
+ BNDemanglerDemangle(
+ self.handle,
+ arch.handle,
+ name_bytes.as_ref().as_ptr() as *const _,
+ &mut out_type,
+ &mut out_var_name,
+ view_ptr,
+ )
+ };
- let var_type =
- if out_type.is_null() {
- None
- } else {
- Some(unsafe { Type::ref_from_raw(out_type) })
- };
- let var_name = QualifiedName(out_var_name);
+ match res {
+ true => {
+ let var_type = match out_type.is_null() {
+ true => None,
+ false => Some(unsafe { Type::ref_from_raw(out_type) }),
+ };
- Ok((var_type, var_name))
+ Some((QualifiedName::from_owned_raw(out_var_name), var_type))
+ }
+ false => None,
+ }
}
pub fn name(&self) -> BnString {
@@ -306,13 +246,7 @@ impl Demangler {
}
}
- pub fn list() -> Array<Self> {
- let mut count: usize = 0;
- let demanglers = unsafe { BNGetDemanglerList(&mut count) };
- unsafe { Array::<Demangler>::new(demanglers, count, ()) }
- }
-
- pub fn register<S, C>(name: S, callbacks: C) -> Self
+ pub fn register<S, C>(name: S, demangler: C) -> Self
where
S: BnStrCompatible,
C: CustomDemangler,
@@ -323,71 +257,66 @@ impl Demangler {
{
ffi_wrap!("CustomDemangler::cb_is_mangled_string", unsafe {
let cmd = &*(ctxt as *const C);
- let name =
- if let Some(n) = raw_to_string(name) {
- n
- } else {
- return false;
- };
+ let Some(name) = raw_to_string(name) else {
+ return false;
+ };
cmd.is_mangled_string(&name)
})
}
- extern "C" fn cb_demangle<C>(ctxt: *mut c_void, arch: *mut BNArchitecture, name: *const c_char, out_type: *mut *mut BNType, out_var_name: *mut BNQualifiedName, view: *mut BNBinaryView) -> bool
+ extern "C" fn cb_demangle<C>(
+ ctxt: *mut c_void,
+ arch: *mut BNArchitecture,
+ name: *const c_char,
+ out_type: *mut *mut BNType,
+ out_var_name: *mut BNQualifiedName,
+ view: *mut BNBinaryView,
+ ) -> bool
where
C: CustomDemangler,
{
ffi_wrap!("CustomDemangler::cb_demangle", unsafe {
let cmd = &*(ctxt as *const C);
let arch = CoreArchitecture::from_raw(arch);
- let name =
- if let Some(n) = raw_to_string(name) {
- n
- } else {
- return false;
- };
- let view = if view.is_null() {
- None
- } else {
- Some(BinaryView::from_raw(BNNewViewReference(view)))
+ let Some(name) = raw_to_string(name) else {
+ return false;
+ };
+ let view = match view.is_null() {
+ false => Some(BinaryView::from_raw(view).to_owned()),
+ true => None,
};
match cmd.demangle(&arch, &name, view) {
- Ok((type_, name)) => {
- *out_type = match type_ {
- Some(t) => RefCountable::inc_ref(t.as_ref()).handle,
- None => std::ptr::null_mut()
+ Some((name, ty)) => {
+ // NOTE: Leaked to the caller, who must pick the ref up.
+ *out_type = match ty {
+ Some(t) => Ref::into_raw(t).handle,
+ None => std::ptr::null_mut(),
};
- // TODO: Need to have a better way for api-owned QNames
- (*out_var_name).nameCount = name.0.nameCount;
- (*out_var_name).join = BNAllocString(name.0.join);
- (*out_var_name).name = BNAllocStringList(name.0.name as *mut *const _, name.0.nameCount);
+ // NOTE: Leaked to be freed with `cb_free_var_name`.
+ *out_var_name = QualifiedName::into_raw(name);
true
- },
- Err(_) => {
- false
}
+ None => false,
}
})
}
- extern "C" fn cb_free_var_name<C>(_ctxt: *mut c_void, name: *mut BNQualifiedName)
- where
- C: CustomDemangler,
- {
+
+ extern "C" fn cb_free_var_name(_ctxt: *mut c_void, name: *mut BNQualifiedName) {
ffi_wrap!("CustomDemangler::cb_free_var_name", unsafe {
- BNFreeString((*name).join);
- BNFreeStringList((*name).name, (*name).nameCount);
+ // TODO: What is the point of this free callback?
+ QualifiedName::free_raw(*name)
})
}
let name = name.into_bytes_with_nul();
let name_ptr = name.as_ref().as_ptr() as *mut _;
- let ctxt = Box::into_raw(Box::new(callbacks));
+ let ctxt = Box::into_raw(Box::new(demangler));
let callbacks = BNDemanglerCallbacks {
context: ctxt as *mut c_void,
isMangledString: Some(cb_is_mangled_string::<C>),
demangle: Some(cb_demangle::<C>),
- freeVarName: Some(cb_free_var_name::<C>),
+ freeVarName: Some(cb_free_var_name),
};
unsafe {
@@ -399,7 +328,9 @@ impl Demangler {
}
pub fn promote(demangler: &Demangler) {
- unsafe { BNPromoteDemangler(demangler.handle); }
+ unsafe {
+ BNPromoteDemangler(demangler.handle);
+ }
}
}
@@ -431,5 +362,5 @@ pub trait CustomDemangler: 'static + Sync {
arch: &CoreArchitecture,
name: &str,
view: Option<Ref<BinaryView>>,
- ) -> Result<(Option<Ref<Type>>, QualifiedName)>;
+ ) -> Option<(QualifiedName, Option<Ref<Type>>)>;
}
diff --git a/rust/src/disassembly.rs b/rust/src/disassembly.rs
index 5d3da31e..99938e5f 100644
--- a/rust/src/disassembly.rs
+++ b/rust/src/disassembly.rs
@@ -11,467 +11,955 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
-
-// TODO : Combine this with the architecture implementation
+#![allow(unused)]
use binaryninjacore_sys::*;
-use crate::string::BnString;
-use crate::{BN_FULL_CONFIDENCE, BN_INVALID_EXPR};
+use crate::string::{raw_to_string, strings_to_string_list, BnString};
use crate::rc::*;
+use crate::confidence::MAX_CONFIDENCE;
+use crate::function::HighlightColor;
+use crate::tags::Tag;
+use crate::types::Type;
use std::convert::From;
-use std::ffi::CStr;
-use std::mem;
-use std::ptr;
+use std::fmt::{Display, Formatter};
+pub type DisassemblyOption = BNDisassemblyOption;
pub type InstructionTextTokenType = BNInstructionTextTokenType;
-pub type InstructionTextTokenContext = BNInstructionTextTokenContext;
-
-// InstructionTextTokenType's; * = Implemented
-// *TextToken = 0,
-// *InstructionToken = 1,
-// *OperandSeparatorToken = 2,
-// *RegisterToken = 3,
-// *IntegerToken = 4,
-// *PossibleAddressToken = 5,
-// *BeginMemoryOperandToken = 6,
-// *EndMemoryOperandToken = 7,
-// *FloatingPointToken = 8,
-// AnnotationToken = 9,
-// *CodeRelativeAddressToken = 10,
-// ArgumentNameToken = 11,
-// HexDumpByteValueToken = 12,
-// HexDumpSkippedByteToken = 13,
-// HexDumpInvalidByteToken = 14,
-// HexDumpTextToken = 15,
-// OpcodeToken = 16,
-// *StringToken = 17,
-// CharacterConstantToken = 18,
-// *KeywordToken = 19,
-// *TypeNameToken = 20,
-// *FieldNameToken = 21,
-// *NameSpaceToken = 22,
-// NameSpaceSeparatorToken = 23,
-// TagToken = 24,
-// StructOffsetToken = 25,
-// StructOffsetByteValueToken = 26,
-// StructureHexDumpTextToken = 27,
-// *GotoLabelToken = 28,
-// CommentToken = 29,
-// PossibleValueToken = 30,
-// PossibleValueTypeToken = 31,
-// ArrayIndexToken = 32,
-// *IndentationToken = 33,
-// UnknownMemoryToken = 34,
-// CodeSymbolToken = 64,
-// DataSymbolToken = 65,
-// LocalVariableToken = 66,
-// ImportToken = 67,
-// AddressDisplayToken = 68,
-// IndirectImportToken = 69,
-// ExternalSymbolToken = 70,
-
-#[repr(transparent)]
-pub struct InstructionTextToken(pub(crate) BNInstructionTextToken);
+pub type StringType = BNStringType;
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
-pub enum InstructionTextTokenContents {
- Text,
- Instruction,
- OperandSeparator,
- Register,
- Integer(u64), // TODO size?
- PossibleAddress(u64), // TODO size?
- BeginMemoryOperand,
- EndMemoryOperand,
- FloatingPoint,
- CodeRelativeAddress(u64),
- String(u64),
- Keyword,
- TypeName,
- FieldName,
- NameSpace,
- GotoLabel(u64),
- Indentation,
- Brace,
+#[derive(Clone, PartialEq, Debug, Default)]
+pub struct DisassemblyTextLine {
+ pub address: u64,
+ pub instruction_index: usize,
+ pub tokens: Vec<InstructionTextToken>,
+ pub highlight: HighlightColor,
+ pub tags: Vec<Ref<Tag>>,
+ pub type_info: DisassemblyTextLineTypeInfo,
}
-impl InstructionTextToken {
- pub(crate) unsafe fn from_raw(raw: &BNInstructionTextToken) -> &Self {
- mem::transmute(raw)
+impl DisassemblyTextLine {
+ pub(crate) fn from_raw(value: &BNDisassemblyTextLine) -> Self {
+ let raw_tokens = unsafe { std::slice::from_raw_parts(value.tokens, value.count) };
+ let tokens: Vec<_> = raw_tokens
+ .iter()
+ .map(InstructionTextToken::from_raw)
+ .collect();
+ // SAFETY: Increment the tag ref as we are going from ref to owned.
+ let raw_tags = unsafe { std::slice::from_raw_parts(value.tags, value.tagCount) };
+ let tags: Vec<_> = raw_tags
+ .iter()
+ .map(|&t| unsafe { Tag::from_raw(t) }.to_owned())
+ .collect();
+ Self {
+ address: value.addr,
+ instruction_index: value.instrIndex,
+ tokens,
+ highlight: value.highlight.into(),
+ tags,
+ type_info: DisassemblyTextLineTypeInfo::from_raw(&value.typeInfo),
+ }
}
- pub(crate) fn into_raw(self) -> BNInstructionTextToken {
- mem::ManuallyDrop::new(self).0
+ /// Convert into a raw [BNDisassemblyTextLine], use with caution.
+ ///
+ /// NOTE: The allocations here for tokens and tags MUST be freed by rust using [Self::free_raw].
+ pub(crate) fn into_raw(value: Self) -> BNDisassemblyTextLine {
+ // NOTE: The instruction text and type names fields are being leaked here. To be freed with [Self::free_raw].
+ let tokens: Box<[BNInstructionTextToken]> = value
+ .tokens
+ .into_iter()
+ .map(InstructionTextToken::into_raw)
+ .collect();
+ let tags: Box<[*mut BNTag]> = value
+ .tags
+ .into_iter()
+ .map(|t| {
+ // SAFETY: The tags ref will be temporarily incremented here, until [Self::free_raw] is called.
+ // SAFETY: This is so that tags lifetime is long enough, as we might be the last holders of the ref.
+ unsafe { Ref::into_raw(t) }.handle
+ })
+ .collect();
+ BNDisassemblyTextLine {
+ addr: value.address,
+ instrIndex: value.instruction_index,
+ count: tokens.len(),
+ // NOTE: Leaking tokens here to be freed with [Self::free_raw].
+ tokens: Box::leak(tokens).as_mut_ptr(),
+ highlight: value.highlight.into(),
+ tagCount: tags.len(),
+ // NOTE: Leaking tags here to be freed with [Self::free_raw].
+ tags: Box::leak(tags).as_mut_ptr(),
+ typeInfo: DisassemblyTextLineTypeInfo::into_raw(value.type_info),
+ }
}
- pub fn new(text: &str, contents: InstructionTextTokenContents) -> Self {
- let (value, address) = match contents {
- InstructionTextTokenContents::Integer(v) => (v, 0),
- InstructionTextTokenContents::PossibleAddress(v)
- | InstructionTextTokenContents::CodeRelativeAddress(v)
- | InstructionTextTokenContents::GotoLabel(v) => (v, v),
- InstructionTextTokenContents::String(v) => (v, 0),
- _ => (0, 0),
- };
+ /// Frees raw object created with [Self::into_raw], use with caution.
+ ///
+ /// NOTE: The allocations freed MUST have been created in rust using [Self::into_raw].
+ pub(crate) fn free_raw(value: BNDisassemblyTextLine) {
+ // Free the token list
+ let raw_tokens = unsafe { std::slice::from_raw_parts_mut(value.tokens, value.count) };
+ let boxed_tokens = unsafe { Box::from_raw(raw_tokens) };
+ for token in boxed_tokens {
+ // SAFETY: As we have leaked the token contents we need to now free them (text and typeNames).
+ InstructionTextToken::free_raw(token);
+ }
+ // Free the tag list
+ let raw_tags = unsafe { std::slice::from_raw_parts_mut(value.tags, value.tagCount) };
+ let boxed_tags = unsafe { Box::from_raw(raw_tags) };
+ for tag in boxed_tags {
+ // SAFETY: As we have incremented the tags ref in [Self::into_raw] we must now decrement.
+ let _ = unsafe { Tag::ref_from_raw(tag) };
+ }
+ // Free the type info
+ DisassemblyTextLineTypeInfo::free_raw(value.typeInfo);
+ }
- let type_ = match contents {
- InstructionTextTokenContents::Text => InstructionTextTokenType::TextToken,
- InstructionTextTokenContents::Instruction => InstructionTextTokenType::InstructionToken,
- InstructionTextTokenContents::OperandSeparator => {
- InstructionTextTokenType::OperandSeparatorToken
- }
- InstructionTextTokenContents::Register => InstructionTextTokenType::RegisterToken,
- InstructionTextTokenContents::Integer(_) => InstructionTextTokenType::IntegerToken,
- InstructionTextTokenContents::PossibleAddress(_) => {
- InstructionTextTokenType::PossibleAddressToken
- }
- InstructionTextTokenContents::BeginMemoryOperand => {
- InstructionTextTokenType::BeginMemoryOperandToken
- }
- InstructionTextTokenContents::EndMemoryOperand => {
- InstructionTextTokenType::EndMemoryOperandToken
- }
- InstructionTextTokenContents::FloatingPoint => {
- InstructionTextTokenType::FloatingPointToken
- }
- InstructionTextTokenContents::CodeRelativeAddress(_) => {
- InstructionTextTokenType::CodeRelativeAddressToken
- }
- InstructionTextTokenContents::String(_) => InstructionTextTokenType::StringToken,
- InstructionTextTokenContents::Keyword => InstructionTextTokenType::KeywordToken,
- InstructionTextTokenContents::TypeName => InstructionTextTokenType::TypeNameToken,
- InstructionTextTokenContents::FieldName => InstructionTextTokenType::FieldNameToken,
- InstructionTextTokenContents::NameSpace => InstructionTextTokenType::NameSpaceToken,
- InstructionTextTokenContents::GotoLabel(_) => InstructionTextTokenType::GotoLabelToken,
- InstructionTextTokenContents::Indentation => InstructionTextTokenType::IndentationToken,
- InstructionTextTokenContents::Brace => InstructionTextTokenType::BraceToken,
- };
+ pub fn new(tokens: Vec<InstructionTextToken>) -> Self {
+ Self {
+ tokens,
+ ..Default::default()
+ }
+ }
+}
- let width = text.len() as u64;
+impl From<&str> for DisassemblyTextLine {
+ fn from(value: &str) -> Self {
+ Self::new(vec![InstructionTextToken::new(
+ value,
+ InstructionTextTokenKind::Text,
+ )])
+ }
+}
- InstructionTextToken(BNInstructionTextToken {
- type_,
- text: BnString::new(text).into_raw(),
+impl From<String> for DisassemblyTextLine {
+ fn from(value: String) -> Self {
+ Self::new(vec![InstructionTextToken::new(
value,
- width,
- size: 0,
- operand: 0xffff_ffff,
- context: InstructionTextTokenContext::NoTokenContext,
- confidence: BN_FULL_CONFIDENCE,
- address,
- typeNames: ptr::null_mut(),
- namesCount: 0,
- exprIndex: BN_INVALID_EXPR,
- })
+ InstructionTextTokenKind::Text,
+ )])
}
+}
- pub fn set_value(&mut self, value: u64) {
- self.0.value = value;
+impl Display for DisassemblyTextLine {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ for token in &self.tokens {
+ write!(f, "{}", token)?;
+ }
+ Ok(())
}
+}
- pub fn set_context(&mut self, context: InstructionTextTokenContext) {
- self.0.context = context;
+impl CoreArrayProvider for DisassemblyTextLine {
+ type Raw = BNDisassemblyTextLine;
+ type Context = ();
+ type Wrapped<'a> = Self;
+}
+
+unsafe impl CoreArrayProviderInner for DisassemblyTextLine {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeDisassemblyTextLines(raw, count)
}
- pub fn text(&self) -> &str {
- unsafe { CStr::from_ptr(self.0.text) }.to_str().unwrap()
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ Self::from_raw(raw)
}
+}
- pub fn contents(&self) -> InstructionTextTokenContents {
- use self::BNInstructionTextTokenType::*;
- use self::InstructionTextTokenContents::*;
+#[derive(Default, Clone, PartialEq, Eq, Debug, Hash)]
+pub struct DisassemblyTextLineTypeInfo {
+ pub has_type_info: bool,
+ pub parent_type: Option<Ref<Type>>,
+ pub field_index: usize,
+ pub offset: u64,
+}
- match self.0.type_ {
- TextToken => Text,
- InstructionToken => Instruction,
- OperandSeparatorToken => OperandSeparator,
- RegisterToken => Register,
- IntegerToken => Integer(self.0.value),
- PossibleAddressToken => PossibleAddress(self.0.value),
- BeginMemoryOperandToken => BeginMemoryOperand,
- EndMemoryOperandToken => EndMemoryOperand,
- FloatingPointToken => FloatingPoint,
- CodeRelativeAddressToken => CodeRelativeAddress(self.0.value),
- _ => unimplemented!("woops"),
+impl DisassemblyTextLineTypeInfo {
+ pub(crate) fn from_raw(value: &BNDisassemblyTextLineTypeInfo) -> Self {
+ Self {
+ has_type_info: value.hasTypeInfo,
+ parent_type: match value.parentType.is_null() {
+ false => Some(unsafe { Type::from_raw(value.parentType).to_owned() }),
+ true => None,
+ },
+ field_index: value.fieldIndex,
+ offset: value.offset,
}
}
- pub fn context(&self) -> InstructionTextTokenContext {
- self.0.context
+ pub(crate) fn from_owned_raw(value: BNDisassemblyTextLineTypeInfo) -> Self {
+ Self {
+ has_type_info: value.hasTypeInfo,
+ parent_type: match value.parentType.is_null() {
+ false => Some(unsafe { Type::ref_from_raw(value.parentType) }),
+ true => None,
+ },
+ field_index: value.fieldIndex,
+ offset: value.offset,
+ }
}
- pub fn size(&self) -> usize {
- self.0.size
+ pub(crate) fn into_raw(value: Self) -> BNDisassemblyTextLineTypeInfo {
+ BNDisassemblyTextLineTypeInfo {
+ hasTypeInfo: value.has_type_info,
+ parentType: value
+ .parent_type
+ .map(|t| unsafe { Ref::into_raw(t) }.handle)
+ .unwrap_or(std::ptr::null_mut()),
+ fieldIndex: value.field_index,
+ offset: value.offset,
+ }
}
- pub fn operand(&self) -> usize {
- self.0.operand
+ pub(crate) fn into_owned_raw(value: &Self) -> BNDisassemblyTextLineTypeInfo {
+ BNDisassemblyTextLineTypeInfo {
+ hasTypeInfo: value.has_type_info,
+ parentType: value
+ .parent_type
+ .as_ref()
+ .map(|t| t.handle)
+ .unwrap_or(std::ptr::null_mut()),
+ fieldIndex: value.field_index,
+ offset: value.offset,
+ }
}
- pub fn address(&self) -> u64 {
- self.0.address
+ pub(crate) fn free_raw(value: BNDisassemblyTextLineTypeInfo) {
+ if !value.parentType.is_null() {
+ let _ = unsafe { Type::ref_from_raw(value.parentType) };
+ }
}
+}
- pub fn expr_index(&self) -> usize {
- self.0.exprIndex
- }
+#[derive(Debug, Clone, PartialEq)]
+pub struct InstructionTextToken {
+ pub address: u64,
+ pub text: String,
+ pub confidence: u8,
+ pub context: InstructionTextTokenContext,
+ // TODO: Document that this is not necessary to set and that this is valid in a limited context.
+ pub expr_index: usize,
+ pub kind: InstructionTextTokenKind,
}
-impl Default for InstructionTextToken {
- fn default() -> Self {
- InstructionTextToken(BNInstructionTextToken {
- type_: InstructionTextTokenType::TextToken,
- text: ptr::null_mut(),
- value: 0,
- width: 0,
- size: 0,
- operand: 0,
- context: InstructionTextTokenContext::NoTokenContext,
- confidence: BN_FULL_CONFIDENCE,
- address: 0,
- typeNames: ptr::null_mut(),
- namesCount: 0,
- exprIndex: BN_INVALID_EXPR,
- })
+impl InstructionTextToken {
+ pub(crate) fn from_raw(value: &BNInstructionTextToken) -> Self {
+ Self {
+ address: value.address,
+ text: raw_to_string(value.text).unwrap(),
+ confidence: value.confidence,
+ context: value.context.into(),
+ expr_index: value.exprIndex,
+ kind: InstructionTextTokenKind::from_raw(value),
+ }
}
-}
-impl Clone for InstructionTextToken {
- fn clone(&self) -> Self {
- InstructionTextToken(BNInstructionTextToken {
- type_: self.0.type_,
- context: self.0.context,
- address: self.0.address,
- size: self.0.size,
- operand: self.0.operand,
- value: self.0.value,
+ pub(crate) fn into_raw(value: Self) -> BNInstructionTextToken {
+ let bn_text = BnString::new(value.text);
+ // These can be gathered from value.kind
+ let kind_value = value.kind.try_value().unwrap_or(0);
+ let operand = value.kind.try_operand().unwrap_or(0);
+ let size = value.kind.try_size().unwrap_or(0);
+ let type_names = value.kind.try_type_names().unwrap_or_default();
+ BNInstructionTextToken {
+ type_: value.kind.into(),
+ // NOTE: Expected to be freed with `InstructionTextToken::free_raw`.
+ text: BnString::into_raw(bn_text),
+ value: kind_value,
+ // TODO: Where is this even used?
width: 0,
- text: BnString::new(self.text()).into_raw(),
- confidence: 0xff,
- typeNames: ptr::null_mut(),
- namesCount: 0,
- exprIndex: self.0.exprIndex,
- })
+ size,
+ operand,
+ context: value.context.into(),
+ confidence: value.confidence,
+ address: value.address,
+ // NOTE: Expected to be freed with `InstructionTextToken::free_raw`.
+ typeNames: strings_to_string_list(&type_names),
+ namesCount: type_names.len(),
+ exprIndex: value.expr_index,
+ }
}
-}
-impl Drop for InstructionTextToken {
- fn drop(&mut self) {
- if !self.0.text.is_null() {
- let _owned = unsafe { BnString::from_raw(self.0.text) };
+ pub(crate) fn free_raw(value: BNInstructionTextToken) {
+ if !value.text.is_null() {
+ unsafe { BNFreeString(value.text) };
+ }
+ if !value.typeNames.is_null() {
+ unsafe { BNFreeStringList(value.typeNames, value.namesCount) };
}
- if !self.0.typeNames.is_null() && self.0.namesCount != 0 {
- unsafe { BNFreeStringList(self.0.typeNames, self.0.namesCount) }
+ }
+
+ pub fn new(text: impl Into<String>, kind: InstructionTextTokenKind) -> Self {
+ Self {
+ address: 0,
+ text: text.into(),
+ confidence: MAX_CONFIDENCE,
+ context: InstructionTextTokenContext::Normal,
+ expr_index: 0,
+ kind,
+ }
+ }
+
+ pub fn new_with_address(
+ address: u64,
+ text: impl Into<String>,
+ kind: InstructionTextTokenKind,
+ ) -> Self {
+ Self {
+ address,
+ text: text.into(),
+ confidence: MAX_CONFIDENCE,
+ context: InstructionTextTokenContext::Normal,
+ expr_index: 0,
+ kind,
}
}
}
+impl Display for InstructionTextToken {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ self.text.fmt(f)
+ }
+}
+
impl CoreArrayProvider for InstructionTextToken {
type Raw = BNInstructionTextToken;
type Context = ();
- type Wrapped<'a> = &'a Self;
+ type Wrapped<'a> = Self;
}
+
unsafe impl CoreArrayProviderInner for InstructionTextToken {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ // SAFETY: The Array MUST have been allocated on the core side. This will `delete[] raw`.
BNFreeInstructionText(raw, count)
}
+
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
- core::mem::transmute(raw)
+ Self::from_raw(raw)
}
}
impl CoreArrayProvider for Array<InstructionTextToken> {
type Raw = BNInstructionTextLine;
type Context = ();
- type Wrapped<'a> = mem::ManuallyDrop<Self>;
+ type Wrapped<'a> = std::mem::ManuallyDrop<Self>;
}
+
unsafe impl CoreArrayProviderInner for Array<InstructionTextToken> {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ // SAFETY: The Array MUST have been allocated on the core side. This will `delete[] raw`.
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, ()))
+ // TODO: This is insane.
+ std::mem::ManuallyDrop::new(Self::new(raw.tokens, raw.count, ()))
}
}
-#[repr(transparent)]
-pub struct DisassemblyTextLine(pub(crate) BNDisassemblyTextLine);
-
-impl DisassemblyTextLine {
- // TODO : this should probably be removed, though it doesn't actually hurt anything
- pub fn debug_print(&self) {
- println!("{}", self);
- }
-
- pub fn addr(&self) -> u64 {
- self.0.addr
- }
-
- pub fn instr_idx(&self) -> usize {
- self.0.instrIndex
- }
+#[derive(Clone, PartialEq, Debug)]
+pub enum InstructionTextTokenKind {
+ Text,
+ Instruction,
+ OperandSeparator,
+ Register,
+ Integer {
+ value: u64,
+ /// Size of the integer
+ size: Option<usize>,
+ },
+ PossibleAddress {
+ value: u64,
+ /// Size of the address
+ size: Option<usize>,
+ },
+ BeginMemoryOperand,
+ EndMemoryOperand,
+ FloatingPoint {
+ value: f64,
+ /// Size of the floating point
+ size: Option<usize>,
+ },
+ Annotation,
+ CodeRelativeAddress {
+ value: u64,
+ size: Option<usize>,
+ },
+ ArgumentName {
+ // TODO: The argument index?
+ value: u64,
+ },
+ HexDumpByteValue {
+ value: u8,
+ },
+ HexDumpSkippedByte,
+ HexDumpInvalidByte,
+ HexDumpText {
+ // TODO: Explain what this does
+ width: u64,
+ },
+ Opcode,
+ String {
+ ty: StringType,
+ },
+ CharacterConstant,
+ Keyword,
+ TypeName,
+ FieldName {
+ /// Offset to this field in the respective structure
+ offset: u64,
+ /// Stores the type names for the referenced field name.
+ ///
+ /// This is typically just the members name.
+ /// For example MyStructure.my_field will have type_names be \["my_field"\].
+ type_names: Vec<String>,
+ },
+ NameSpace,
+ NameSpaceSeparator,
+ Tag,
+ StructOffset {
+ /// Offset to this field in the respective structure
+ offset: u64,
+ // TODO: This makes no sense for struct offset, they dont have types?
+ /// Stores the type names for the referenced field name.
+ type_names: Vec<String>,
+ },
+ // TODO: Unused?
+ StructOffsetByteValue,
+ // TODO: Unused?
+ StructureHexDumpText {
+ // TODO: Explain what this does
+ width: u64,
+ },
+ GotoLabel {
+ target: u64,
+ },
+ Comment {
+ target: u64,
+ },
+ PossibleValue {
+ value: u64,
+ },
+ // TODO: This is weird, you pass the value type as the text, we should restrict this behavior and type it
+ PossibleValueType,
+ ArrayIndex {
+ index: u64,
+ },
+ Indentation,
+ UnknownMemory,
+ EnumerationMember {
+ value: u64,
+ // TODO: Document where this type id comes from
+ // TODO: Can we type this to something other than a string?
+ /// The enumerations type id
+ type_id: Option<String>,
+ },
+ /// Operations like +, -, %
+ Operation,
+ BaseStructureName,
+ BaseStructureSeparator,
+ Brace {
+ // TODO: Explain what this is
+ hash: Option<u64>,
+ },
+ CodeSymbol {
+ // TODO: Value of what?
+ value: u64,
+ // TODO: Size of what?
+ size: usize, // TODO: Operand?
+ },
+ DataSymbol {
+ // TODO: Value of what?
+ value: u64,
+ // TODO: Size of what?
+ size: usize, // TODO: Operand?
+ },
+ LocalVariable {
+ // This comes from the token.value
+ // TODO: Do we have a variable id type we can attach to this?
+ // TODO: Probably not considering this is used at multiple IL levels.
+ variable_id: u64,
+ /// NOTE: This is only valid in SSA form
+ ssa_version: usize,
+ },
+ Import {
+ // TODO: Looks to be the target address from the import.
+ target: u64,
+ },
+ AddressDisplay {
+ address: u64,
+ },
+ // TODO: BAD
+ IndirectImport {
+ /// The address of the import
+ ///
+ /// If you want the address of the import token use [`InstructionTextToken::address`] instead.
+ target: u64,
+ /// Size of the instruction this token is apart of
+ size: usize,
+ // TODO: Type this
+ source_operand: usize,
+ },
+ ExternalSymbol {
+ // TODO: Value of what?
+ value: u64,
+ },
+ StackVariable {
+ // TODO: Do we have a variable id type we can attach to this?
+ // TODO: Probably not considering this is used at multiple IL levels.
+ variable_id: u64,
+ },
+ AddressSeparator,
+ CollapsedInformation,
+ CollapseStateIndicator {
+ // TODO: Explain what this is
+ hash: Option<u64>,
+ },
+}
- pub fn count(&self) -> usize {
- self.0.count
+impl InstructionTextTokenKind {
+ pub(crate) fn from_raw(value: &BNInstructionTextToken) -> Self {
+ match value.type_ {
+ BNInstructionTextTokenType::TextToken => Self::Text,
+ BNInstructionTextTokenType::InstructionToken => Self::Instruction,
+ BNInstructionTextTokenType::OperandSeparatorToken => Self::OperandSeparator,
+ BNInstructionTextTokenType::RegisterToken => Self::Register,
+ BNInstructionTextTokenType::IntegerToken => Self::Integer {
+ value: value.value,
+ size: match value.size {
+ 0 => None,
+ size => Some(size),
+ },
+ },
+ BNInstructionTextTokenType::PossibleAddressToken => Self::PossibleAddress {
+ value: value.value,
+ size: match value.size {
+ 0 => None,
+ size => Some(size),
+ },
+ },
+ BNInstructionTextTokenType::BeginMemoryOperandToken => Self::BeginMemoryOperand,
+ BNInstructionTextTokenType::EndMemoryOperandToken => Self::EndMemoryOperand,
+ BNInstructionTextTokenType::FloatingPointToken => Self::FloatingPoint {
+ value: value.value as f64,
+ size: match value.size {
+ 0 => None,
+ size => Some(size),
+ },
+ },
+ BNInstructionTextTokenType::AnnotationToken => Self::Annotation,
+ BNInstructionTextTokenType::CodeRelativeAddressToken => Self::CodeRelativeAddress {
+ value: value.value,
+ size: match value.size {
+ 0 => None,
+ size => Some(size),
+ },
+ },
+ BNInstructionTextTokenType::ArgumentNameToken => {
+ Self::ArgumentName { value: value.value }
+ }
+ BNInstructionTextTokenType::HexDumpByteValueToken => Self::HexDumpByteValue {
+ value: value.value as u8,
+ },
+ BNInstructionTextTokenType::HexDumpSkippedByteToken => Self::HexDumpSkippedByte,
+ BNInstructionTextTokenType::HexDumpInvalidByteToken => Self::HexDumpInvalidByte,
+ BNInstructionTextTokenType::HexDumpTextToken => {
+ Self::HexDumpText { width: value.value }
+ }
+ BNInstructionTextTokenType::OpcodeToken => Self::Opcode,
+ BNInstructionTextTokenType::StringToken => Self::String {
+ ty: match value.value {
+ 0 => StringType::AsciiString,
+ 1 => StringType::Utf8String,
+ 2 => StringType::Utf16String,
+ 3 => StringType::Utf32String,
+ _ => unreachable!(),
+ },
+ },
+ BNInstructionTextTokenType::CharacterConstantToken => Self::CharacterConstant,
+ BNInstructionTextTokenType::KeywordToken => Self::Keyword,
+ BNInstructionTextTokenType::TypeNameToken => Self::TypeName,
+ BNInstructionTextTokenType::FieldNameToken => Self::FieldName {
+ offset: value.value,
+ type_names: {
+ // NOTE: Do not need to free, this is a part of the From<&> impl
+ let raw_names =
+ unsafe { std::slice::from_raw_parts(value.typeNames, value.namesCount) };
+ raw_names.iter().filter_map(|&r| raw_to_string(r)).collect()
+ },
+ },
+ BNInstructionTextTokenType::NameSpaceToken => Self::NameSpace,
+ BNInstructionTextTokenType::NameSpaceSeparatorToken => Self::NameSpaceSeparator,
+ BNInstructionTextTokenType::TagToken => Self::Tag,
+ BNInstructionTextTokenType::StructOffsetToken => Self::StructOffset {
+ offset: value.value,
+ type_names: {
+ // NOTE: Do not need to free, this is a part of the From<&> impl
+ let raw_names =
+ unsafe { std::slice::from_raw_parts(value.typeNames, value.namesCount) };
+ raw_names.iter().filter_map(|&r| raw_to_string(r)).collect()
+ },
+ },
+ BNInstructionTextTokenType::StructOffsetByteValueToken => Self::StructOffsetByteValue,
+ BNInstructionTextTokenType::StructureHexDumpTextToken => {
+ Self::StructureHexDumpText { width: value.value }
+ }
+ BNInstructionTextTokenType::GotoLabelToken => Self::GotoLabel {
+ target: value.value,
+ },
+ BNInstructionTextTokenType::CommentToken => Self::Comment {
+ target: value.value,
+ },
+ BNInstructionTextTokenType::PossibleValueToken => {
+ Self::PossibleValue { value: value.value }
+ }
+ // NOTE: See my comment about this type in [`Self::PossibleValueType`]
+ BNInstructionTextTokenType::PossibleValueTypeToken => Self::PossibleValueType,
+ BNInstructionTextTokenType::ArrayIndexToken => Self::ArrayIndex { index: value.value },
+ BNInstructionTextTokenType::IndentationToken => Self::Indentation,
+ BNInstructionTextTokenType::UnknownMemoryToken => Self::UnknownMemory,
+ BNInstructionTextTokenType::EnumerationMemberToken => Self::EnumerationMember {
+ value: value.value,
+ type_id: {
+ // NOTE: Type id comes from value.typeNames, it should be the first one (hence the .next)
+ // NOTE: Do not need to free, this is a part of the From<&> impl
+ let raw_names =
+ unsafe { std::slice::from_raw_parts(value.typeNames, value.namesCount) };
+ raw_names.iter().filter_map(|&r| raw_to_string(r)).next()
+ },
+ },
+ BNInstructionTextTokenType::OperationToken => Self::Operation,
+ BNInstructionTextTokenType::BaseStructureNameToken => Self::BaseStructureName,
+ BNInstructionTextTokenType::BaseStructureSeparatorToken => Self::BaseStructureSeparator,
+ BNInstructionTextTokenType::BraceToken => Self::Brace {
+ hash: match value.value {
+ 0 => None,
+ hash => Some(hash),
+ },
+ },
+ BNInstructionTextTokenType::CodeSymbolToken => Self::CodeSymbol {
+ value: value.value,
+ size: value.size,
+ },
+ BNInstructionTextTokenType::DataSymbolToken => Self::DataSymbol {
+ value: value.value,
+ size: value.size,
+ },
+ BNInstructionTextTokenType::LocalVariableToken => Self::LocalVariable {
+ variable_id: value.value,
+ ssa_version: value.operand,
+ },
+ BNInstructionTextTokenType::ImportToken => Self::Import {
+ target: value.value,
+ },
+ BNInstructionTextTokenType::AddressDisplayToken => Self::AddressDisplay {
+ address: value.value,
+ },
+ BNInstructionTextTokenType::IndirectImportToken => Self::IndirectImport {
+ target: value.value,
+ size: value.size,
+ source_operand: value.operand,
+ },
+ BNInstructionTextTokenType::ExternalSymbolToken => {
+ Self::ExternalSymbol { value: value.value }
+ }
+ BNInstructionTextTokenType::StackVariableToken => Self::StackVariable {
+ variable_id: value.value,
+ },
+ BNInstructionTextTokenType::AddressSeparatorToken => Self::AddressSeparator,
+ BNInstructionTextTokenType::CollapsedInformationToken => Self::CollapsedInformation,
+ BNInstructionTextTokenType::CollapseStateIndicatorToken => {
+ Self::CollapseStateIndicator {
+ hash: match value.value {
+ 0 => None,
+ hash => Some(hash),
+ },
+ }
+ }
+ }
}
- pub fn tag_count(&self) -> usize {
- self.0.tagCount
+ /// Mapping to the [`BNInstructionTextTokenType::value`] field.
+ fn try_value(&self) -> Option<u64> {
+ // TODO: Double check to make sure these are correct.
+ match self {
+ InstructionTextTokenKind::Integer { value, .. } => Some(*value),
+ InstructionTextTokenKind::PossibleAddress { value, .. } => Some(*value),
+ InstructionTextTokenKind::PossibleValue { value, .. } => Some(*value),
+ InstructionTextTokenKind::FloatingPoint { value, .. } => Some(*value as u64),
+ InstructionTextTokenKind::CodeRelativeAddress { value, .. } => Some(*value),
+ InstructionTextTokenKind::ArgumentName { value, .. } => Some(*value),
+ InstructionTextTokenKind::HexDumpByteValue { value, .. } => Some(*value as u64),
+ InstructionTextTokenKind::HexDumpText { width, .. } => Some(*width),
+ InstructionTextTokenKind::String { ty, .. } => Some(*ty as u64),
+ InstructionTextTokenKind::FieldName { offset, .. } => Some(*offset),
+ InstructionTextTokenKind::StructOffset { offset, .. } => Some(*offset),
+ InstructionTextTokenKind::StructureHexDumpText { width, .. } => Some(*width),
+ InstructionTextTokenKind::GotoLabel { target, .. } => Some(*target),
+ InstructionTextTokenKind::Comment { target, .. } => Some(*target),
+ InstructionTextTokenKind::ArrayIndex { index, .. } => Some(*index),
+ InstructionTextTokenKind::EnumerationMember { value, .. } => Some(*value),
+ InstructionTextTokenKind::LocalVariable { variable_id, .. } => Some(*variable_id),
+ InstructionTextTokenKind::Import { target, .. } => Some(*target),
+ InstructionTextTokenKind::AddressDisplay { address, .. } => Some(*address),
+ InstructionTextTokenKind::IndirectImport { target, .. } => Some(*target),
+ InstructionTextTokenKind::Brace { hash, .. } => *hash,
+ InstructionTextTokenKind::CodeSymbol { value, .. } => Some(*value),
+ InstructionTextTokenKind::DataSymbol { value, .. } => Some(*value),
+ InstructionTextTokenKind::ExternalSymbol { value, .. } => Some(*value),
+ InstructionTextTokenKind::StackVariable { variable_id, .. } => Some(*variable_id),
+ InstructionTextTokenKind::CollapseStateIndicator { hash, .. } => *hash,
+ _ => None,
+ }
}
- pub fn tokens(&self) -> Vec<InstructionTextToken> {
- unsafe {
- std::slice::from_raw_parts::<BNInstructionTextToken>(self.0.tokens, self.0.count)
- .iter()
- .map(|x| InstructionTextToken::from_raw(x).clone())
- .collect()
+ /// Mapping to the [`BNInstructionTextTokenType::size`] field.
+ fn try_size(&self) -> Option<usize> {
+ match self {
+ InstructionTextTokenKind::Integer { size, .. } => *size,
+ InstructionTextTokenKind::FloatingPoint { size, .. } => *size,
+ InstructionTextTokenKind::PossibleAddress { size, .. } => *size,
+ InstructionTextTokenKind::CodeRelativeAddress { size, .. } => *size,
+ InstructionTextTokenKind::CodeSymbol { size, .. } => Some(*size),
+ InstructionTextTokenKind::DataSymbol { size, .. } => Some(*size),
+ InstructionTextTokenKind::IndirectImport { size, .. } => Some(*size),
+ _ => None,
}
}
-}
-impl std::fmt::Display for DisassemblyTextLine {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- for token in self.tokens() {
- write!(f, "{}", token.text())?;
+ /// Mapping to the [`BNInstructionTextTokenType::operand`] field.
+ fn try_operand(&self) -> Option<usize> {
+ match self {
+ InstructionTextTokenKind::LocalVariable { ssa_version, .. } => Some(*ssa_version),
+ InstructionTextTokenKind::IndirectImport { source_operand, .. } => {
+ Some(*source_operand)
+ }
+ _ => None,
}
-
- Ok(())
}
-}
-impl From<Vec<InstructionTextToken>> for DisassemblyTextLine {
- fn from(tokens: Vec<InstructionTextToken>) -> Self {
- let mut tokens: Box<[_]> = tokens.into();
-
- // 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();
- mem::forget(tokens);
-
- DisassemblyTextLine(BNDisassemblyTextLine {
- addr: 0,
- instrIndex: BN_INVALID_EXPR,
- tokens: tokens_pointer as *mut _,
- count: tokens_len,
- highlight: BNHighlightColor {
- style: BNHighlightColorStyle::StandardHighlightColor,
- color: BNHighlightStandardColor::NoHighlightColor,
- mixColor: BNHighlightStandardColor::NoHighlightColor,
- mix: 0,
- r: 0,
- g: 0,
- b: 0,
- alpha: 0,
- },
- tags: ptr::null_mut(),
- tagCount: 0,
- typeInfo: BNDisassemblyTextLineTypeInfo {
- hasTypeInfo: false,
- parentType: ptr::null_mut(),
- fieldIndex: usize::MAX,
- offset: 0,
- },
- })
+ /// Mapping to the [`BNInstructionTextTokenType::typeNames`] field.
+ fn try_type_names(&self) -> Option<Vec<String>> {
+ match self {
+ InstructionTextTokenKind::FieldName { type_names, .. } => Some(type_names.clone()),
+ InstructionTextTokenKind::StructOffset { type_names, .. } => Some(type_names.clone()),
+ InstructionTextTokenKind::EnumerationMember { type_id, .. } => {
+ Some(vec![type_id.clone()?])
+ }
+ _ => None,
+ }
}
}
-impl From<&Vec<&str>> for DisassemblyTextLine {
- fn from(string_tokens: &Vec<&str>) -> Self {
- let mut tokens: Box<[BNInstructionTextToken]> = string_tokens
- .iter()
- .map(|&token| InstructionTextToken::new(token, InstructionTextTokenContents::Text).into_raw())
- .collect();
-
- // 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();
- mem::forget(tokens);
-
- DisassemblyTextLine(BNDisassemblyTextLine {
- addr: 0,
- instrIndex: BN_INVALID_EXPR,
- tokens: tokens_pointer as *mut _,
- count: tokens_len,
- highlight: BNHighlightColor {
- style: BNHighlightColorStyle::StandardHighlightColor,
- color: BNHighlightStandardColor::NoHighlightColor,
- mixColor: BNHighlightStandardColor::NoHighlightColor,
- mix: 0,
- r: 0,
- g: 0,
- b: 0,
- alpha: 0,
- },
- tags: ptr::null_mut(),
- tagCount: 0,
- typeInfo: BNDisassemblyTextLineTypeInfo {
- hasTypeInfo: false,
- parentType: ptr::null_mut(),
- fieldIndex: usize::MAX,
- offset: 0,
- },
- })
+impl From<InstructionTextTokenKind> for BNInstructionTextTokenType {
+ fn from(value: InstructionTextTokenKind) -> Self {
+ match value {
+ InstructionTextTokenKind::Text => BNInstructionTextTokenType::TextToken,
+ InstructionTextTokenKind::Instruction => BNInstructionTextTokenType::InstructionToken,
+ InstructionTextTokenKind::OperandSeparator => {
+ BNInstructionTextTokenType::OperandSeparatorToken
+ }
+ InstructionTextTokenKind::Register => BNInstructionTextTokenType::RegisterToken,
+ InstructionTextTokenKind::Integer { .. } => BNInstructionTextTokenType::IntegerToken,
+ InstructionTextTokenKind::PossibleAddress { .. } => {
+ BNInstructionTextTokenType::PossibleAddressToken
+ }
+ InstructionTextTokenKind::BeginMemoryOperand => {
+ BNInstructionTextTokenType::BeginMemoryOperandToken
+ }
+ InstructionTextTokenKind::EndMemoryOperand => {
+ BNInstructionTextTokenType::EndMemoryOperandToken
+ }
+ InstructionTextTokenKind::FloatingPoint { .. } => {
+ BNInstructionTextTokenType::FloatingPointToken
+ }
+ InstructionTextTokenKind::Annotation => BNInstructionTextTokenType::AnnotationToken,
+ InstructionTextTokenKind::CodeRelativeAddress { .. } => {
+ BNInstructionTextTokenType::CodeRelativeAddressToken
+ }
+ InstructionTextTokenKind::ArgumentName { .. } => {
+ BNInstructionTextTokenType::ArgumentNameToken
+ }
+ InstructionTextTokenKind::HexDumpByteValue { .. } => {
+ BNInstructionTextTokenType::HexDumpByteValueToken
+ }
+ InstructionTextTokenKind::HexDumpSkippedByte => {
+ BNInstructionTextTokenType::HexDumpSkippedByteToken
+ }
+ InstructionTextTokenKind::HexDumpInvalidByte => {
+ BNInstructionTextTokenType::HexDumpInvalidByteToken
+ }
+ InstructionTextTokenKind::HexDumpText { .. } => {
+ BNInstructionTextTokenType::HexDumpTextToken
+ }
+ InstructionTextTokenKind::Opcode => BNInstructionTextTokenType::OpcodeToken,
+ InstructionTextTokenKind::String { .. } => BNInstructionTextTokenType::StringToken,
+ InstructionTextTokenKind::CharacterConstant => {
+ BNInstructionTextTokenType::CharacterConstantToken
+ }
+ InstructionTextTokenKind::Keyword => BNInstructionTextTokenType::KeywordToken,
+ InstructionTextTokenKind::TypeName => BNInstructionTextTokenType::TypeNameToken,
+ InstructionTextTokenKind::FieldName { .. } => {
+ BNInstructionTextTokenType::FieldNameToken
+ }
+ InstructionTextTokenKind::NameSpace => BNInstructionTextTokenType::NameSpaceToken,
+ InstructionTextTokenKind::NameSpaceSeparator => {
+ BNInstructionTextTokenType::NameSpaceSeparatorToken
+ }
+ InstructionTextTokenKind::Tag => BNInstructionTextTokenType::TagToken,
+ InstructionTextTokenKind::StructOffset { .. } => {
+ BNInstructionTextTokenType::StructOffsetToken
+ }
+ InstructionTextTokenKind::StructOffsetByteValue => {
+ BNInstructionTextTokenType::StructOffsetByteValueToken
+ }
+ InstructionTextTokenKind::StructureHexDumpText { .. } => {
+ BNInstructionTextTokenType::StructureHexDumpTextToken
+ }
+ InstructionTextTokenKind::GotoLabel { .. } => {
+ BNInstructionTextTokenType::GotoLabelToken
+ }
+ InstructionTextTokenKind::Comment { .. } => BNInstructionTextTokenType::CommentToken,
+ InstructionTextTokenKind::PossibleValue { .. } => {
+ BNInstructionTextTokenType::PossibleValueToken
+ }
+ InstructionTextTokenKind::PossibleValueType => {
+ BNInstructionTextTokenType::PossibleValueTypeToken
+ }
+ InstructionTextTokenKind::ArrayIndex { .. } => {
+ BNInstructionTextTokenType::ArrayIndexToken
+ }
+ InstructionTextTokenKind::Indentation => BNInstructionTextTokenType::IndentationToken,
+ InstructionTextTokenKind::UnknownMemory => {
+ BNInstructionTextTokenType::UnknownMemoryToken
+ }
+ InstructionTextTokenKind::EnumerationMember { .. } => {
+ BNInstructionTextTokenType::EnumerationMemberToken
+ }
+ InstructionTextTokenKind::Operation => BNInstructionTextTokenType::OperationToken,
+ InstructionTextTokenKind::BaseStructureName => {
+ BNInstructionTextTokenType::BaseStructureNameToken
+ }
+ InstructionTextTokenKind::BaseStructureSeparator => {
+ BNInstructionTextTokenType::BaseStructureSeparatorToken
+ }
+ InstructionTextTokenKind::Brace { .. } => BNInstructionTextTokenType::BraceToken,
+ InstructionTextTokenKind::CodeSymbol { .. } => {
+ BNInstructionTextTokenType::CodeSymbolToken
+ }
+ InstructionTextTokenKind::DataSymbol { .. } => {
+ BNInstructionTextTokenType::DataSymbolToken
+ }
+ InstructionTextTokenKind::LocalVariable { .. } => {
+ BNInstructionTextTokenType::LocalVariableToken
+ }
+ InstructionTextTokenKind::Import { .. } => BNInstructionTextTokenType::ImportToken,
+ InstructionTextTokenKind::AddressDisplay { .. } => {
+ BNInstructionTextTokenType::AddressDisplayToken
+ }
+ InstructionTextTokenKind::IndirectImport { .. } => {
+ BNInstructionTextTokenType::IndirectImportToken
+ }
+ InstructionTextTokenKind::ExternalSymbol { .. } => {
+ BNInstructionTextTokenType::ExternalSymbolToken
+ }
+ InstructionTextTokenKind::StackVariable { .. } => {
+ BNInstructionTextTokenType::StackVariableToken
+ }
+ InstructionTextTokenKind::AddressSeparator => {
+ BNInstructionTextTokenType::AddressSeparatorToken
+ }
+ InstructionTextTokenKind::CollapsedInformation => {
+ BNInstructionTextTokenType::CollapsedInformationToken
+ }
+ InstructionTextTokenKind::CollapseStateIndicator { .. } => {
+ BNInstructionTextTokenType::CollapseStateIndicatorToken
+ }
+ }
}
}
-impl Default for DisassemblyTextLine {
- fn default() -> Self {
- DisassemblyTextLine(BNDisassemblyTextLine {
- addr: 0,
- instrIndex: BN_INVALID_EXPR,
- tokens: ptr::null_mut(),
- count: 0,
- highlight: BNHighlightColor {
- style: BNHighlightColorStyle::StandardHighlightColor,
- color: BNHighlightStandardColor::NoHighlightColor,
- mixColor: BNHighlightStandardColor::NoHighlightColor,
- mix: 0,
- r: 0,
- g: 0,
- b: 0,
- alpha: 0,
- },
- tags: ptr::null_mut(),
- tagCount: 0,
- typeInfo: BNDisassemblyTextLineTypeInfo {
- hasTypeInfo: false,
- parentType: ptr::null_mut(),
- fieldIndex: usize::MAX,
- offset: 0,
- },
- })
- }
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+pub enum InstructionTextTokenContext {
+ Normal,
+ LocalVariable,
+ DataVariable,
+ FunctionReturn,
+ InstructionAddress,
+ ILInstructionIndex,
+ ConstData,
+ /// Use only with [`InstructionTextTokenKind::String`]
+ ConstStringData,
+ /// Use only with [`InstructionTextTokenKind::String`]
+ StringReference,
+ /// Use only with [`InstructionTextTokenKind::String`]
+ StringDataVariable,
+ /// For displaying strings which aren't associated with an address
+ ///
+ /// Use only with [`InstructionTextTokenKind::String`]
+ StringDisplay,
+ /// Use only with [`InstructionTextTokenKind::CollapseStateIndicator`]
+ Collapsed,
+ /// Use only with [`InstructionTextTokenKind::CollapseStateIndicator`]
+ Expanded,
+ /// Use only with [`InstructionTextTokenKind::CollapseStateIndicator`]
+ CollapsiblePadding,
}
-impl Drop for DisassemblyTextLine {
- fn drop(&mut self) {
- 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 From<BNInstructionTextTokenContext> for InstructionTextTokenContext {
+ fn from(value: BNInstructionTextTokenContext) -> Self {
+ match value {
+ BNInstructionTextTokenContext::NoTokenContext => Self::Normal,
+ BNInstructionTextTokenContext::LocalVariableTokenContext => Self::LocalVariable,
+ BNInstructionTextTokenContext::DataVariableTokenContext => Self::DataVariable,
+ BNInstructionTextTokenContext::FunctionReturnTokenContext => Self::FunctionReturn,
+ BNInstructionTextTokenContext::InstructionAddressTokenContext => {
+ Self::InstructionAddress
+ }
+ BNInstructionTextTokenContext::ILInstructionIndexTokenContext => {
+ Self::ILInstructionIndex
+ }
+ BNInstructionTextTokenContext::ConstDataTokenContext => Self::ConstData,
+ // For use with [`InstructionTextTokenKind::String`]
+ BNInstructionTextTokenContext::ConstStringDataTokenContext => Self::ConstStringData,
+ BNInstructionTextTokenContext::StringReferenceTokenContext => Self::StringReference,
+ BNInstructionTextTokenContext::StringDataVariableTokenContext => {
+ Self::StringDataVariable
+ }
+ BNInstructionTextTokenContext::StringDisplayTokenContext => Self::StringDisplay,
+ // For use with [`InstructionTextTokenKind::CollapseStateIndicator`]
+ BNInstructionTextTokenContext::ContentCollapsedContext => Self::Collapsed,
+ BNInstructionTextTokenContext::ContentExpandedContext => Self::Expanded,
+ BNInstructionTextTokenContext::ContentCollapsiblePadding => Self::CollapsiblePadding,
}
}
}
-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)
+impl From<InstructionTextTokenContext> for BNInstructionTextTokenContext {
+ fn from(value: InstructionTextTokenContext) -> Self {
+ match value {
+ InstructionTextTokenContext::Normal => Self::NoTokenContext,
+ InstructionTextTokenContext::LocalVariable => Self::LocalVariableTokenContext,
+ InstructionTextTokenContext::DataVariable => Self::DataVariableTokenContext,
+ InstructionTextTokenContext::FunctionReturn => Self::FunctionReturnTokenContext,
+ InstructionTextTokenContext::InstructionAddress => Self::InstructionAddressTokenContext,
+ InstructionTextTokenContext::ILInstructionIndex => Self::ILInstructionIndexTokenContext,
+ InstructionTextTokenContext::ConstData => Self::ConstDataTokenContext,
+ InstructionTextTokenContext::ConstStringData => Self::ConstStringDataTokenContext,
+ InstructionTextTokenContext::StringReference => Self::StringReferenceTokenContext,
+ InstructionTextTokenContext::StringDataVariable => Self::StringDataVariableTokenContext,
+ InstructionTextTokenContext::StringDisplay => Self::StringDisplayTokenContext,
+ InstructionTextTokenContext::Collapsed => Self::ContentCollapsedContext,
+ InstructionTextTokenContext::Expanded => Self::ContentExpandedContext,
+ InstructionTextTokenContext::CollapsiblePadding => Self::ContentCollapsiblePadding,
+ }
}
}
-pub type DisassemblyOption = BNDisassemblyOption;
-
+// TODO: Make a builder for this.
#[derive(PartialEq, Eq, Hash)]
pub struct DisassemblySettings {
pub(crate) handle: *mut BNDisassemblySettings,
@@ -481,9 +969,7 @@ impl DisassemblySettings {
pub fn new() -> Ref<Self> {
unsafe {
let handle = BNCreateDisassemblySettings();
-
debug_assert!(!handle.is_null());
-
Ref::new(Self { handle })
}
}
diff --git a/rust/src/downloadprovider.rs b/rust/src/download_provider.rs
index 0d388486..1e61c87d 100644
--- a/rust/src/downloadprovider.rs
+++ b/rust/src/download_provider.rs
@@ -1,4 +1,4 @@
-use crate::rc::{Array, CoreArrayProvider, Guard, CoreArrayProviderInner, Ref, RefCountable};
+use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
use crate::settings::Settings;
use crate::string::{BnStrCompatible, BnString};
use binaryninjacore_sys::*;
@@ -38,8 +38,8 @@ impl DownloadProvider {
/// TODO : We may want to `impl Default`....excessive error checking might be preventing us from doing so
pub fn try_default() -> Result<DownloadProvider, ()> {
- let s = Settings::new("");
- let dp_name = s.get_string("network.downloadProviderName", None, None);
+ let s = Settings::new();
+ let dp_name = s.get_string("network.downloadProviderName");
Self::get(dp_name).ok_or(())
}
@@ -68,6 +68,7 @@ unsafe impl CoreArrayProviderInner for DownloadProvider {
unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
BNFreeDownloadProviderList(raw);
}
+
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
Guard::new(DownloadProvider::from_raw(*raw), &())
}
diff --git a/rust/src/enterprise.rs b/rust/src/enterprise.rs
index 5b8877f3..34fa8a03 100644
--- a/rust/src/enterprise.rs
+++ b/rust/src/enterprise.rs
@@ -1,59 +1,68 @@
-use std::marker::PhantomData;
-use std::time::{Duration, SystemTime, UNIX_EPOCH};
-
use crate::rc::Array;
use crate::string::{BnStrCompatible, BnString};
+use std::marker::PhantomData;
+use std::time::{Duration, SystemTime, UNIX_EPOCH};
+use thiserror::Error;
-
-#[derive(Debug)]
-pub struct EnterpriseCheckoutError(pub String);
-
-impl std::fmt::Display for EnterpriseCheckoutError {
- fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
- write!(f, "{}", self.0)
- }
+#[derive(Error, Debug)]
+pub enum EnterpriseCheckoutError {
+ #[error("enterprise server returned error: {0}")]
+ ServerError(String),
+ #[error("no username set for credential authentication")]
+ NoUsername,
+ #[error("no password set for credential authentication")]
+ NoPassword,
+ #[error("failed to authenticate with username and password")]
+ NotAuthenticated,
+ #[error("failed to refresh expired license")]
+ RefreshExpiredLicenseFailed,
}
-impl std::error::Error for EnterpriseCheckoutError {}
-
+/// Initialize the enterprise server connection to check out a floating license.
pub fn checkout_license(duration: Duration) -> Result<(), EnterpriseCheckoutError> {
if crate::is_ui_enabled() {
+ // We only need to check out a license if running headlessly.
return Ok(());
}
+ #[allow(clippy::collapsible_if)]
if !is_server_initialized() {
+ // We need to first initialize the server.
if !initialize_server() && is_server_floating_license() {
- return Err(EnterpriseCheckoutError(server_last_error().to_string()));
+ let last_error = server_last_error().to_string();
+ return Err(EnterpriseCheckoutError::ServerError(last_error));
}
}
if is_server_floating_license() {
if !is_server_connected() && !connect_server() {
- return Err(EnterpriseCheckoutError(server_last_error().to_string()));
+ let last_error = server_last_error().to_string();
+ return Err(EnterpriseCheckoutError::ServerError(last_error));
}
+ #[allow(clippy::collapsible_if)]
if !is_server_authenticated() {
+ // We have yet to authenticate with the server, we should try all available authentication methods.
if !authenticate_server_with_method("Keychain", false) {
- let Some(username) = std::env::var("BN_ENTERPRISE_USERNAME").ok() else {
- return Err(EnterpriseCheckoutError("BN_ENTERPRISE_USERNAME not set when attempting to authenticate with credentials".to_string()));
- };
- let Some(password) = std::env::var("BN_ENTERPRISE_PASSWORD").ok() else {
- return Err(EnterpriseCheckoutError("BN_ENTERPRISE_PASSWORD not set when attempting to authenticate with credentials".to_string()));
- };
+ // We could not authenticate with the system keychain, we should try with credentials.
+ let username = std::env::var("BN_ENTERPRISE_USERNAME")
+ .map_err(|_| EnterpriseCheckoutError::NoUsername)?;
+ let password = std::env::var("BN_ENTERPRISE_PASSWORD")
+ .map_err(|_| EnterpriseCheckoutError::NoPassword)?;
if !authenticate_server_with_credentials(username, password, true) {
- let failed_message = "Could not checkout a license: Not authenticated. Try one of the following: \n \
- - Log in and check out a license for an extended time\n \
- - Set BN_ENTERPRISE_USERNAME and BN_ENTERPRISE_PASSWORD environment variables\n \
- - Use binaryninja::enterprise::{authenticate_server_with_method OR authenticate_server_with_credentials} in your code";
- return Err(EnterpriseCheckoutError(failed_message.to_string()));
+ return Err(EnterpriseCheckoutError::NotAuthenticated);
}
}
}
}
-
- if !is_server_license_still_activated() || (!is_server_floating_license() && crate::license_expiration_time() < SystemTime::now()) {
+
+ #[allow(clippy::collapsible_if)]
+ if !is_server_license_still_activated()
+ || (!is_server_floating_license() && crate::license_expiration_time() < SystemTime::now())
+ {
+ // If the license is expired we should refresh the license.
if !update_server_license(duration) {
- return Err(EnterpriseCheckoutError("Failed to refresh expired license".to_string()));
+ return Err(EnterpriseCheckoutError::RefreshExpiredLicenseFailed);
}
}
@@ -62,14 +71,27 @@ pub fn checkout_license(duration: Duration) -> Result<(), EnterpriseCheckoutErro
pub fn release_license() {
if !crate::is_ui_enabled() {
+ // This might look dumb, why would we want to connect to the server, would that not just mean
+ // we don't need to release the license? Well no, you could have run a script, acquired a license for 10 hours
+ // then you WOULD want to call release license, and your expectation is that acquired license
+ // will now be released. To release that you must have an active connection which is what this does.
+ if !is_server_initialized() {
+ initialize_server();
+ }
+ if !is_server_connected() {
+ connect_server();
+ }
+ // We should only release the license if we are running headlessly.
release_server_license();
}
}
+// TODO: If "" string return None
pub fn server_username() -> BnString {
unsafe { BnString::from_raw(binaryninjacore_sys::BNGetEnterpriseServerUsername()) }
}
+// TODO: If "" string return None
pub fn server_url() -> BnString {
unsafe { BnString::from_raw(binaryninjacore_sys::BNGetEnterpriseServerUrl()) }
}
@@ -77,7 +99,9 @@ pub fn server_url() -> BnString {
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)
+ binaryninjacore_sys::BNSetEnterpriseServerUrl(
+ url.as_ref().as_ptr() as *const std::os::raw::c_char
+ )
};
if result {
Ok(())
diff --git a/rust/src/external_library.rs b/rust/src/external_library.rs
new file mode 100644
index 00000000..2b3594b6
--- /dev/null
+++ b/rust/src/external_library.rs
@@ -0,0 +1,224 @@
+use crate::project::file::ProjectFile;
+use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
+use crate::string::{BnStrCompatible, BnString};
+use crate::symbol::Symbol;
+use binaryninjacore_sys::*;
+use std::ffi::c_char;
+use std::fmt::Debug;
+use std::ptr::NonNull;
+
+/// An ExternalLibrary is an abstraction for a library that is optionally backed
+/// by a [ProjectFile].
+#[repr(transparent)]
+pub struct ExternalLibrary {
+ pub(crate) handle: NonNull<BNExternalLibrary>,
+}
+
+impl ExternalLibrary {
+ pub(crate) unsafe fn from_raw(handle: NonNull<BNExternalLibrary>) -> Self {
+ Self { handle }
+ }
+
+ pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNExternalLibrary>) -> Ref<Self> {
+ Ref::new(Self { handle })
+ }
+
+ /// Get the name of this external library
+ pub fn name(&self) -> BnString {
+ let result = unsafe { BNExternalLibraryGetName(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ /// Get the file backing this external library
+ pub fn backing_file(&self) -> Option<Ref<ProjectFile>> {
+ let result = unsafe { BNExternalLibraryGetBackingFile(self.handle.as_ptr()) };
+ let handle = NonNull::new(result)?;
+ Some(unsafe { ProjectFile::ref_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| x.handle.as_ptr())
+ .unwrap_or(std::ptr::null_mut());
+ unsafe { BNExternalLibrarySetBackingFile(self.handle.as_ptr(), file_handle) }
+ }
+}
+
+impl ToOwned for ExternalLibrary {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for ExternalLibrary {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: NonNull::new(BNNewExternalLibraryReference(handle.handle.as_ptr())).unwrap(),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeExternalLibrary(handle.handle.as_ptr());
+ }
+}
+
+impl Debug for ExternalLibrary {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("ExternalLibrary")
+ .field("name", &self.name())
+ .field("backing_file", &self.backing_file())
+ .finish()
+ }
+}
+
+impl CoreArrayProvider for ExternalLibrary {
+ type Raw = *mut BNExternalLibrary;
+ type Context = ();
+ type Wrapped<'a> = Guard<'a, ExternalLibrary>;
+}
+
+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> {
+ let raw_ptr = NonNull::new(*raw).unwrap();
+ Guard::new(Self::from_raw(raw_ptr), context)
+ }
+}
+
+/// 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: NonNull<BNExternalLocation>,
+}
+
+impl ExternalLocation {
+ pub(crate) unsafe fn from_raw(handle: NonNull<BNExternalLocation>) -> Self {
+ Self { handle }
+ }
+
+ pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNExternalLocation>) -> Ref<Self> {
+ Ref::new(Self { handle })
+ }
+
+ /// Get the source symbol for this ExternalLocation
+ pub fn source_symbol(&self) -> Ref<Symbol> {
+ let result = unsafe { BNExternalLocationGetSourceSymbol(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { Symbol::ref_from_raw(result) }
+ }
+
+ /// Get the ExternalLibrary that this ExternalLocation targets
+ pub fn library(&self) -> Option<Ref<ExternalLibrary>> {
+ let result = unsafe { BNExternalLocationGetExternalLibrary(self.handle.as_ptr()) };
+ let handle = NonNull::new(result)?;
+ Some(unsafe { ExternalLibrary::ref_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| x.handle.as_ptr())
+ .unwrap_or(std::ptr::null_mut());
+ unsafe { BNExternalLocationSetExternalLibrary(self.handle.as_ptr(), lib_handle) }
+ }
+
+ /// Check if this ExternalLocation has a target address
+ pub fn has_target_address(&self) -> bool {
+ unsafe { BNExternalLocationHasTargetAddress(self.handle.as_ptr()) }
+ }
+
+ /// Check if this ExternalLocation has a target symbol
+ pub fn has_target_symbol(&self) -> bool {
+ unsafe { BNExternalLocationHasTargetSymbol(self.handle.as_ptr()) }
+ }
+
+ /// 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.handle.as_ptr()) })
+ }
+
+ /// 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, address: Option<u64>) -> bool {
+ match address {
+ Some(mut addr) => unsafe {
+ BNExternalLocationSetTargetAddress(self.handle.as_ptr(), &mut addr)
+ },
+ None => unsafe {
+ BNExternalLocationSetTargetAddress(self.handle.as_ptr(), std::ptr::null_mut())
+ },
+ }
+ }
+
+ /// Get the symbol pointed to by this ExternalLocation, if any
+ pub fn target_symbol(&self) -> Option<BnString> {
+ let result = unsafe { BNExternalLocationGetTargetSymbol(self.handle.as_ptr()) };
+ (!result.is_null()).then(|| unsafe { 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 c_char)
+ .unwrap_or(std::ptr::null_mut());
+ unsafe { BNExternalLocationSetTargetSymbol(self.handle.as_ptr(), symbol) }
+ }
+}
+
+impl Debug for ExternalLocation {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("ExternalLocation")
+ .field("source_symbol", &self.source_symbol())
+ .field("library", &self.library())
+ .field("target_address", &self.target_address())
+ .field("target_symbol", &self.target_symbol())
+ .finish()
+ }
+}
+
+impl ToOwned for ExternalLocation {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for ExternalLocation {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: NonNull::new(BNNewExternalLocationReference(handle.handle.as_ptr())).unwrap(),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeExternalLocation(handle.handle.as_ptr());
+ }
+}
+
+impl CoreArrayProvider for ExternalLocation {
+ type Raw = *mut BNExternalLocation;
+ type Context = ();
+ type Wrapped<'a> = Guard<'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> {
+ let raw_ptr = NonNull::new(*raw).unwrap();
+ Guard::new(Self::from_raw(raw_ptr), context)
+ }
+}
diff --git a/rust/src/externallibrary.rs b/rust/src/externallibrary.rs
deleted file mode 100644
index a5dcefc8..00000000
--- a/rust/src/externallibrary.rs
+++ /dev/null
@@ -1,209 +0,0 @@
-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)
- }
-}
diff --git a/rust/src/ffi.rs b/rust/src/ffi.rs
index eb584828..01835912 100644
--- a/rust/src/ffi.rs
+++ b/rust/src/ffi.rs
@@ -12,14 +12,21 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+use std::time::{Duration, SystemTime, UNIX_EPOCH};
+
macro_rules! ffi_wrap {
($n:expr, $b:expr) => {{
use std::panic;
use std::process;
panic::catch_unwind(|| $b).unwrap_or_else(|_| {
- error!("ffi callback caught panic: {}", $n);
+ log::error!("ffi callback caught panic: {}", $n);
process::abort()
})
}};
}
+
+pub(crate) fn time_from_bn(timestamp: u64) -> SystemTime {
+ let m = Duration::from_secs(timestamp);
+ UNIX_EPOCH + m
+}
diff --git a/rust/src/fileaccessor.rs b/rust/src/file_accessor.rs
index cf94e902..3f324710 100644
--- a/rust/src/fileaccessor.rs
+++ b/rust/src/file_accessor.rs
@@ -51,7 +51,7 @@ impl<'a> FileAccessor<'a> {
let dest = unsafe { slice::from_raw_parts_mut(dest as *mut u8, len) };
if f.seek(SeekFrom::Start(offset)).is_err() {
- debug!("Failed to seek to offset {:x}", offset);
+ log::debug!("Failed to seek to offset {:x}", offset);
0
} else {
f.read(dest).unwrap_or(0)
diff --git a/rust/src/filemetadata.rs b/rust/src/file_metadata.rs
index 15a7059d..fbbadcda 100644
--- a/rust/src/filemetadata.rs
+++ b/rust/src/file_metadata.rs
@@ -12,17 +12,26 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use std::ffi::c_void;
-use binaryninjacore_sys::{BNBeginUndoActions, BNCloseFile, BNCommitUndoActions, BNCreateDatabase, BNCreateFileMetadata, BNFileMetadata, BNFileMetadataGetSessionId, BNFreeFileMetadata, BNGetCurrentOffset, BNGetCurrentView, BNGetFileMetadataDatabase, BNGetFileViewOfType, BNGetFilename, BNGetProjectFile, BNIsAnalysisChanged, BNIsBackedByDatabase, BNIsFileModified, BNMarkFileModified, BNMarkFileSaved, BNNavigate, BNNewFileReference, BNOpenDatabaseForConfiguration, BNOpenExistingDatabase, BNRedo, BNRevertUndoActions, BNSaveAutoSnapshot, BNSetFilename, BNUndo};
-use binaryninjacore_sys::{BNCreateDatabaseWithProgress, BNOpenExistingDatabaseWithProgress};
-
-use crate::binaryview::BinaryView;
+use crate::binary_view::BinaryView;
use crate::database::Database;
-use crate::project::ProjectFile;
-
use crate::rc::*;
use crate::string::*;
+use binaryninjacore_sys::{
+ BNBeginUndoActions, BNCloseFile, BNCommitUndoActions, BNCreateDatabase, BNCreateFileMetadata,
+ BNFileMetadata, BNFileMetadataGetSessionId, BNFreeFileMetadata, BNGetCurrentOffset,
+ BNGetCurrentView, BNGetExistingViews, BNGetFileMetadataDatabase, BNGetFileViewOfType,
+ BNGetFilename, BNGetProjectFile, BNIsAnalysisChanged, BNIsBackedByDatabase, BNIsFileModified,
+ BNMarkFileModified, BNMarkFileSaved, BNNavigate, BNNewFileReference,
+ BNOpenDatabaseForConfiguration, BNOpenExistingDatabase, BNRedo, BNRevertUndoActions,
+ BNSaveAutoSnapshot, BNSetFilename, BNUndo,
+};
+use binaryninjacore_sys::{BNCreateDatabaseWithProgress, BNOpenExistingDatabaseWithProgress};
+use std::ffi::c_void;
+use std::fmt::Debug;
+use std::path::Path;
+use crate::progress::ProgressCallback;
+use crate::project::file::ProjectFile;
use std::ptr::{self, NonNull};
#[derive(PartialEq, Eq, Hash)]
@@ -30,20 +39,17 @@ pub struct FileMetadata {
pub(crate) handle: *mut BNFileMetadata,
}
-unsafe impl Send for FileMetadata {}
-unsafe impl Sync for FileMetadata {}
-
impl FileMetadata {
pub(crate) fn from_raw(handle: *mut BNFileMetadata) -> Self {
Self { handle }
}
+ pub(crate) fn ref_from_raw(handle: *mut BNFileMetadata) -> Ref<Self> {
+ unsafe { Ref::new(Self { handle }) }
+ }
+
pub fn new() -> Ref<Self> {
- unsafe {
- Ref::new(Self {
- handle: BNCreateFileMetadata(),
- })
- }
+ Self::ref_from_raw(unsafe { BNCreateFileMetadata() })
}
pub fn with_filename<S: BnStrCompatible>(name: S) -> Ref<Self> {
@@ -57,11 +63,9 @@ impl FileMetadata {
BNCloseFile(self.handle);
}
}
-
+
pub fn session_id(&self) -> usize {
- unsafe {
- BNFileMetadataGetSessionId(self.handle)
- }
+ unsafe { BNFileMetadataGetSessionId(self.handle) }
}
pub fn filename(&self) -> BnString {
@@ -99,7 +103,11 @@ impl FileMetadata {
unsafe { BNIsAnalysisChanged(self.handle) }
}
- pub fn is_database_backed<S: BnStrCompatible>(&self, view_type: S) -> bool {
+ pub fn is_database_backed(&self) -> bool {
+ self.is_database_backed_for_view_type("")
+ }
+
+ pub fn is_database_backed_for_view_type<S: BnStrCompatible>(&self, view_type: S) -> bool {
let view_type = view_type.into_bytes_with_nul();
unsafe { BNIsBackedByDatabase(self.handle, view_type.as_ref().as_ptr() as *const _) }
@@ -173,62 +181,81 @@ impl FileMetadata {
}
}
- pub fn get_view_of_type<S: BnStrCompatible>(&self, view: S) -> Result<Ref<BinaryView>, ()> {
+ pub fn view_of_type<S: BnStrCompatible>(&self, view: S) -> Option<Ref<BinaryView>> {
let view = view.into_bytes_with_nul();
unsafe {
- let res = BNGetFileViewOfType(self.handle, view.as_ref().as_ptr() as *const _);
-
- if res.is_null() {
- Err(())
- } else {
- Ok(BinaryView::from_raw(res))
+ let raw_view_ptr = BNGetFileViewOfType(self.handle, view.as_ref().as_ptr() as *const _);
+ match raw_view_ptr.is_null() {
+ false => Some(BinaryView::ref_from_raw(raw_view_ptr)),
+ true => None,
}
}
}
- pub fn get_project_file(&self) -> Option<ProjectFile> {
+ pub fn view_types(&self) -> Array<BnString> {
+ let mut count = 0;
+ unsafe {
+ let types = BNGetExistingViews(self.handle, &mut count);
+ Array::new(types, count, ())
+ }
+ }
+
+ /// Get the [`ProjectFile`] for the [`FileMetadata`].
+ pub fn project_file(&self) -> Option<Ref<ProjectFile>> {
unsafe {
let res = NonNull::new(BNGetProjectFile(self.handle))?;
- Some(ProjectFile::from_raw(res))
+ Some(ProjectFile::ref_from_raw(res))
}
}
- 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 _;
+ pub fn create_database(&self, file_path: impl AsRef<Path>) -> bool {
+ // Databases are created with the root view (Raw).
+ let Some(raw_view) = self.view_of_type("Raw") else {
+ return false;
+ };
- 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 c_void,
- Some(cb_progress_func),
- ptr::null_mut(),
- )
- },
+ let file_path = file_path.as_ref().into_bytes_with_nul();
+ unsafe {
+ BNCreateDatabase(
+ raw_view.handle,
+ file_path.as_ptr() as *mut _,
+ ptr::null_mut(),
+ )
}
}
- pub fn save_auto_snapshot(&self) -> bool {
- let raw = "Raw".into_bytes_with_nul();
+ // TODO: Pass settings?
+ pub fn create_database_with_progress<S: BnStrCompatible, P: ProgressCallback>(
+ &self,
+ file_path: impl AsRef<Path>,
+ mut progress: P,
+ ) -> bool {
+ // Databases are created with the root view (Raw).
+ let Some(raw_view) = self.view_of_type("Raw") else {
+ return false;
+ };
+ let file_path = file_path.as_ref().into_bytes_with_nul();
unsafe {
- BNSaveAutoSnapshot(
- BNGetFileViewOfType(self.handle, raw.as_ptr() as *mut _),
- ptr::null_mut() as *mut _,
+ BNCreateDatabaseWithProgress(
+ raw_view.handle,
+ file_path.as_ptr() as *mut _,
+ &mut progress as *mut P as *mut c_void,
+ Some(P::cb_progress_callback),
+ ptr::null_mut(),
)
}
}
+ pub fn save_auto_snapshot(&self) -> bool {
+ // Snapshots are saved with the root view (Raw).
+ let Some(raw_view) = self.view_of_type("Raw") else {
+ return false;
+ };
+
+ unsafe { BNSaveAutoSnapshot(raw_view.handle, ptr::null_mut() as *mut _) }
+ }
+
pub fn open_database_for_configuration<S: BnStrCompatible>(
&self,
filename: S,
@@ -241,45 +268,72 @@ impl FileMetadata {
if bv.is_null() {
Err(())
} else {
- Ok(BinaryView::from_raw(bv))
+ Ok(BinaryView::ref_from_raw(bv))
}
}
}
- pub fn open_database<S: BnStrCompatible>(
+ pub fn open_database<S: BnStrCompatible>(&self, filename: S) -> 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) };
+
+ if view.is_null() {
+ Err(())
+ } else {
+ Ok(unsafe { BinaryView::ref_from_raw(view) })
+ }
+ }
+
+ pub fn open_database_with_progress<S: BnStrCompatible, P: ProgressCallback>(
&self,
filename: S,
- progress_func: Option<fn(usize, usize) -> bool>,
+ mut progress: P,
) -> Result<Ref<BinaryView>, ()> {
let filename = filename.into_bytes_with_nul();
let filename_ptr = filename.as_ref().as_ptr() as *mut _;
- let view = match progress_func {
- None => unsafe { BNOpenExistingDatabase(self.handle, filename_ptr) },
- Some(func) => unsafe {
- BNOpenExistingDatabaseWithProgress(
- self.handle,
- filename_ptr,
- func as *mut c_void,
- Some(cb_progress_func),
- )
- },
+ let view = unsafe {
+ BNOpenExistingDatabaseWithProgress(
+ self.handle,
+ filename_ptr,
+ &mut progress as *mut P as *mut c_void,
+ Some(P::cb_progress_callback),
+ )
};
if view.is_null() {
Err(())
} else {
- Ok(unsafe { BinaryView::from_raw(view) })
+ Ok(unsafe { BinaryView::ref_from_raw(view) })
}
}
/// Get the current database
- pub fn database(&self) -> Option<Database> {
+ pub fn database(&self) -> Option<Ref<Database>> {
let result = unsafe { BNGetFileMetadataDatabase(self.handle) };
- ptr::NonNull::new(result).map(|handle| unsafe { Database::from_raw(handle) })
+ NonNull::new(result).map(|handle| unsafe { Database::ref_from_raw(handle) })
}
}
+impl Debug for FileMetadata {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("FileMetadata")
+ .field("filename", &self.filename())
+ .field("session_id", &self.session_id())
+ .field("modified", &self.modified())
+ .field("is_analysis_changed", &self.is_analysis_changed())
+ .field("current_view_type", &self.current_view())
+ .field("current_offset", &self.current_offset())
+ .field("view_types", &self.view_types().to_vec())
+ .finish()
+ }
+}
+
+unsafe impl Send for FileMetadata {}
+unsafe impl Sync for FileMetadata {}
+
impl ToOwned for FileMetadata {
type Owned = Ref<Self>;
@@ -299,12 +353,3 @@ unsafe impl RefCountable for FileMetadata {
BNFreeFileMetadata(handle.handle);
}
}
-
-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)
-}
diff --git a/rust/src/flowgraph.rs b/rust/src/flowgraph.rs
index c8505187..ead984f9 100644
--- a/rust/src/flowgraph.rs
+++ b/rust/src/flowgraph.rs
@@ -27,27 +27,51 @@ pub type EdgePenStyle = BNEdgePenStyle;
pub type ThemeColor = BNThemeColor;
pub type FlowGraphOption = BNFlowGraphOption;
-#[repr(transparent)]
-pub struct EdgeStyle(pub(crate) BNEdgeStyle);
+#[derive(PartialEq, Eq, Hash)]
+pub struct FlowGraph {
+ pub(crate) handle: *mut BNFlowGraph,
+}
-impl EdgeStyle {
- pub fn new(style: EdgePenStyle, width: usize, color: ThemeColor) -> Self {
- EdgeStyle(BNEdgeStyle {
- style,
- width,
- color,
- })
+impl FlowGraph {
+ pub(crate) unsafe fn from_raw(raw: *mut BNFlowGraph) -> Self {
+ Self { handle: raw }
+ }
+
+ pub fn new() -> Ref<Self> {
+ unsafe { Ref::new(FlowGraph::from_raw(BNCreateFlowGraph())) }
+ }
+
+ pub fn append(&self, node: &FlowGraphNode) -> usize {
+ unsafe { BNAddFlowGraphNode(self.handle, node.handle) }
+ }
+
+ pub fn set_option(&self, option: FlowGraphOption, value: bool) {
+ unsafe { BNSetFlowGraphOption(self.handle, option, value) }
+ }
+
+ pub fn is_option_set(&self, option: FlowGraphOption) -> bool {
+ unsafe { BNIsFlowGraphOptionSet(self.handle, option) }
}
}
-impl Default for EdgeStyle {
- fn default() -> Self {
- EdgeStyle(BNEdgeStyle {
- style: EdgePenStyle::SolidLine,
- width: 0,
- color: ThemeColor::AddressColor,
+unsafe impl RefCountable for FlowGraph {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: BNNewFlowGraphReference(handle.handle),
})
}
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeFlowGraph(handle.handle);
+ }
+}
+
+impl ToOwned for FlowGraph {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
}
#[derive(PartialEq, Eq, Hash)]
@@ -68,32 +92,33 @@ impl<'a> FlowGraphNode<'a> {
unsafe { FlowGraphNode::from_raw(BNCreateFlowGraphNode(graph.handle)) }
}
- pub fn set_disassembly_lines(&self, lines: &'a [DisassemblyTextLine]) {
+ pub fn set_lines(&self, lines: impl IntoIterator<Item = DisassemblyTextLine>) {
+ // NOTE: This will create allocations and increment tag refs, we must call DisassemblyTextLine::free_raw
+ let mut raw_lines: Vec<BNDisassemblyTextLine> = lines
+ .into_iter()
+ .map(DisassemblyTextLine::into_raw)
+ .collect();
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?
+ BNSetFlowGraphNodeLines(self.handle, raw_lines.as_mut_ptr(), raw_lines.len());
+ for raw_line in raw_lines {
+ DisassemblyTextLine::free_raw(raw_line);
+ }
}
}
- pub fn set_lines(&self, lines: Vec<&str>) {
- let lines = lines
- .iter()
- .map(|&line| DisassemblyTextLine::from(&vec![line]))
- .collect::<Vec<_>>();
- self.set_disassembly_lines(&lines);
- }
-
pub fn add_outgoing_edge(
&self,
type_: BranchType,
target: &'a FlowGraphNode,
- edge_style: &'a EdgeStyle,
+ edge_style: EdgeStyle,
) {
- unsafe { BNAddFlowGraphNodeOutgoingEdge(self.handle, type_, target.handle, edge_style.0) }
+ unsafe {
+ BNAddFlowGraphNodeOutgoingEdge(self.handle, type_, target.handle, edge_style.into())
+ }
}
}
-unsafe impl<'a> RefCountable for FlowGraphNode<'a> {
+unsafe impl RefCountable for FlowGraphNode<'_> {
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
Ref::new(Self {
handle: BNNewFlowGraphNodeReference(handle.handle),
@@ -106,7 +131,7 @@ unsafe impl<'a> RefCountable for FlowGraphNode<'a> {
}
}
-impl<'a> ToOwned for FlowGraphNode<'a> {
+impl ToOwned for FlowGraphNode<'_> {
type Owned = Ref<Self>;
fn to_owned(&self) -> Self::Owned {
@@ -114,49 +139,41 @@ impl<'a> ToOwned for FlowGraphNode<'a> {
}
}
-#[derive(PartialEq, Eq, Hash)]
-pub struct FlowGraph {
- pub(crate) handle: *mut BNFlowGraph,
+#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
+pub struct EdgeStyle {
+ style: EdgePenStyle,
+ width: usize,
+ color: ThemeColor,
}
-impl FlowGraph {
- pub(crate) unsafe fn from_raw(raw: *mut BNFlowGraph) -> Self {
- Self { handle: raw }
- }
-
- pub fn new() -> Ref<Self> {
- unsafe { Ref::new(FlowGraph::from_raw(BNCreateFlowGraph())) }
- }
-
- pub fn append(&self, node: &FlowGraphNode) -> usize {
- unsafe { BNAddFlowGraphNode(self.handle, node.handle) }
- }
-
- pub fn set_option(&self, option: FlowGraphOption, value: bool) {
- unsafe { BNSetFlowGraphOption(self.handle, option, value) }
- }
-
- pub fn is_option_set(&self, option: FlowGraphOption) -> bool {
- unsafe { BNIsFlowGraphOptionSet(self.handle, option) }
+impl EdgeStyle {
+ pub fn new(style: EdgePenStyle, width: usize, color: ThemeColor) -> Self {
+ Self {
+ style,
+ width,
+ color,
+ }
}
}
-unsafe impl RefCountable for FlowGraph {
- unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
- Ref::new(Self {
- handle: BNNewFlowGraphReference(handle.handle),
- })
+impl Default for EdgeStyle {
+ fn default() -> Self {
+ Self::new(EdgePenStyle::SolidLine, 0, ThemeColor::AddressColor)
}
+}
- unsafe fn dec_ref(handle: &Self) {
- BNFreeFlowGraph(handle.handle);
+impl From<BNEdgeStyle> for EdgeStyle {
+ fn from(style: BNEdgeStyle) -> Self {
+ Self::new(style.style, style.width, style.color)
}
}
-impl ToOwned for FlowGraph {
- type Owned = Ref<Self>;
-
- fn to_owned(&self) -> Self::Owned {
- unsafe { RefCountable::inc_ref(self) }
+impl From<EdgeStyle> for BNEdgeStyle {
+ fn from(style: EdgeStyle) -> Self {
+ Self {
+ style: style.style,
+ width: style.width,
+ color: style.color,
+ }
}
}
diff --git a/rust/src/function.rs b/rust/src/function.rs
index 0df9fcf5..b524edfb 100644
--- a/rust/src/function.rs
+++ b/rust/src/function.rs
@@ -16,41 +16,58 @@ use binaryninjacore_sys::*;
use crate::{
architecture::{Architecture, CoreArchitecture, CoreRegister, Register},
- basicblock::{BasicBlock, BlockContext},
- binaryview::{BinaryView, BinaryViewExt},
- callingconvention::CallingConvention,
+ basic_block::{BasicBlock, BlockContext},
+ binary_view::{BinaryView, BinaryViewExt},
+ calling_convention::CoreCallingConvention,
component::Component,
disassembly::{DisassemblySettings, DisassemblyTextLine},
flowgraph::FlowGraph,
- hlil, llil,
- mlil::{self, FunctionGraphType},
+ medium_level_il::FunctionGraphType,
platform::Platform,
references::CodeReference,
string::*,
symbol::Symbol,
tags::{Tag, TagReference, TagType},
- types::{
- Conf, ConstantReference, HighlightColor, IndirectBranchInfo, IntegerDisplayType,
- MergedVariable, NamedTypedVariable, QualifiedName, RegisterStackAdjustment, RegisterValue,
- RegisterValueType, StackVariableReference, Type, UnresolvedIndirectBranches, Variable,
- },
+ types::{IntegerDisplayType, QualifiedName, Type},
};
-use crate::{databuffer::DataBuffer, disassembly::InstructionTextToken, rc::*};
+use crate::{data_buffer::DataBuffer, disassembly::InstructionTextToken, rc::*};
pub use binaryninjacore_sys::BNAnalysisSkipReason as AnalysisSkipReason;
+pub use binaryninjacore_sys::BNBuiltinType as BuiltinType;
pub use binaryninjacore_sys::BNFunctionAnalysisSkipOverride as FunctionAnalysisSkipOverride;
pub use binaryninjacore_sys::BNFunctionUpdateType as FunctionUpdateType;
-pub use binaryninjacore_sys::BNBuiltinType as BuiltinType;
+pub use binaryninjacore_sys::BNHighlightStandardColor as HighlightStandardColor;
-use std::{fmt, mem};
-use std::{ffi::c_char, hash::Hash, ops::Range};
-use std::ptr::NonNull;
+use crate::architecture::RegisterId;
+use crate::confidence::Conf;
+use crate::high_level_il::HighLevelILFunction;
+use crate::low_level_il::{LiftedILFunction, RegularLowLevelILFunction};
+use crate::medium_level_il::MediumLevelILFunction;
+use crate::variable::{
+ IndirectBranchInfo, MergedVariable, NamedVariableWithType, RegisterValue, RegisterValueType,
+ StackVariableReference, Variable,
+};
use crate::workflow::Workflow;
+use std::fmt::{Debug, Formatter};
+use std::ptr::NonNull;
+use std::time::Duration;
+use std::{ffi::c_char, hash::Hash, ops::Range};
+/// Used to describe a location within a [`Function`].
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Location {
pub arch: Option<CoreArchitecture>,
pub addr: u64,
}
+impl Location {
+ pub(crate) fn from_raw(addr: u64, arch: *mut BNArchitecture) -> Self {
+ Self {
+ addr,
+ arch: Some(unsafe { CoreArchitecture::from_raw(arch) }),
+ }
+ }
+}
+
impl From<u64> for Location {
fn from(addr: u64) -> Self {
Location { arch: None, addr }
@@ -66,6 +83,21 @@ impl From<(CoreArchitecture, u64)> for Location {
}
}
+impl From<BNArchitectureAndAddress> for Location {
+ fn from(value: BNArchitectureAndAddress) -> Self {
+ Self::from_raw(value.address, value.arch)
+ }
+}
+
+impl From<Location> for BNArchitectureAndAddress {
+ fn from(value: Location) -> Self {
+ Self {
+ arch: value.arch.map(|a| a.handle).unwrap_or(std::ptr::null_mut()),
+ address: value.addr,
+ }
+ }
+}
+
pub struct NativeBlockIter {
arch: CoreArchitecture,
bv: Ref<BinaryView>,
@@ -108,19 +140,20 @@ impl NativeBlock {
}
impl BlockContext for NativeBlock {
- type Iter = NativeBlockIter;
type Instruction = u64;
+ type InstructionIndex = u64;
+ type Iter = NativeBlockIter;
fn start(&self, block: &BasicBlock<Self>) -> u64 {
- block.raw_start()
+ block.start_index()
}
fn iter(&self, block: &BasicBlock<Self>) -> NativeBlockIter {
NativeBlockIter {
arch: block.arch(),
bv: block.function().view(),
- cur: block.raw_start(),
- end: block.raw_end(),
+ cur: block.start_index(),
+ end: block.end_index(),
}
}
}
@@ -140,59 +173,119 @@ pub enum FunctionViewType {
HighLevelLanguageRepresentation(String),
}
-pub(crate) struct RawFunctionViewType(pub BNFunctionViewType);
-
+#[allow(unused)]
impl FunctionViewType {
- pub(crate) fn as_raw(&self) -> RawFunctionViewType {
- let view_type = match self {
+ pub(crate) fn from_raw(value: &BNFunctionViewType) -> Option<Self> {
+ match value.type_ {
+ BNFunctionGraphType::InvalidILViewType => None,
+ BNFunctionGraphType::NormalFunctionGraph => Some(FunctionViewType::Normal),
+ BNFunctionGraphType::LowLevelILFunctionGraph => Some(FunctionViewType::LowLevelIL),
+ BNFunctionGraphType::LiftedILFunctionGraph => Some(FunctionViewType::LiftedIL),
+ BNFunctionGraphType::LowLevelILSSAFormFunctionGraph => {
+ Some(FunctionViewType::LowLevelILSSAForm)
+ }
+ BNFunctionGraphType::MediumLevelILFunctionGraph => {
+ Some(FunctionViewType::MediumLevelIL)
+ }
+ BNFunctionGraphType::MediumLevelILSSAFormFunctionGraph => {
+ Some(FunctionViewType::MediumLevelILSSAForm)
+ }
+ BNFunctionGraphType::MappedMediumLevelILFunctionGraph => {
+ Some(FunctionViewType::MappedMediumLevelIL)
+ }
+ BNFunctionGraphType::MappedMediumLevelILSSAFormFunctionGraph => {
+ Some(FunctionViewType::MappedMediumLevelILSSAForm)
+ }
+ BNFunctionGraphType::HighLevelILFunctionGraph => Some(FunctionViewType::HighLevelIL),
+ BNFunctionGraphType::HighLevelILSSAFormFunctionGraph => {
+ Some(FunctionViewType::HighLevelILSSAForm)
+ }
+ BNFunctionGraphType::HighLevelLanguageRepresentationFunctionGraph => {
+ Some(FunctionViewType::HighLevelLanguageRepresentation(
+ raw_to_string(value.name).unwrap(),
+ ))
+ }
+ }
+ }
+
+ pub(crate) fn from_owned_raw(value: BNFunctionViewType) -> Option<Self> {
+ let owned = Self::from_raw(&value);
+ Self::free_raw(value);
+ owned
+ }
+
+ pub(crate) fn into_raw(value: Self) -> BNFunctionViewType {
+ let view_type = match value {
FunctionViewType::Normal => BNFunctionGraphType::NormalFunctionGraph,
FunctionViewType::LowLevelIL => BNFunctionGraphType::LowLevelILFunctionGraph,
FunctionViewType::LiftedIL => BNFunctionGraphType::LiftedILFunctionGraph,
- FunctionViewType::LowLevelILSSAForm => BNFunctionGraphType::LowLevelILSSAFormFunctionGraph,
+ FunctionViewType::LowLevelILSSAForm => {
+ BNFunctionGraphType::LowLevelILSSAFormFunctionGraph
+ }
FunctionViewType::MediumLevelIL => BNFunctionGraphType::MediumLevelILFunctionGraph,
- FunctionViewType::MediumLevelILSSAForm => BNFunctionGraphType::MediumLevelILSSAFormFunctionGraph,
- FunctionViewType::MappedMediumLevelIL => BNFunctionGraphType::MappedMediumLevelILFunctionGraph,
- FunctionViewType::MappedMediumLevelILSSAForm => BNFunctionGraphType::MappedMediumLevelILSSAFormFunctionGraph,
+ FunctionViewType::MediumLevelILSSAForm => {
+ BNFunctionGraphType::MediumLevelILSSAFormFunctionGraph
+ }
+ FunctionViewType::MappedMediumLevelIL => {
+ BNFunctionGraphType::MappedMediumLevelILFunctionGraph
+ }
+ FunctionViewType::MappedMediumLevelILSSAForm => {
+ BNFunctionGraphType::MappedMediumLevelILSSAFormFunctionGraph
+ }
FunctionViewType::HighLevelIL => BNFunctionGraphType::HighLevelILFunctionGraph,
- FunctionViewType::HighLevelILSSAForm => BNFunctionGraphType::HighLevelILSSAFormFunctionGraph,
- FunctionViewType::HighLevelLanguageRepresentation(_) => BNFunctionGraphType::HighLevelLanguageRepresentationFunctionGraph,
+ FunctionViewType::HighLevelILSSAForm => {
+ BNFunctionGraphType::HighLevelILSSAFormFunctionGraph
+ }
+ FunctionViewType::HighLevelLanguageRepresentation(_) => {
+ BNFunctionGraphType::HighLevelLanguageRepresentationFunctionGraph
+ }
+ };
+ let view_name = match value {
+ FunctionViewType::HighLevelLanguageRepresentation(name) => Some(BnString::new(name)),
+ _ => None,
};
- RawFunctionViewType(BNFunctionViewType {
+ BNFunctionViewType {
type_: view_type,
- name: if let FunctionViewType::HighLevelLanguageRepresentation(ref name) = self {
- std::ffi::CString::new(name.to_string()).unwrap().into_raw()
- } else {
- std::ptr::null()
- },
- })
+ name: view_name
+ .map(|n| BnString::into_raw(n) as *mut _)
+ .unwrap_or(std::ptr::null_mut()),
+ }
+ }
+
+ pub(crate) fn free_raw(value: BNFunctionViewType) {
+ let _ = unsafe { BnString::from_raw(value.name as *mut _) };
}
}
-impl Into<FunctionViewType> for FunctionGraphType {
- fn into(self) -> FunctionViewType {
- match self {
+impl From<FunctionGraphType> for FunctionViewType {
+ fn from(view_type: FunctionGraphType) -> Self {
+ match view_type {
BNFunctionGraphType::LowLevelILFunctionGraph => FunctionViewType::LowLevelIL,
BNFunctionGraphType::LiftedILFunctionGraph => FunctionViewType::LiftedIL,
- BNFunctionGraphType::LowLevelILSSAFormFunctionGraph => FunctionViewType::LowLevelILSSAForm,
+ BNFunctionGraphType::LowLevelILSSAFormFunctionGraph => {
+ FunctionViewType::LowLevelILSSAForm
+ }
BNFunctionGraphType::MediumLevelILFunctionGraph => FunctionViewType::MediumLevelIL,
- BNFunctionGraphType::MediumLevelILSSAFormFunctionGraph => FunctionViewType::MediumLevelILSSAForm,
- BNFunctionGraphType::MappedMediumLevelILFunctionGraph => FunctionViewType::MappedMediumLevelIL,
- BNFunctionGraphType::MappedMediumLevelILSSAFormFunctionGraph => FunctionViewType::MappedMediumLevelILSSAForm,
+ BNFunctionGraphType::MediumLevelILSSAFormFunctionGraph => {
+ FunctionViewType::MediumLevelILSSAForm
+ }
+ BNFunctionGraphType::MappedMediumLevelILFunctionGraph => {
+ FunctionViewType::MappedMediumLevelIL
+ }
+ BNFunctionGraphType::MappedMediumLevelILSSAFormFunctionGraph => {
+ FunctionViewType::MappedMediumLevelILSSAForm
+ }
BNFunctionGraphType::HighLevelILFunctionGraph => FunctionViewType::HighLevelIL,
- BNFunctionGraphType::HighLevelILSSAFormFunctionGraph => FunctionViewType::HighLevelILSSAForm,
+ BNFunctionGraphType::HighLevelILSSAFormFunctionGraph => {
+ FunctionViewType::HighLevelILSSAForm
+ }
BNFunctionGraphType::HighLevelLanguageRepresentationFunctionGraph => {
- FunctionViewType::HighLevelLanguageRepresentation("Pseudo C".into()
- )
+ // Historically this was the only language representation.
+ FunctionViewType::HighLevelLanguageRepresentation("Pseudo C".into())
+ }
+ BNFunctionGraphType::InvalidILViewType | BNFunctionGraphType::NormalFunctionGraph => {
+ FunctionViewType::Normal
}
- _ => FunctionViewType::Normal,
- }
- }
-}
-
-impl Drop for RawFunctionViewType {
- fn drop(&mut self) {
- if !self.0.name.is_null() {
- unsafe { let _ = std::ffi::CString::from_raw(self.0.name as *mut _); }
}
}
}
@@ -202,11 +295,14 @@ pub struct Function {
pub(crate) handle: *mut BNFunction,
}
-unsafe impl Send for Function {}
-unsafe impl Sync for Function {}
-
impl Function {
- pub(crate) unsafe fn from_raw(handle: *mut BNFunction) -> Ref<Self> {
+ pub(crate) unsafe fn from_raw(handle: *mut BNFunction) -> Self {
+ debug_assert!(!handle.is_null());
+ Self { handle }
+ }
+
+ pub(crate) unsafe fn ref_from_raw(handle: *mut BNFunction) -> Ref<Self> {
+ debug_assert!(!handle.is_null());
Ref::new(Self { handle })
}
@@ -227,7 +323,7 @@ impl Function {
pub fn view(&self) -> Ref<BinaryView> {
unsafe {
let view = BNGetFunctionData(self.handle);
- BinaryView::from_raw(view)
+ BinaryView::ref_from_raw(view)
}
}
@@ -301,7 +397,7 @@ impl Function {
}
/// All comments in the function
- pub fn comments(&self) -> Array<Comments> {
+ pub fn comments(&self) -> Array<Comment> {
let mut count = 0;
let lines = unsafe { BNGetCommentedAddresses(self.handle, &mut count) };
unsafe { Array::new(lines, count, self.to_owned()) }
@@ -335,14 +431,12 @@ impl Function {
) -> Option<Ref<BasicBlock<NativeBlock>>> {
let arch = arch.unwrap_or_else(|| self.arch());
unsafe {
- let block = BNGetFunctionBasicBlockAtAddress(self.handle, arch.0, addr);
+ let basic_block_ptr = BNGetFunctionBasicBlockAtAddress(self.handle, arch.handle, addr);
let context = NativeBlock { _priv: () };
-
- if block.is_null() {
- return None;
+ match basic_block_ptr.is_null() {
+ false => Some(BasicBlock::ref_from_raw(basic_block_ptr, context)),
+ true => None,
}
-
- Some(Ref::new(BasicBlock::from_raw(block, context)))
}
}
@@ -353,147 +447,135 @@ impl Function {
) -> Array<Array<InstructionTextToken>> {
let arch = arch.unwrap_or_else(|| self.arch());
let mut count = 0;
- let lines = unsafe { BNGetFunctionBlockAnnotations(self.handle, arch.0, addr, &mut count) };
+ let lines =
+ unsafe { BNGetFunctionBlockAnnotations(self.handle, arch.handle, addr, &mut count) };
assert!(!lines.is_null());
unsafe { Array::new(lines, count, ()) }
}
- pub fn get_variable_name(&self, var: &Variable) -> BnString {
+ pub fn variable_name(&self, var: &Variable) -> BnString {
unsafe {
- let raw_var = var.raw();
+ let raw_var = BNVariable::from(var);
let raw_name = BNGetVariableName(self.handle, &raw_var);
BnString::from_raw(raw_name)
}
}
- pub fn high_level_il(&self, full_ast: bool) -> Result<Ref<hlil::HighLevelILFunction>, ()> {
+ pub fn high_level_il(&self, full_ast: bool) -> Result<Ref<HighLevelILFunction>, ()> {
unsafe {
- let hlil = BNGetFunctionHighLevelIL(self.handle);
-
- if hlil.is_null() {
- return Err(());
+ let hlil_ptr = BNGetFunctionHighLevelIL(self.handle);
+ match hlil_ptr.is_null() {
+ false => Ok(HighLevelILFunction::ref_from_raw(hlil_ptr, full_ast)),
+ true => Err(()),
}
-
- Ok(hlil::HighLevelILFunction::ref_from_raw(hlil, full_ast))
}
}
- pub fn high_level_il_if_available(&self) -> Option<Ref<hlil::HighLevelILFunction>> {
- let hlil = unsafe { BNGetFunctionHighLevelILIfAvailable(self.handle) };
- (!hlil.is_null()).then(|| unsafe { hlil::HighLevelILFunction::ref_from_raw(hlil, true) })
+ pub fn high_level_il_if_available(&self) -> Option<Ref<HighLevelILFunction>> {
+ let hlil_ptr = unsafe { BNGetFunctionHighLevelILIfAvailable(self.handle) };
+ match hlil_ptr.is_null() {
+ false => Some(unsafe { HighLevelILFunction::ref_from_raw(hlil_ptr, true) }),
+ true => None,
+ }
}
/// MediumLevelILFunction used to represent Function mapped medium level IL
- pub fn mapped_medium_level_il(&self) -> Result<Ref<mlil::MediumLevelILFunction>, ()> {
- let mlil = unsafe { BNGetFunctionMappedMediumLevelIL(self.handle) };
- if mlil.is_null() {
- return Err(());
+ pub fn mapped_medium_level_il(&self) -> Result<Ref<MediumLevelILFunction>, ()> {
+ let mlil_ptr = unsafe { BNGetFunctionMappedMediumLevelIL(self.handle) };
+ match mlil_ptr.is_null() {
+ false => Ok(unsafe { MediumLevelILFunction::ref_from_raw(mlil_ptr) }),
+ true => Err(()),
}
- Ok(unsafe { mlil::MediumLevelILFunction::ref_from_raw(mlil) })
}
- pub fn mapped_medium_level_il_if_available(
- &self,
- ) -> Result<Ref<mlil::MediumLevelILFunction>, ()> {
- let mlil = unsafe { BNGetFunctionMappedMediumLevelILIfAvailable(self.handle) };
- if mlil.is_null() {
- return Err(());
+ pub fn mapped_medium_level_il_if_available(&self) -> Option<Ref<MediumLevelILFunction>> {
+ let mlil_ptr = unsafe { BNGetFunctionMappedMediumLevelILIfAvailable(self.handle) };
+ match mlil_ptr.is_null() {
+ false => Some(unsafe { MediumLevelILFunction::ref_from_raw(mlil_ptr) }),
+ true => None,
}
- Ok(unsafe { mlil::MediumLevelILFunction::ref_from_raw(mlil) })
}
- pub fn medium_level_il(&self) -> Result<Ref<mlil::MediumLevelILFunction>, ()> {
+ pub fn medium_level_il(&self) -> Result<Ref<MediumLevelILFunction>, ()> {
unsafe {
- let mlil = BNGetFunctionMediumLevelIL(self.handle);
-
- if mlil.is_null() {
- return Err(());
+ let mlil_ptr = BNGetFunctionMediumLevelIL(self.handle);
+ match mlil_ptr.is_null() {
+ false => Ok(MediumLevelILFunction::ref_from_raw(mlil_ptr)),
+ true => Err(()),
}
-
- Ok(mlil::MediumLevelILFunction::ref_from_raw(mlil))
}
}
- pub fn medium_level_il_if_available(&self) -> Option<Ref<mlil::MediumLevelILFunction>> {
- let mlil = unsafe { BNGetFunctionMediumLevelILIfAvailable(self.handle) };
- (!mlil.is_null()).then(|| unsafe { mlil::MediumLevelILFunction::ref_from_raw(mlil) })
+ pub fn medium_level_il_if_available(&self) -> Option<Ref<MediumLevelILFunction>> {
+ let mlil_ptr = unsafe { BNGetFunctionMediumLevelILIfAvailable(self.handle) };
+ match mlil_ptr.is_null() {
+ false => Some(unsafe { MediumLevelILFunction::ref_from_raw(mlil_ptr) }),
+ true => None,
+ }
}
- pub fn low_level_il(&self) -> Result<Ref<llil::RegularFunction<CoreArchitecture>>, ()> {
+ pub fn low_level_il(&self) -> Result<Ref<RegularLowLevelILFunction<CoreArchitecture>>, ()> {
unsafe {
- let llil = BNGetFunctionLowLevelIL(self.handle);
-
- if llil.is_null() {
- return Err(());
+ let llil_ptr = BNGetFunctionLowLevelIL(self.handle);
+ match llil_ptr.is_null() {
+ false => Ok(RegularLowLevelILFunction::ref_from_raw(
+ self.arch(),
+ llil_ptr,
+ )),
+ true => Err(()),
}
-
- Ok(llil::RegularFunction::ref_from_raw(self.arch(), llil))
}
}
pub fn low_level_il_if_available(
&self,
- ) -> Option<Ref<llil::RegularFunction<CoreArchitecture>>> {
- let llil = unsafe { BNGetFunctionLowLevelILIfAvailable(self.handle) };
- (!llil.is_null()).then(|| unsafe { llil::RegularFunction::ref_from_raw(self.arch(), llil) })
+ ) -> Option<Ref<RegularLowLevelILFunction<CoreArchitecture>>> {
+ let llil_ptr = unsafe { BNGetFunctionLowLevelILIfAvailable(self.handle) };
+ match llil_ptr.is_null() {
+ false => {
+ Some(unsafe { RegularLowLevelILFunction::ref_from_raw(self.arch(), llil_ptr) })
+ }
+ true => None,
+ }
}
- pub fn lifted_il(&self) -> Result<Ref<llil::LiftedFunction<CoreArchitecture>>, ()> {
+ pub fn lifted_il(&self) -> Result<Ref<LiftedILFunction<CoreArchitecture>>, ()> {
unsafe {
- let llil = BNGetFunctionLiftedIL(self.handle);
-
- if llil.is_null() {
- return Err(());
+ let llil_ptr = BNGetFunctionLiftedIL(self.handle);
+ match llil_ptr.is_null() {
+ false => Ok(LiftedILFunction::ref_from_raw(self.arch(), llil_ptr)),
+ true => Err(()),
}
-
- Ok(llil::LiftedFunction::ref_from_raw(self.arch(), llil))
}
}
- pub fn lifted_il_if_available(&self) -> Option<Ref<llil::LiftedFunction<CoreArchitecture>>> {
- let llil = unsafe { BNGetFunctionLiftedILIfAvailable(self.handle) };
- (!llil.is_null()).then(|| unsafe { llil::LiftedFunction::ref_from_raw(self.arch(), llil) })
+ pub fn lifted_il_if_available(&self) -> Option<Ref<LiftedILFunction<CoreArchitecture>>> {
+ let llil_ptr = unsafe { BNGetFunctionLiftedILIfAvailable(self.handle) };
+ match llil_ptr.is_null() {
+ false => Some(unsafe { LiftedILFunction::ref_from_raw(self.arch(), llil_ptr) }),
+ true => None,
+ }
}
pub fn return_type(&self) -> Conf<Ref<Type>> {
- let result = unsafe { BNGetFunctionReturnType(self.handle) };
-
- Conf::new(
- unsafe { Type::ref_from_raw(result.type_) },
- result.confidence,
- )
+ let raw_return_type = unsafe { BNGetFunctionReturnType(self.handle) };
+ Conf::<Ref<Type>>::from_owned_raw(raw_return_type)
}
pub fn set_auto_return_type<'a, C>(&self, return_type: C)
where
C: Into<Conf<&'a Type>>,
{
- let return_type: Conf<&Type> = return_type.into();
- unsafe {
- BNSetAutoFunctionReturnType(
- self.handle,
- &mut BNTypeWithConfidence {
- type_: return_type.contents.handle,
- confidence: return_type.confidence,
- },
- )
- }
+ let mut raw_return_type = Conf::<&Type>::into_raw(return_type.into());
+ unsafe { BNSetAutoFunctionReturnType(self.handle, &mut raw_return_type) }
}
pub fn set_user_return_type<'a, C>(&self, return_type: C)
where
C: Into<Conf<&'a Type>>,
{
- let return_type: Conf<&Type> = return_type.into();
- unsafe {
- BNSetUserFunctionReturnType(
- self.handle,
- &mut BNTypeWithConfidence {
- type_: return_type.contents.handle,
- confidence: return_type.confidence,
- },
- )
- }
+ let mut raw_return_type = Conf::<&Type>::into_raw(return_type.into());
+ unsafe { BNSetUserFunctionReturnType(self.handle, &mut raw_return_type) }
}
pub fn function_type(&self) -> Ref<Type> {
@@ -512,7 +594,7 @@ impl Function {
unsafe { BNSetFunctionAutoType(self.handle, t.handle) }
}
- pub fn stack_layout(&self) -> Array<NamedTypedVariable> {
+ pub fn stack_layout(&self) -> Array<NamedVariableWithType> {
let mut count = 0;
unsafe {
let variables = BNGetStackLayout(self.handle, &mut count);
@@ -547,7 +629,7 @@ impl Function {
pub fn call_stack_adjustment(&self, addr: u64, arch: Option<CoreArchitecture>) -> Conf<i64> {
let arch = arch.unwrap_or_else(|| self.arch());
- let result = unsafe { BNGetCallStackAdjustment(self.handle, arch.0, addr) };
+ let result = unsafe { BNGetCallStackAdjustment(self.handle, arch.handle, addr) };
result.into()
}
@@ -564,7 +646,7 @@ impl Function {
unsafe {
BNSetUserCallStackAdjustment(
self.handle,
- arch.0,
+ arch.handle,
addr,
adjust.contents,
adjust.confidence,
@@ -585,7 +667,7 @@ impl Function {
unsafe {
BNSetAutoCallStackAdjustment(
self.handle,
- arch.0,
+ arch.handle,
addr,
adjust.contents,
adjust.confidence,
@@ -599,9 +681,11 @@ impl Function {
arch: Option<CoreArchitecture>,
) -> Option<Conf<Ref<Type>>> {
let arch = arch.unwrap_or_else(|| self.arch());
- let result = unsafe { BNGetCallTypeAdjustment(self.handle, arch.0, addr) };
- (!result.type_.is_null())
- .then(|| unsafe { Conf::new(Type::ref_from_raw(result.type_), result.confidence) })
+ let result = unsafe { BNGetCallTypeAdjustment(self.handle, arch.handle, addr) };
+ match result.type_.is_null() {
+ false => Some(Conf::<Ref<Type>>::from_owned_raw(result)),
+ true => None,
+ }
}
/// Sets or removes the call type override at a call site to the given type.
@@ -628,8 +712,8 @@ impl Function {
let adjust_ptr = adjust_type
.as_mut()
.map(|x| x as *mut _)
- .unwrap_or(core::ptr::null_mut());
- unsafe { BNSetUserCallTypeAdjustment(self.handle, arch.0, addr, adjust_ptr) }
+ .unwrap_or(std::ptr::null_mut());
+ unsafe { BNSetUserCallTypeAdjustment(self.handle, arch.handle, addr, adjust_ptr) }
}
pub fn set_auto_call_type_adjustment<'a, I>(
@@ -645,7 +729,7 @@ impl Function {
unsafe {
BNSetAutoCallTypeAdjustment(
self.handle,
- arch.0,
+ arch.handle,
addr,
&mut BNTypeWithConfidence {
type_: adjust_type.contents.handle,
@@ -659,13 +743,13 @@ impl Function {
&self,
addr: u64,
arch: Option<CoreArchitecture>,
- ) -> Array<RegisterStackAdjustment<CoreArchitecture>> {
+ ) -> Array<RegisterStackAdjustment> {
let arch = arch.unwrap_or_else(|| self.arch());
let mut count = 0;
let adjust =
- unsafe { BNGetCallRegisterStackAdjustment(self.handle, arch.0, addr, &mut count) };
+ unsafe { BNGetCallRegisterStackAdjustment(self.handle, arch.handle, addr, &mut count) };
assert!(!adjust.is_null());
- unsafe { Array::new(adjust, count, arch.handle()) }
+ unsafe { Array::new(adjust, count, ()) }
}
pub fn set_user_call_reg_stack_adjustment<I>(
@@ -674,18 +758,18 @@ impl Function {
adjust: I,
arch: Option<CoreArchitecture>,
) where
- I: IntoIterator<Item = RegisterStackAdjustment<CoreArchitecture>>,
+ I: IntoIterator<Item = RegisterStackAdjustment>,
{
let arch = arch.unwrap_or_else(|| self.arch());
- let mut adjust_buf: Box<[BNRegisterStackAdjustment]> =
- adjust.into_iter().map(|adjust| adjust.into_raw()).collect();
+ let adjustments: Vec<BNRegisterStackAdjustment> =
+ adjust.into_iter().map(Into::into).collect();
unsafe {
BNSetUserCallRegisterStackAdjustment(
self.handle,
- arch.0,
+ arch.handle,
addr,
- adjust_buf.as_mut_ptr(),
- adjust_buf.len(),
+ adjustments.as_ptr() as *mut _,
+ adjustments.len(),
)
}
}
@@ -696,19 +780,18 @@ impl Function {
adjust: I,
arch: Option<CoreArchitecture>,
) where
- I: IntoIterator<Item = RegisterStackAdjustment<CoreArchitecture>>,
+ I: IntoIterator<Item = RegisterStackAdjustment>,
{
let arch = arch.unwrap_or_else(|| self.arch());
- let mut adjust_buf: Box<[BNRegisterStackAdjustment]> =
- adjust.into_iter().map(|reg| reg.into_raw()).collect();
-
+ let adjustments: Vec<BNRegisterStackAdjustment> =
+ adjust.into_iter().map(Into::into).collect();
unsafe {
BNSetAutoCallRegisterStackAdjustment(
self.handle,
- arch.0,
+ arch.handle,
addr,
- adjust_buf.as_mut_ptr(),
- adjust_buf.len(),
+ adjustments.as_ptr() as *mut _,
+ adjustments.len(),
)
}
}
@@ -718,17 +801,17 @@ impl Function {
addr: u64,
reg_stack_id: u32,
arch: Option<CoreArchitecture>,
- ) -> RegisterStackAdjustment<CoreArchitecture> {
+ ) -> RegisterStackAdjustment {
let arch = arch.unwrap_or_else(|| self.arch());
let adjust = unsafe {
BNGetCallRegisterStackAdjustmentForRegisterStack(
self.handle,
- arch.0,
+ arch.handle,
addr,
reg_stack_id,
)
};
- unsafe { RegisterStackAdjustment::from_raw(adjust, arch) }
+ RegisterStackAdjustment::from(adjust)
}
pub fn set_user_call_reg_stack_adjustment_for_reg_stack<I>(
@@ -745,7 +828,7 @@ impl Function {
unsafe {
BNSetUserCallRegisterStackAdjustmentForRegisterStack(
self.handle,
- arch.0,
+ arch.handle,
addr,
reg_stack_id,
adjust.contents,
@@ -768,7 +851,7 @@ impl Function {
unsafe {
BNSetAutoCallRegisterStackAdjustmentForRegisterStack(
self.handle,
- arch.0,
+ arch.handle,
addr,
reg_stack_id,
adjust.contents,
@@ -777,45 +860,42 @@ impl Function {
}
}
- pub fn reg_stack_adjustments(&self) -> Array<RegisterStackAdjustment<CoreArchitecture>> {
+ pub fn reg_stack_adjustments(&self) -> Array<RegisterStackAdjustment> {
let mut count = 0;
let adjust = unsafe { BNGetFunctionRegisterStackAdjustments(self.handle, &mut count) };
assert!(!adjust.is_null());
- unsafe { Array::new(adjust, count, self.arch().handle()) }
+ unsafe { Array::new(adjust, count, ()) }
}
- pub fn set_user_reg_stack_adjustments<I, A>(&self, values: I)
+ pub fn set_user_reg_stack_adjustments<I>(&self, values: I)
where
- I: IntoIterator<Item = RegisterStackAdjustment<A>>,
- A: Architecture,
+ I: IntoIterator<Item = RegisterStackAdjustment>,
{
- let mut values: Box<[BNRegisterStackAdjustment]> =
- values.into_iter().map(|r| r.into_raw()).collect();
+ let values: Vec<BNRegisterStackAdjustment> = values.into_iter().map(Into::into).collect();
unsafe {
BNSetUserFunctionRegisterStackAdjustments(
self.handle,
- values.as_mut_ptr(),
+ values.as_ptr() as *mut _,
values.len(),
)
}
}
- pub fn set_auto_reg_stack_adjustments<I, A>(&self, values: I)
+ pub fn set_auto_reg_stack_adjustments<I>(&self, values: I)
where
- I: IntoIterator<Item = RegisterStackAdjustment<A>>,
- A: Architecture,
+ I: IntoIterator<Item = RegisterStackAdjustment>,
{
- let mut values: Box<[BNRegisterStackAdjustment]> =
- values.into_iter().map(|r| r.into_raw()).collect();
+ let values: Vec<BNRegisterStackAdjustment> = values.into_iter().map(Into::into).collect();
unsafe {
BNSetAutoFunctionRegisterStackAdjustments(
self.handle,
- values.as_mut_ptr(),
+ values.as_ptr() as *mut _,
values.len(),
)
}
}
+ // TODO: Turn this into an actual type?
/// List of function variables: including name, variable and type
pub fn variables(&self) -> Array<(&str, Variable, &Type)> {
let mut count = 0;
@@ -833,17 +913,12 @@ impl Function {
pub fn parameter_variables(&self) -> Conf<Vec<Variable>> {
unsafe {
- let mut variables = BNGetFunctionParameterVariables(self.handle);
- let mut result = Vec::with_capacity(variables.count);
- let confidence = variables.confidence;
- let vars = std::slice::from_raw_parts(variables.vars, variables.count);
-
- for var in vars.iter().take(variables.count) {
- result.push(Variable::from_raw(*var));
- }
-
- BNFreeParameterVariables(&mut variables);
- Conf::new(result, confidence)
+ let mut raw_variables = BNGetFunctionParameterVariables(self.handle);
+ let raw_var_list = std::slice::from_raw_parts(raw_variables.vars, raw_variables.count);
+ let variables: Vec<Variable> = raw_var_list.iter().map(Into::into).collect();
+ let confidence = raw_variables.confidence;
+ BNFreeParameterVariables(&mut raw_variables);
+ Conf::new(variables, confidence)
}
}
@@ -851,12 +926,12 @@ impl Function {
where
I: IntoIterator<Item = Variable>,
{
- let mut vars: Box<[BNVariable]> = values.into_iter().map(|var| var.raw()).collect();
+ let vars: Vec<BNVariable> = values.into_iter().map(Into::into).collect();
unsafe {
BNSetUserFunctionParameterVariables(
self.handle,
&mut BNParameterVariablesWithConfidence {
- vars: vars.as_mut_ptr(),
+ vars: vars.as_ptr() as *mut _,
count: vars.len(),
confidence,
},
@@ -868,12 +943,12 @@ impl Function {
where
I: IntoIterator<Item = Variable>,
{
- let mut vars: Box<[BNVariable]> = values.into_iter().map(|var| var.raw()).collect();
+ let vars: Vec<BNVariable> = values.into_iter().map(Into::into).collect();
unsafe {
BNSetAutoFunctionParameterVariables(
self.handle,
&mut BNParameterVariablesWithConfidence {
- vars: vars.as_mut_ptr(),
+ vars: vars.as_ptr() as *mut _,
count: vars.len(),
confidence,
},
@@ -889,9 +964,10 @@ impl Function {
arch: Option<CoreArchitecture>,
) -> RegisterValue {
let arch = arch.unwrap_or_else(|| self.arch());
- let func_type = func_type.map(|f| f.handle).unwrap_or(core::ptr::null_mut());
- let value =
- unsafe { BNGetParameterValueAtInstruction(self.handle, arch.0, addr, func_type, i) };
+ let func_type = func_type.map(|f| f.handle).unwrap_or(std::ptr::null_mut());
+ let value = unsafe {
+ BNGetParameterValueAtInstruction(self.handle, arch.handle, addr, func_type, i)
+ };
value.into()
}
@@ -912,11 +988,7 @@ impl Function {
BNApplyImportedTypes(
self.handle,
sym.handle,
- if let Some(t) = t {
- t.handle
- } else {
- core::ptr::null_mut()
- },
+ t.map(|t| t.handle).unwrap_or(std::ptr::null_mut()),
);
}
}
@@ -972,15 +1044,7 @@ impl Function {
C: Into<Conf<bool>>,
{
let value: Conf<bool> = value.into();
- unsafe {
- BNSetAutoFunctionInlinedDuringAnalysis(
- self.handle,
- BNBoolWithConfidence {
- value: value.contents,
- confidence: value.confidence,
- },
- )
- }
+ unsafe { BNSetAutoFunctionInlinedDuringAnalysis(self.handle, value.into()) }
}
pub fn set_user_inline_during_analysis<C>(&self, value: C)
@@ -988,15 +1052,7 @@ impl Function {
C: Into<Conf<bool>>,
{
let value: Conf<bool> = value.into();
- unsafe {
- BNSetUserFunctionInlinedDuringAnalysis(
- self.handle,
- BNBoolWithConfidence {
- value: value.contents,
- confidence: value.confidence,
- },
- )
- }
+ unsafe { BNSetUserFunctionInlinedDuringAnalysis(self.handle, value.into()) }
}
pub fn analysis_performance_info(&self) -> Array<PerformanceInfo> {
@@ -1024,12 +1080,18 @@ impl Function {
/// # Example
///
/// ```no_run
- /// # use binaryninja::binaryview::{BinaryView, BinaryViewExt};
+ /// # use binaryninja::binary_view::{BinaryView, BinaryViewExt};
/// # use binaryninja::function::Function;
/// # let fun: Function = todo!();
/// # let bv: BinaryView = todo!();
/// let important = bv.create_tag_type("Important", "⚠️");
- /// fun.add_tag(&important, "I think this is the main function", None, false, None);
+ /// fun.add_tag(
+ /// &important,
+ /// "I think this is the main function",
+ /// None,
+ /// false,
+ /// None,
+ /// );
/// let crash = bv.create_tag_type("Crashes", "🎯");
/// fun.add_tag(&crash, "Nullpointer dereference", Some(0x1337), false, None);
/// ```
@@ -1045,15 +1107,19 @@ impl Function {
// Create tag
let tag = Tag::new(tag_type, data);
- let binaryview = unsafe { BinaryView::from_raw(BNGetFunctionData(self.handle)) };
+ let binaryview = unsafe { BinaryView::ref_from_raw(BNGetFunctionData(self.handle)) };
unsafe { BNAddTag(binaryview.handle, tag.handle, user) };
unsafe {
match (user, addr) {
(false, None) => BNAddAutoFunctionTag(self.handle, tag.handle),
- (false, Some(addr)) => BNAddAutoAddressTag(self.handle, arch.0, addr, tag.handle),
+ (false, Some(addr)) => {
+ BNAddAutoAddressTag(self.handle, arch.handle, addr, tag.handle)
+ }
(true, None) => BNAddUserFunctionTag(self.handle, tag.handle),
- (true, Some(addr)) => BNAddUserAddressTag(self.handle, arch.0, addr, tag.handle),
+ (true, Some(addr)) => {
+ BNAddUserAddressTag(self.handle, arch.handle, addr, tag.handle)
+ }
}
}
}
@@ -1075,10 +1141,12 @@ impl Function {
match (user, addr) {
(false, None) => BNRemoveAutoFunctionTag(self.handle, tag.handle),
(false, Some(addr)) => {
- BNRemoveAutoAddressTag(self.handle, arch.0, addr, tag.handle)
+ BNRemoveAutoAddressTag(self.handle, arch.handle, addr, tag.handle)
}
(true, None) => BNRemoveUserFunctionTag(self.handle, tag.handle),
- (true, Some(addr)) => BNRemoveUserAddressTag(self.handle, arch.0, addr, tag.handle),
+ (true, Some(addr)) => {
+ BNRemoveUserAddressTag(self.handle, arch.handle, addr, tag.handle)
+ }
}
}
}
@@ -1101,11 +1169,11 @@ impl Function {
match (user, addr) {
(false, None) => BNRemoveAutoFunctionTagsOfType(self.handle, tag_type.handle),
(false, Some(addr)) => {
- BNRemoveAutoAddressTagsOfType(self.handle, arch.0, addr, tag_type.handle)
+ BNRemoveAutoAddressTagsOfType(self.handle, arch.handle, addr, tag_type.handle)
}
(true, None) => BNRemoveUserFunctionTagsOfType(self.handle, tag_type.handle),
(true, Some(addr)) => {
- BNRemoveUserAddressTagsOfType(self.handle, arch.0, addr, tag_type.handle)
+ BNRemoveUserAddressTagsOfType(self.handle, arch.handle, addr, tag_type.handle)
}
}
}
@@ -1129,7 +1197,7 @@ impl Function {
/// ```
pub fn add_user_code_ref(&self, from_addr: u64, to_addr: u64, arch: Option<CoreArchitecture>) {
let arch = arch.unwrap_or_else(|| self.arch());
- unsafe { BNAddUserCodeReference(self.handle, arch.0, from_addr, to_addr) }
+ unsafe { BNAddUserCodeReference(self.handle, arch.handle, from_addr, to_addr) }
}
/// Removes a user-defined cross-reference.
@@ -1154,7 +1222,7 @@ impl Function {
arch: Option<CoreArchitecture>,
) {
let arch = arch.unwrap_or_else(|| self.arch());
- unsafe { BNRemoveUserCodeReference(self.handle, arch.0, from_addr, to_addr) }
+ unsafe { BNRemoveUserCodeReference(self.handle, arch.handle, from_addr, to_addr) }
}
/// Places a user-defined type cross-reference from the instruction at
@@ -1170,17 +1238,18 @@ impl Function {
/// ```no_run
/// # use binaryninja::function::Function;
/// # let fun: Function = todo!();
- /// fun.add_user_type_ref(0x1337, &"A".into(), None);
+ /// fun.add_user_type_ref(0x1337, "A", None);
/// ```
- pub fn add_user_type_ref(
+ pub fn add_user_type_ref<T: Into<QualifiedName>>(
&self,
from_addr: u64,
- name: &QualifiedName,
+ name: T,
arch: Option<CoreArchitecture>,
) {
let arch = arch.unwrap_or_else(|| self.arch());
- let name_ptr = &name.0 as *const BNQualifiedName as *mut _;
- unsafe { BNAddUserTypeReference(self.handle, arch.0, from_addr, name_ptr) }
+ let mut raw_name = QualifiedName::into_raw(name.into());
+ unsafe { BNAddUserTypeReference(self.handle, arch.handle, from_addr, &mut raw_name) };
+ QualifiedName::free_raw(raw_name);
}
/// Removes a user-defined type cross-reference.
@@ -1195,17 +1264,18 @@ impl Function {
/// ```no_run
/// # use binaryninja::function::Function;
/// # let fun: Function = todo!();
- /// fun.remove_user_type_ref(0x1337, &"A".into(), None);
+ /// fun.remove_user_type_ref(0x1337, "A", None);
/// ```
- pub fn remove_user_type_ref(
+ pub fn remove_user_type_ref<T: Into<QualifiedName>>(
&self,
from_addr: u64,
- name: &QualifiedName,
+ name: T,
arch: Option<CoreArchitecture>,
) {
let arch = arch.unwrap_or_else(|| self.arch());
- let name_ptr = &name.0 as *const BNQualifiedName as *mut _;
- unsafe { BNRemoveUserTypeReference(self.handle, arch.0, from_addr, name_ptr) }
+ let mut raw_name = QualifiedName::into_raw(name.into());
+ unsafe { BNRemoveUserTypeReference(self.handle, arch.handle, from_addr, &mut raw_name) };
+ QualifiedName::free_raw(raw_name);
}
/// Places a user-defined type field cross-reference from the
@@ -1223,22 +1293,30 @@ impl Function {
/// ```no_run
/// # use binaryninja::function::Function;
/// # let fun: Function = todo!();
- /// fun.add_user_type_field_ref(0x1337, &"A".into(), 0x8, None, None);
+ /// fun.add_user_type_field_ref(0x1337, "A", 0x8, None, None);
/// ```
- pub fn add_user_type_field_ref(
+ pub fn add_user_type_field_ref<T: Into<QualifiedName>>(
&self,
from_addr: u64,
- name: &QualifiedName,
+ name: T,
offset: u64,
arch: Option<CoreArchitecture>,
size: Option<usize>,
) {
let size = size.unwrap_or(0);
let arch = arch.unwrap_or_else(|| self.arch());
- let name_ptr = &name.0 as *const _ as *mut _;
+ let mut raw_name = QualifiedName::into_raw(name.into());
unsafe {
- BNAddUserTypeFieldReference(self.handle, arch.0, from_addr, name_ptr, offset, size)
- }
+ BNAddUserTypeFieldReference(
+ self.handle,
+ arch.handle,
+ from_addr,
+ &mut raw_name,
+ offset,
+ size,
+ )
+ };
+ QualifiedName::free_raw(raw_name);
}
/// Removes a user-defined type field cross-reference.
@@ -1255,22 +1333,30 @@ impl Function {
/// ```no_run
/// # use binaryninja::function::Function;
/// # let fun: Function = todo!();
- /// fun.remove_user_type_field_ref(0x1337, &"A".into(), 0x8, None, None);
+ /// fun.remove_user_type_field_ref(0x1337, "A", 0x8, None, None);
/// ```
- pub fn remove_user_type_field_ref(
+ pub fn remove_user_type_field_ref<T: Into<QualifiedName>>(
&self,
from_addr: u64,
- name: &QualifiedName,
+ name: T,
offset: u64,
arch: Option<CoreArchitecture>,
size: Option<usize>,
) {
let size = size.unwrap_or(0);
let arch = arch.unwrap_or_else(|| self.arch());
- let name_ptr = &name.0 as *const _ as *mut _;
+ let mut raw_name = QualifiedName::into_raw(name.into());
unsafe {
- BNRemoveUserTypeFieldReference(self.handle, arch.0, from_addr, name_ptr, offset, size)
+ BNRemoveUserTypeFieldReference(
+ self.handle,
+ arch.handle,
+ from_addr,
+ &mut raw_name,
+ offset,
+ size,
+ )
}
+ QualifiedName::free_raw(raw_name);
}
pub fn constant_data(
@@ -1280,9 +1366,11 @@ impl Function {
size: Option<usize>,
) -> (DataBuffer, BuiltinType) {
let size = size.unwrap_or(0);
- let state_raw = state.into_raw_value();
+ // TODO: Adjust `BuiltinType`?
let mut builtin_type = BuiltinType::BuiltinNone;
- let buffer = DataBuffer::from_raw(unsafe { BNGetConstantData(self.handle, state_raw, value, size, &mut builtin_type) });
+ let buffer = DataBuffer::from_raw(unsafe {
+ BNGetConstantData(self.handle, state, value, size, &mut builtin_type)
+ });
(buffer, builtin_type)
}
@@ -1293,8 +1381,9 @@ impl Function {
) -> Array<ConstantReference> {
let arch = arch.unwrap_or_else(|| self.arch());
let mut count = 0;
- let refs =
- unsafe { BNGetConstantsReferencedByInstruction(self.handle, arch.0, addr, &mut count) };
+ let refs = unsafe {
+ BNGetConstantsReferencedByInstruction(self.handle, arch.handle, addr, &mut count)
+ };
assert!(!refs.is_null());
unsafe { Array::new(refs, count, ()) }
}
@@ -1307,7 +1396,12 @@ impl Function {
let arch = arch.unwrap_or_else(|| self.arch());
let mut count = 0;
let refs = unsafe {
- BNGetConstantsReferencedByInstructionIfAvailable(self.handle, arch.0, addr, &mut count)
+ BNGetConstantsReferencedByInstructionIfAvailable(
+ self.handle,
+ arch.handle,
+ addr,
+ &mut count,
+ )
};
assert!(!refs.is_null());
unsafe { Array::new(refs, count, ()) }
@@ -1320,12 +1414,12 @@ impl Function {
pub fn function_tags(&self, auto: Option<bool>, tag_type: Option<&str>) -> Array<Tag> {
let mut count = 0;
- let tag_type = tag_type.map(|tag_type| self.view().get_tag_type(tag_type));
+ let tag_type = tag_type.map(|tag_type| self.view().tag_type_by_name(tag_type));
let tags = unsafe {
match (tag_type, auto) {
// received a tag_type, BinaryView found none
- (Some(None), _) => return Array::new(core::ptr::null_mut(), 0, ()),
+ (Some(None), _) => return Array::new(std::ptr::null_mut(), 0, ()),
// with tag_type
(Some(Some(tag_type)), None) => {
@@ -1368,9 +1462,13 @@ impl Function {
let mut count = 0;
let tags = match auto {
- None => unsafe { BNGetAddressTags(self.handle, arch.0, addr, &mut count) },
- Some(true) => unsafe { BNGetAutoAddressTags(self.handle, arch.0, addr, &mut count) },
- Some(false) => unsafe { BNGetUserAddressTags(self.handle, arch.0, addr, &mut count) },
+ None => unsafe { BNGetAddressTags(self.handle, arch.handle, addr, &mut count) },
+ Some(true) => unsafe {
+ BNGetAutoAddressTags(self.handle, arch.handle, addr, &mut count)
+ },
+ Some(false) => unsafe {
+ BNGetUserAddressTags(self.handle, arch.handle, addr, &mut count)
+ },
};
assert!(!tags.is_null());
unsafe { Array::new(tags, count, ()) }
@@ -1391,13 +1489,31 @@ impl Function {
let tags = match auto {
None => unsafe {
- BNGetAddressTagsInRange(self.handle, arch.0, range.start, range.end, &mut count)
+ BNGetAddressTagsInRange(
+ self.handle,
+ arch.handle,
+ range.start,
+ range.end,
+ &mut count,
+ )
},
Some(true) => unsafe {
- BNGetAutoAddressTagsInRange(self.handle, arch.0, range.start, range.end, &mut count)
+ BNGetAutoAddressTagsInRange(
+ self.handle,
+ arch.handle,
+ range.start,
+ range.end,
+ &mut count,
+ )
},
Some(false) => unsafe {
- BNGetUserAddressTagsInRange(self.handle, arch.0, range.start, range.end, &mut count)
+ BNGetUserAddressTagsInRange(
+ self.handle,
+ arch.handle,
+ range.start,
+ range.end,
+ &mut count,
+ )
},
};
assert!(!tags.is_null());
@@ -1425,13 +1541,13 @@ impl Function {
.into_iter()
.map(|address| BNArchitectureAndAddress {
address,
- arch: arch.0,
+ arch: arch.handle,
})
.collect();
unsafe {
BNSetUserIndirectBranches(
self.handle,
- arch.0,
+ arch.handle,
source,
branches.as_mut_ptr(),
branches.len(),
@@ -1452,13 +1568,13 @@ impl Function {
.into_iter()
.map(|address| BNArchitectureAndAddress {
address,
- arch: arch.0,
+ arch: arch.handle,
})
.collect();
unsafe {
BNSetAutoIndirectBranches(
self.handle,
- arch.0,
+ arch.handle,
source,
branches.as_mut_ptr(),
branches.len(),
@@ -1474,7 +1590,8 @@ impl Function {
) -> Array<IndirectBranchInfo> {
let arch = arch.unwrap_or_else(|| self.arch());
let mut count = 0;
- let branches = unsafe { BNGetIndirectBranchesAt(self.handle, arch.0, addr, &mut count) };
+ let branches =
+ unsafe { BNGetIndirectBranchesAt(self.handle, arch.handle, addr, &mut count) };
assert!(!branches.is_null());
unsafe { Array::new(branches, count, ()) }
}
@@ -1486,8 +1603,8 @@ impl Function {
/// ```
pub fn instr_highlight(&self, addr: u64, arch: Option<CoreArchitecture>) -> HighlightColor {
let arch = arch.unwrap_or_else(|| self.arch());
- let color = unsafe { BNGetInstructionHighlight(self.handle, arch.0, addr) };
- HighlightColor::from_raw(color)
+ let color = unsafe { BNGetInstructionHighlight(self.handle, arch.handle, addr) };
+ HighlightColor::from(color)
}
/// Sets the highlights the instruction at the specified address with the supplied color
@@ -1504,22 +1621,24 @@ impl Function {
arch: Option<CoreArchitecture>,
) {
let arch = arch.unwrap_or_else(|| self.arch());
- let color_raw = color.into_raw();
- unsafe { BNSetAutoInstructionHighlight(self.handle, arch.0, addr, color_raw) }
+ unsafe { BNSetAutoInstructionHighlight(self.handle, arch.handle, addr, color.into()) }
}
/// Sets the highlights the instruction at the specified address with the supplied color
///
/// * `addr` - virtual address of the instruction to be highlighted
/// * `color` - Color value to use for highlighting
- /// * `arch` - (optional) Architecture of the instruction if different from self.arch
+ /// * `arch` - (optional) Architecture of the instruction, pass this if not views default arch
///
/// # Example
/// ```no_run
- /// # use binaryninja::types::HighlightColor;
- /// # let fun: binaryninja::function::Function = todo!();
- /// let color = HighlightColor::NoHighlightColor { alpha: u8::MAX };
- /// fun.set_user_instr_highlight(0x1337, color, None);
+ /// # use binaryninja::function::{HighlightColor, HighlightStandardColor};
+ /// # let function: binaryninja::function::Function = todo!();
+ /// let color = HighlightColor::StandardHighlightColor {
+ /// color: HighlightStandardColor::RedHighlightColor,
+ /// alpha: u8::MAX,
+ /// };
+ /// function.set_user_instr_highlight(0x1337, color, None);
/// ```
pub fn set_user_instr_highlight(
&self,
@@ -1528,8 +1647,7 @@ impl Function {
arch: Option<CoreArchitecture>,
) {
let arch = arch.unwrap_or_else(|| self.arch());
- let color_raw = color.into_raw();
- unsafe { BNSetUserInstructionHighlight(self.handle, arch.0, addr, color_raw) }
+ unsafe { BNSetUserInstructionHighlight(self.handle, arch.handle, addr, color.into()) }
}
/// return the address, if any, of the instruction that contains the
@@ -1541,7 +1659,7 @@ impl Function {
) -> Option<u64> {
let arch = arch.unwrap_or_else(|| self.arch());
let mut start = 0;
- unsafe { BNGetInstructionContainingAddress(self.handle, arch.0, addr, &mut start) }
+ unsafe { BNGetInstructionContainingAddress(self.handle, arch.handle, addr, &mut start) }
.then_some(start)
}
@@ -1561,7 +1679,9 @@ impl Function {
arch: Option<CoreArchitecture>,
) -> IntegerDisplayType {
let arch = arch.unwrap_or_else(|| self.arch());
- unsafe { BNGetIntegerConstantDisplayType(self.handle, arch.0, instr_addr, value, operand) }
+ unsafe {
+ BNGetIntegerConstantDisplayType(self.handle, arch.handle, instr_addr, value, operand)
+ }
}
/// Change the text display type for an integer token in the disassembly or IL views
@@ -1585,11 +1705,11 @@ impl Function {
let enum_display_typeid = enum_display_typeid.map(BnStrCompatible::into_bytes_with_nul);
let enum_display_typeid_ptr = enum_display_typeid
.map(|x| x.as_ref().as_ptr() as *const c_char)
- .unwrap_or(core::ptr::null());
+ .unwrap_or(std::ptr::null());
unsafe {
BNSetIntegerConstantDisplayType(
self.handle,
- arch.0,
+ arch.handle,
instr_addr,
value,
operand,
@@ -1618,7 +1738,7 @@ impl Function {
unsafe {
BnString::from_raw(BNGetIntegerConstantDisplayTypeEnumerationType(
self.handle,
- arch.0,
+ arch.handle,
instr_addr,
value,
operand,
@@ -1661,11 +1781,12 @@ impl Function {
pub fn register_value_at(
&self,
addr: u64,
- reg: u32,
+ reg: RegisterId,
arch: Option<CoreArchitecture>,
) -> RegisterValue {
let arch = arch.unwrap_or_else(|| self.arch());
- let register = unsafe { BNGetRegisterValueAtInstruction(self.handle, arch.0, addr, reg) };
+ let register =
+ unsafe { BNGetRegisterValueAtInstruction(self.handle, arch.handle, addr, reg.0) };
register.into()
}
@@ -1685,12 +1806,12 @@ impl Function {
pub fn register_value_after(
&self,
addr: u64,
- reg: u32,
+ reg: RegisterId,
arch: Option<CoreArchitecture>,
) -> RegisterValue {
let arch = arch.unwrap_or_else(|| self.arch());
let register =
- unsafe { BNGetRegisterValueAfterInstruction(self.handle, arch.0, addr, reg) };
+ unsafe { BNGetRegisterValueAfterInstruction(self.handle, arch.handle, addr, reg.0) };
register.into()
}
@@ -1707,7 +1828,7 @@ impl Function {
let arch = arch.unwrap_or_else(|| self.arch());
let mut count = 0;
let regs =
- unsafe { BNGetRegistersReadByInstruction(self.handle, arch.0, addr, &mut count) };
+ unsafe { BNGetRegistersReadByInstruction(self.handle, arch.handle, addr, &mut count) };
assert!(!regs.is_null());
unsafe { Array::new(regs, count, arch) }
}
@@ -1719,8 +1840,9 @@ impl Function {
) -> Array<CoreRegister> {
let arch = arch.unwrap_or_else(|| self.arch());
let mut count = 0;
- let regs =
- unsafe { BNGetRegistersWrittenByInstruction(self.handle, arch.0, addr, &mut count) };
+ let regs = unsafe {
+ BNGetRegistersWrittenByInstruction(self.handle, arch.handle, addr, &mut count)
+ };
assert!(!regs.is_null());
unsafe { Array::new(regs, count, arch) }
}
@@ -1737,7 +1859,7 @@ impl Function {
where
I: IntoIterator<Item = CoreRegister>,
{
- let mut regs: Box<[u32]> = registers.into_iter().map(|reg| reg.id()).collect();
+ let mut regs: Box<[u32]> = registers.into_iter().map(|reg| reg.id().0).collect();
let mut regs = BNRegisterSetWithConfidence {
regs: regs.as_mut_ptr(),
count: regs.len(),
@@ -1750,7 +1872,7 @@ impl Function {
where
I: IntoIterator<Item = CoreRegister>,
{
- let mut regs: Box<[u32]> = registers.into_iter().map(|reg| reg.id()).collect();
+ let mut regs: Box<[u32]> = registers.into_iter().map(|reg| reg.id().0).collect();
let mut regs = BNRegisterSetWithConfidence {
regs: regs.as_mut_ptr(),
count: regs.len(),
@@ -1767,8 +1889,9 @@ impl Function {
arch: Option<CoreArchitecture>,
) -> RegisterValue {
let arch = arch.unwrap_or_else(|| self.arch());
- let value =
- unsafe { BNGetStackContentsAtInstruction(self.handle, arch.0, addr, offset, size) };
+ let value = unsafe {
+ BNGetStackContentsAtInstruction(self.handle, arch.handle, addr, offset, size)
+ };
value.into()
}
@@ -1780,8 +1903,9 @@ impl Function {
arch: Option<CoreArchitecture>,
) -> RegisterValue {
let arch = arch.unwrap_or_else(|| self.arch());
- let value =
- unsafe { BNGetStackContentsAfterInstruction(self.handle, arch.0, addr, offset, size) };
+ let value = unsafe {
+ BNGetStackContentsAfterInstruction(self.handle, arch.handle, addr, offset, size)
+ };
value.into()
}
@@ -1792,14 +1916,20 @@ impl Function {
arch: Option<CoreArchitecture>,
) -> Option<(Variable, BnString, Conf<Ref<Type>>)> {
let arch = arch.unwrap_or_else(|| self.arch());
- let mut found_value: BNVariableNameAndType = unsafe { mem::zeroed() };
+ let mut found_value = BNVariableNameAndType::default();
let found = unsafe {
- BNGetStackVariableAtFrameOffset(self.handle, arch.0, addr, offset, &mut found_value)
+ BNGetStackVariableAtFrameOffset(
+ self.handle,
+ arch.handle,
+ addr,
+ offset,
+ &mut found_value,
+ )
};
if !found {
return None;
}
- let var = unsafe { Variable::from_raw(found_value.var) };
+ let var = Variable::from(found_value.var);
let name = unsafe { BnString::from_raw(found_value.name) };
let var_type = Conf::new(
unsafe { Type::ref_from_raw(found_value.type_) },
@@ -1816,7 +1946,7 @@ impl Function {
let arch = arch.unwrap_or_else(|| self.arch());
let mut count = 0;
let refs = unsafe {
- BNGetStackVariablesReferencedByInstruction(self.handle, arch.0, addr, &mut count)
+ BNGetStackVariablesReferencedByInstruction(self.handle, arch.handle, addr, &mut count)
};
assert!(!refs.is_null());
unsafe { Array::new(refs, count, ()) }
@@ -1832,7 +1962,7 @@ impl Function {
let refs = unsafe {
BNGetStackVariablesReferencedByInstructionIfAvailable(
self.handle,
- arch.0,
+ arch.handle,
addr,
&mut count,
)
@@ -1851,7 +1981,7 @@ impl Function {
&self,
settings: Option<&DisassemblySettings>,
) -> Array<DisassemblyTextLine> {
- let settings = settings.map(|s| s.handle).unwrap_or(core::ptr::null_mut());
+ let settings = settings.map(|s| s.handle).unwrap_or(std::ptr::null_mut());
let mut count = 0;
let lines = unsafe { BNGetFunctionTypeTokens(self.handle, settings, &mut count) };
assert!(!lines.is_null());
@@ -1860,11 +1990,12 @@ impl Function {
pub fn is_call_instruction(&self, addr: u64, arch: Option<CoreArchitecture>) -> bool {
let arch = arch.unwrap_or_else(|| self.arch());
- unsafe { BNIsCallInstruction(self.handle, arch.0, addr) }
+ unsafe { BNIsCallInstruction(self.handle, arch.handle, addr) }
}
pub fn is_variable_user_defined(&self, var: &Variable) -> bool {
- unsafe { BNIsVariableUserDefined(self.handle, &var.raw()) }
+ let raw_var = BNVariable::from(var);
+ unsafe { BNIsVariableUserDefined(self.handle, &raw_var) }
}
pub fn is_pure(&self) -> Conf<bool> {
@@ -1906,7 +2037,7 @@ impl Function {
/// Indicates that callers of this function need to be reanalyzed during the next update cycle
///
- /// * `uppdate_type` - Desired update type
+ /// * `update_type` - Desired update type
pub fn mark_caller_updates_required(&self, update_type: FunctionUpdateType) {
unsafe { BNMarkCallerUpdatesRequired(self.handle, update_type) }
}
@@ -1923,7 +2054,7 @@ impl Function {
unsafe { Array::new(vars, count, ()) }
}
- /// Merge one or more varibles in `sources` into the `target` variable. All
+ /// Merge one or more variables in `sources` into the `target` variable. All
/// variable accesses to the variables in `sources` will be rewritten to use `target`.
///
/// * `target` - target variable
@@ -1933,11 +2064,12 @@ impl Function {
target: &Variable,
sources: impl IntoIterator<Item = &'a Variable>,
) {
- let sources_raw: Box<[BNVariable]> = sources.into_iter().map(|s| s.raw()).collect();
+ let raw_target_var = BNVariable::from(target);
+ let sources_raw: Vec<BNVariable> = sources.into_iter().copied().map(Into::into).collect();
unsafe {
BNMergeVariables(
self.handle,
- &target.raw(),
+ &raw_target_var,
sources_raw.as_ptr(),
sources_raw.len(),
)
@@ -1954,20 +2086,21 @@ impl Function {
target: &Variable,
sources: impl IntoIterator<Item = &'a Variable>,
) {
- let sources_raw: Box<[BNVariable]> = sources.into_iter().map(|s| s.raw()).collect();
+ let raw_target_var = BNVariable::from(target);
+ let sources_raw: Vec<BNVariable> = sources.into_iter().copied().map(Into::into).collect();
unsafe {
BNUnmergeVariables(
self.handle,
- &target.raw(),
+ &raw_target_var,
sources_raw.as_ptr(),
sources_raw.len(),
)
}
}
- /// Splits a varible at the definition site. The given `var` must be the
+ /// Splits a variable at the definition site. The given `var` must be the
/// variable unique to the definition and should be obtained by using
- /// [mlil::MediumLevelILInstruction::get_split_var_for_definition] at the definition site.
+ /// [crate::medium_level_il::MediumLevelILInstruction::get_split_var_for_definition] at the definition site.
///
/// This function is not meant to split variables that have been previously merged. Use
/// [Function::unmerge_variables] to split previously merged variables.
@@ -1984,16 +2117,18 @@ impl Function {
///
/// * `var` - variable to split
pub fn split_variable(&self, var: &Variable) {
- unsafe { BNSplitVariable(self.handle, &var.raw()) }
+ let raw_var = BNVariable::from(var);
+ unsafe { BNSplitVariable(self.handle, &raw_var) }
}
- /// Undoes varible splitting performed with [Function::split_variable]. The given `var`
+ /// Undoes variable splitting performed with [Function::split_variable]. The given `var`
/// must be the variable unique to the definition and should be obtained by using
- /// [mlil::MediumLevelILInstruction::get_split_var_for_definition] at the definition site.
+ /// [crate::medium_level_il::MediumLevelILInstruction::get_split_var_for_definition] at the definition site.
///
/// * `var` - variable to unsplit
pub fn unsplit_variable(&self, var: &Variable) {
- unsafe { BNUnsplitVariable(self.handle, &var.raw()) }
+ let raw_var = BNVariable::from(var);
+ unsafe { BNUnsplitVariable(self.handle, &raw_var) }
}
/// Causes this function to be reanalyzed. This function does not wait for the analysis to finish.
@@ -2043,7 +2178,7 @@ impl Function {
/// 'user' functions may or may not have been created by a user through the or API. For instance the entry point
/// into a function is always created a 'user' function. 'user' functions should be considered the root of auto
/// analysis.
- pub fn auto(&self) -> bool {
+ pub fn is_auto(&self) -> bool {
unsafe { BNWasFunctionAutomaticallyDiscovered(self.handle) }
}
@@ -2061,15 +2196,15 @@ impl Function {
/// Returns a list of ReferenceSource objects corresponding to the addresses
/// in functions which reference this function
pub fn caller_sites(&self) -> Array<CodeReference> {
- self.view().get_code_refs(self.start())
+ self.view().code_refs_to_addr(self.start())
}
/// Calling convention used by the function
- pub fn calling_convention(&self) -> Option<Conf<Ref<CallingConvention<CoreArchitecture>>>> {
+ pub fn calling_convention(&self) -> Option<Conf<Ref<CoreCallingConvention>>> {
let result = unsafe { BNGetFunctionCallingConvention(self.handle) };
(!result.convention.is_null()).then(|| {
Conf::new(
- unsafe { CallingConvention::ref_from_raw(result.convention, self.arch()) },
+ unsafe { CoreCallingConvention::ref_from_raw(result.convention, self.arch()) },
result.confidence,
)
})
@@ -2078,9 +2213,9 @@ impl Function {
/// Set the User calling convention used by the function
pub fn set_user_calling_convention<'a, I>(&self, value: Option<I>)
where
- I: Into<Conf<&'a CallingConvention<CoreArchitecture>>>,
+ I: Into<Conf<&'a CoreCallingConvention>>,
{
- let mut conv_conf: BNCallingConventionWithConfidence = unsafe { mem::zeroed() };
+ let mut conv_conf = BNCallingConventionWithConfidence::default();
if let Some(value) = value {
let value = value.into();
conv_conf.convention = value.contents.handle;
@@ -2092,9 +2227,9 @@ impl Function {
/// Set the calling convention used by the function
pub fn set_auto_calling_convention<'a, I>(&self, value: Option<I>)
where
- I: Into<Conf<&'a CallingConvention<CoreArchitecture>>>,
+ I: Into<Conf<&'a CoreCallingConvention>>,
{
- let mut conv_conf: BNCallingConventionWithConfidence = unsafe { mem::zeroed() };
+ let mut conv_conf = BNCallingConventionWithConfidence::default();
if let Some(value) = value {
let value = value.into();
conv_conf.convention = value.contents.handle;
@@ -2186,7 +2321,7 @@ impl Function {
where
I: IntoIterator<Item = CoreRegister>,
{
- let mut regs: Box<[u32]> = values.into_iter().map(|reg| reg.id()).collect();
+ let mut regs: Box<[u32]> = values.into_iter().map(|reg| reg.id().0).collect();
let mut regs = BNRegisterSetWithConfidence {
regs: regs.as_mut_ptr(),
count: regs.len(),
@@ -2199,7 +2334,7 @@ impl Function {
where
I: IntoIterator<Item = CoreRegister>,
{
- let mut regs: Box<[u32]> = values.into_iter().map(|reg| reg.id()).collect();
+ let mut regs: Box<[u32]> = values.into_iter().map(|reg| reg.id().0).collect();
let mut regs = BNRegisterSetWithConfidence {
regs: regs.as_mut_ptr(),
count: regs.len(),
@@ -2217,33 +2352,42 @@ impl Function {
pub fn create_graph(
&self,
view_type: FunctionViewType,
- settings: Option<DisassemblySettings>,
+ settings: Option<&DisassemblySettings>,
) -> Ref<FlowGraph> {
- let settings_raw = settings.map(|s| s.handle).unwrap_or(core::ptr::null_mut());
- let result = unsafe { BNCreateFunctionGraph(self.handle, view_type.as_raw().0, settings_raw) };
+ let settings_raw = settings.map(|s| s.handle).unwrap_or(std::ptr::null_mut());
+ let raw_view_type = FunctionViewType::into_raw(view_type);
+ let result = unsafe { BNCreateFunctionGraph(self.handle, raw_view_type, settings_raw) };
+ FunctionViewType::free_raw(raw_view_type);
unsafe { Ref::new(FlowGraph::from_raw(result)) }
}
pub fn parent_components(&self) -> Array<Component> {
let mut count = 0;
- let result = unsafe{ BNGetFunctionParentComponents(self.view().handle, self.handle, &mut count) };
+ let result =
+ unsafe { BNGetFunctionParentComponents(self.view().handle, self.handle, &mut count) };
assert!(!result.is_null());
- unsafe{ Array::new(result, count, ()) }
+ unsafe { Array::new(result, count, ()) }
}
}
-impl fmt::Debug for Function {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(
- f,
- "<func '{}' ({}) {:x}>",
- self.symbol().full_name(),
- self.platform().name(),
- self.start()
- )
+impl Debug for Function {
+ fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
+ // TODO: I am sure there is more we should add to this.
+ f.debug_struct("Function")
+ .field("start", &self.start())
+ .field("arch", &self.arch())
+ .field("platform", &self.platform())
+ .field("symbol", &self.symbol())
+ .field("is_auto", &self.is_auto())
+ .field("tags", &self.tags().to_vec())
+ .field("comments", &self.comments().to_vec())
+ .finish()
}
}
+unsafe impl Send for Function {}
+unsafe impl Sync for Function {}
+
impl ToOwned for Function {
type Owned = Ref<Self>;
@@ -2271,11 +2415,12 @@ impl CoreArrayProvider for Function {
}
unsafe impl CoreArrayProviderInner for Function {
- unsafe fn free(raw: *mut *mut BNFunction, count: usize, _context: &()) {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeFunctionList(raw, count);
}
- unsafe fn wrap_raw<'a>(raw: &'a *mut BNFunction, context: &'a ()) -> Self::Wrapped<'a> {
- Guard::new(Function { handle: *raw }, context)
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
+ Guard::new(Self::from_raw(*raw), context)
}
}
@@ -2299,97 +2444,315 @@ impl PartialEq for Function {
}
}
-/////////////////
-// AddressRange
-
-#[repr(transparent)]
-pub struct AddressRange(pub(crate) BNAddressRange);
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct AddressRange {
+ pub start: u64,
+ pub end: u64,
+}
-impl AddressRange {
- pub fn start(&self) -> u64 {
- self.0.start
+impl From<BNAddressRange> for AddressRange {
+ fn from(raw: BNAddressRange) -> Self {
+ Self {
+ start: raw.start,
+ end: raw.end,
+ }
}
+}
- pub fn end(&self) -> u64 {
- self.0.end
+impl From<AddressRange> for BNAddressRange {
+ fn from(raw: AddressRange) -> Self {
+ Self {
+ start: raw.start,
+ end: raw.end,
+ }
}
}
impl CoreArrayProvider for AddressRange {
type Raw = BNAddressRange;
type Context = ();
- type Wrapped<'a> = &'a AddressRange;
+ type Wrapped<'a> = Self;
}
+
unsafe impl CoreArrayProviderInner for AddressRange {
unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
BNFreeAddressRanges(raw);
}
+
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
- mem::transmute(raw)
+ Self::from(*raw)
}
}
-/////////////////
-// PerformanceInfo
-
-// NOTE only exists as Array<PerformanceInfo>, cant be owned
-#[repr(transparent)]
-pub struct PerformanceInfo(BNPerformanceInfo);
+#[derive(Clone, Debug, Eq, Hash, PartialEq)]
+pub struct PerformanceInfo {
+ pub name: String,
+ pub seconds: Duration,
+}
-impl PerformanceInfo {
- pub fn name(&self) -> &str {
- unsafe { std::ffi::CStr::from_ptr(self.0.name) }
- .to_str()
- .unwrap()
+impl From<BNPerformanceInfo> for PerformanceInfo {
+ fn from(value: BNPerformanceInfo) -> Self {
+ Self {
+ name: unsafe { BnString::from_raw(value.name) }.to_string(),
+ seconds: Duration::from_secs_f64(value.seconds),
+ }
}
- pub fn seconds(&self) -> f64 {
- self.0.seconds
+}
+
+impl From<&BNPerformanceInfo> for PerformanceInfo {
+ fn from(value: &BNPerformanceInfo) -> Self {
+ Self {
+ // TODO: Name will be freed by this. FIX!
+ name: unsafe { BnString::from_raw(value.name) }.to_string(),
+ seconds: Duration::from_secs_f64(value.seconds),
+ }
}
}
impl CoreArrayProvider for PerformanceInfo {
type Raw = BNPerformanceInfo;
type Context = ();
- type Wrapped<'a> = Guard<'a, PerformanceInfo>;
+ type Wrapped<'a> = Self;
}
+
unsafe impl CoreArrayProviderInner for PerformanceInfo {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeAnalysisPerformanceInfo(raw, count);
}
- unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
- Guard::new(Self(*raw), context)
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ // TODO: Swap this to the ref version.
+ Self::from(*raw)
}
}
-/////////////////
-// Comments
+// NOTE: only exists as part of an Array, never owned
+pub struct UnresolvedIndirectBranches(u64);
-// NOTE only exists as Array<Comments>, cant be owned
-pub struct Comments {
- addr: u64,
- comment: BnString,
+impl UnresolvedIndirectBranches {
+ pub fn address(&self) -> u64 {
+ self.0
+ }
}
-impl Comments {
- pub fn address(&self) -> u64 {
- self.addr
+impl CoreArrayProvider for UnresolvedIndirectBranches {
+ type Raw = u64;
+ type Context = ();
+ type Wrapped<'a> = Self;
+}
+
+unsafe impl CoreArrayProviderInner for UnresolvedIndirectBranches {
+ unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
+ BNFreeAddressList(raw)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ Self(*raw)
+ }
+}
+
+#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
+pub struct ConstantReference {
+ pub value: i64,
+ pub size: usize,
+ pub pointer: bool,
+ pub intermediate: bool,
+}
+
+impl From<BNConstantReference> for ConstantReference {
+ fn from(value: BNConstantReference) -> Self {
+ Self {
+ value: value.value,
+ size: value.size,
+ pointer: value.pointer,
+ intermediate: value.intermediate,
+ }
}
- pub fn comment(&self) -> &str {
- self.comment.as_str()
+}
+
+impl From<ConstantReference> for BNConstantReference {
+ fn from(value: ConstantReference) -> Self {
+ Self {
+ value: value.value,
+ size: value.size,
+ pointer: value.pointer,
+ intermediate: value.intermediate,
+ }
+ }
+}
+
+impl CoreArrayProvider for ConstantReference {
+ type Raw = BNConstantReference;
+ type Context = ();
+ type Wrapped<'a> = Self;
+}
+
+unsafe impl CoreArrayProviderInner for ConstantReference {
+ unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
+ BNFreeConstantReferenceList(raw)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ Self::from(*raw)
+ }
+}
+
+#[derive(Debug, Copy, Clone)]
+pub struct RegisterStackAdjustment {
+ pub register_id: u32,
+ pub adjustment: Conf<i32>,
+}
+
+impl RegisterStackAdjustment {
+ pub fn new(register_id: u32, adjustment: impl Into<Conf<i32>>) -> Self {
+ Self {
+ register_id,
+ adjustment: adjustment.into(),
+ }
+ }
+}
+
+impl From<BNRegisterStackAdjustment> for RegisterStackAdjustment {
+ fn from(value: BNRegisterStackAdjustment) -> Self {
+ Self {
+ register_id: value.regStack,
+ adjustment: Conf::new(value.adjustment, value.confidence),
+ }
+ }
+}
+
+impl From<RegisterStackAdjustment> for BNRegisterStackAdjustment {
+ fn from(value: RegisterStackAdjustment) -> Self {
+ Self {
+ regStack: value.register_id,
+ adjustment: value.adjustment.contents,
+ confidence: value.adjustment.confidence,
+ }
+ }
+}
+
+impl CoreArrayProvider for RegisterStackAdjustment {
+ type Raw = BNRegisterStackAdjustment;
+ type Context = ();
+ type Wrapped<'a> = Self;
+}
+
+unsafe impl CoreArrayProviderInner for RegisterStackAdjustment {
+ unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
+ BNFreeRegisterStackAdjustments(raw)
}
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ Self::from(*raw)
+ }
+}
+
+#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
+pub enum HighlightColor {
+ StandardHighlightColor {
+ color: HighlightStandardColor,
+ alpha: u8,
+ },
+ MixedHighlightColor {
+ color: HighlightStandardColor,
+ mix_color: HighlightStandardColor,
+ mix: u8,
+ alpha: u8,
+ },
+ CustomHighlightColor {
+ r: u8,
+ g: u8,
+ b: u8,
+ alpha: u8,
+ },
}
-impl CoreArrayProvider for Comments {
+impl From<BNHighlightColor> for HighlightColor {
+ fn from(value: BNHighlightColor) -> Self {
+ match value.style {
+ BNHighlightColorStyle::StandardHighlightColor => Self::StandardHighlightColor {
+ color: value.color,
+ alpha: value.alpha,
+ },
+ BNHighlightColorStyle::MixedHighlightColor => Self::MixedHighlightColor {
+ color: value.color,
+ mix_color: value.mixColor,
+ mix: value.mix,
+ alpha: value.alpha,
+ },
+ BNHighlightColorStyle::CustomHighlightColor => Self::CustomHighlightColor {
+ r: value.r,
+ g: value.g,
+ b: value.b,
+ alpha: value.alpha,
+ },
+ }
+ }
+}
+
+impl From<HighlightColor> for BNHighlightColor {
+ fn from(value: HighlightColor) -> Self {
+ match value {
+ HighlightColor::StandardHighlightColor { color, alpha } => BNHighlightColor {
+ style: BNHighlightColorStyle::StandardHighlightColor,
+ color,
+ alpha,
+ ..Default::default()
+ },
+ HighlightColor::MixedHighlightColor {
+ color,
+ mix_color,
+ mix,
+ alpha,
+ } => BNHighlightColor {
+ style: BNHighlightColorStyle::MixedHighlightColor,
+ color,
+ mixColor: mix_color,
+ mix,
+ alpha,
+ ..Default::default()
+ },
+ HighlightColor::CustomHighlightColor { r, g, b, alpha } => BNHighlightColor {
+ style: BNHighlightColorStyle::CustomHighlightColor,
+ r,
+ g,
+ b,
+ alpha,
+ ..Default::default()
+ },
+ }
+ }
+}
+
+impl Default for HighlightColor {
+ fn default() -> Self {
+ Self::StandardHighlightColor {
+ color: HighlightStandardColor::NoHighlightColor,
+ alpha: 0,
+ }
+ }
+}
+
+// NOTE only exists as Array<Comments>, cant be owned
+#[derive(Clone, Debug, Hash, Eq, PartialEq)]
+pub struct Comment {
+ pub addr: u64,
+ pub comment: BnString,
+}
+
+impl CoreArrayProvider for Comment {
type Raw = u64;
type Context = Ref<Function>;
- type Wrapped<'a> = Comments;
+ type Wrapped<'a> = Comment;
}
-unsafe impl CoreArrayProviderInner for Comments {
+
+unsafe impl CoreArrayProviderInner for Comment {
unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
BNFreeAddressList(raw);
}
+
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, function: &'a Self::Context) -> Self::Wrapped<'a> {
- Comments {
+ Comment {
addr: *raw,
comment: function.comment_at(*raw),
}
diff --git a/rust/src/functionrecognizer.rs b/rust/src/function_recognizer.rs
index c63edcab..64e33806 100644
--- a/rust/src/functionrecognizer.rs
+++ b/rust/src/function_recognizer.rs
@@ -1,7 +1,7 @@
-use crate::architecture::Architecture;
-use crate::{
- architecture::CoreArchitecture, binaryview::BinaryView, function::Function, llil, mlil,
-};
+use crate::low_level_il::function::LowLevelILFunction;
+use crate::low_level_il::RegularLowLevelILFunction;
+use crate::medium_level_il::MediumLevelILFunction;
+use crate::{architecture::CoreArchitecture, binary_view::BinaryView, function::Function};
use binaryninjacore_sys::*;
use std::os::raw::c_void;
@@ -10,7 +10,7 @@ pub trait FunctionRecognizer {
&self,
_bv: &BinaryView,
_func: &Function,
- _llil: &llil::RegularFunction<CoreArchitecture>,
+ _llil: &RegularLowLevelILFunction<CoreArchitecture>,
) -> bool {
false
}
@@ -19,7 +19,7 @@ pub trait FunctionRecognizer {
&self,
_bv: &BinaryView,
_func: &Function,
- _mlil: &mlil::MediumLevelILFunction,
+ _mlil: &MediumLevelILFunction,
) -> bool {
false
}
@@ -46,16 +46,11 @@ where
where
R: 'static + FunctionRecognizer + Send + Sync,
{
- let custom_handler = unsafe { &*(ctxt as *mut R) };
- let bv = unsafe { BinaryView::from_raw(BNNewViewReference(bv)) };
- let arch = unsafe { BNGetFunctionArchitecture(func) };
- let func = unsafe { Function::from_raw(BNNewFunctionReference(func)) };
- if arch.is_null() {
- return false;
- }
- let arch = unsafe { CoreArchitecture::from_raw(arch) };
- let llil = unsafe { llil::RegularFunction::from_raw(arch, llil) };
- custom_handler.recognize_low_level_il(bv.as_ref(), func.as_ref(), &llil)
+ let context = unsafe { &*(ctxt as *mut FunctionRecognizerHandlerContext<R>) };
+ let bv = unsafe { BinaryView::from_raw(bv).to_owned() };
+ let func = unsafe { Function::from_raw(func).to_owned() };
+ let llil = unsafe { LowLevelILFunction::from_raw(func.arch(), llil).to_owned() };
+ context.recognizer.recognize_low_level_il(&bv, &func, &llil)
}
extern "C" fn cb_recognize_medium_level_il<R>(
@@ -67,14 +62,17 @@ where
where
R: 'static + FunctionRecognizer + Send + Sync,
{
- let custom_handler = unsafe { &*(ctxt as *mut R) };
- let bv = unsafe { BinaryView::from_raw(BNNewViewReference(bv)) };
- let func = unsafe { Function::from_raw(BNNewFunctionReference(func)) };
- let mlil = unsafe { mlil::MediumLevelILFunction::ref_from_raw(mlil) };
- custom_handler.recognize_medium_level_il(bv.as_ref(), func.as_ref(), &mlil)
+ let context = unsafe { &*(ctxt as *mut FunctionRecognizerHandlerContext<R>) };
+ let bv = unsafe { BinaryView::from_raw(bv).to_owned() };
+ let func = unsafe { Function::from_raw(func).to_owned() };
+ let mlil = unsafe { MediumLevelILFunction::from_raw(mlil).to_owned() };
+ context
+ .recognizer
+ .recognize_medium_level_il(&bv, &func, &mlil)
}
let recognizer = FunctionRecognizerHandlerContext { recognizer };
+ // TODO: Currently we leak `recognizer`.
let raw = Box::into_raw(Box::new(recognizer));
BNFunctionRecognizer {
context: raw as *mut _,
@@ -89,7 +87,7 @@ where
{
let mut recognizer = create_function_recognizer_registration::<R>(recognizer);
unsafe {
- BNRegisterGlobalFunctionRecognizer(&mut recognizer as *mut _);
+ BNRegisterGlobalFunctionRecognizer(&mut recognizer);
}
}
@@ -99,9 +97,6 @@ where
{
let mut recognizer = create_function_recognizer_registration::<R>(recognizer);
unsafe {
- BNRegisterArchitectureFunctionRecognizer(
- arch.handle().as_ref().0,
- &mut recognizer as *mut _,
- );
+ BNRegisterArchitectureFunctionRecognizer(arch.handle, &mut recognizer);
}
}
diff --git a/rust/src/headless.rs b/rust/src/headless.rs
index 0cc92faa..6613bf90 100644
--- a/rust/src/headless.rs
+++ b/rust/src/headless.rs
@@ -13,170 +13,295 @@
// limitations under the License.
use crate::{
- binaryview,
- rc,
- string::{BnStrCompatible, IntoJson},
+ binary_view, bundled_plugin_directory, enterprise, is_license_validated, is_main_thread,
+ license_path, set_bundled_plugin_directory, set_license, string::IntoJson,
};
+use std::io;
+use std::path::{Path, PathBuf};
+use thiserror::Error;
-use std::env;
-use std::path::PathBuf;
+use crate::enterprise::release_license;
+use crate::main_thread::{MainThreadAction, MainThreadHandler};
+use crate::rc::Ref;
+use binaryninjacore_sys::{BNInitPlugins, BNInitRepoPlugins};
+use std::sync::mpsc::Sender;
+use std::sync::Mutex;
+use std::thread::JoinHandle;
use std::time::Duration;
-#[cfg(not(target_os = "windows"))]
-fn binja_path() -> PathBuf {
- use std::ffi::{CStr, OsStr};
- use std::mem;
- use std::os::raw;
- use std::os::unix::ffi::OsStrExt;
+static MAIN_THREAD_HANDLE: Mutex<Option<JoinHandle<()>>> = Mutex::new(None);
- #[repr(C)]
- struct DlInfo {
- dli_fname: *const raw::c_char,
- dli_fbase: *mut raw::c_void,
- dli_sname: *const raw::c_char,
- dli_saddr: *mut raw::c_void,
+#[derive(Error, Debug)]
+pub enum InitializationError {
+ #[error("main thread could not be started: {0}")]
+ MainThreadNotStarted(#[from] io::Error),
+ #[error("enterprise license checkout failed: {0:?}")]
+ FailedEnterpriseCheckout(#[from] enterprise::EnterpriseCheckoutError),
+ #[error("invalid license")]
+ InvalidLicense,
+ #[error("no license could located, please see `binaryninja::set_license` for details")]
+ NoLicenseFound,
+}
+
+/// Loads plugins, core architecture, platform, etc.
+///
+/// ⚠️ Important! Must be called at the beginning of scripts. Plugins do not need to call this. ⚠️
+///
+/// You can instead call this through [`Session`].
+///
+/// If you need to customize initialization, use [`init_with_opts`] instead.
+pub fn init() -> Result<(), InitializationError> {
+ let options = InitializationOptions::default();
+ init_with_opts(options)
+}
+
+/// Unloads plugins, stops all worker threads, and closes open logs.
+///
+/// If the core was initialized using an enterprise license, that will also be freed.
+///
+/// ⚠️ Important! Must be called at the end of scripts. ⚠️
+pub fn shutdown() {
+ match crate::product().as_str() {
+ "Binary Ninja Enterprise Client" | "Binary Ninja Ultimate" => enterprise::release_license(),
+ _ => {}
}
+ unsafe { binaryninjacore_sys::BNShutdown() };
+ release_license();
+ // TODO: We might want to drop the main thread here, however that requires getting the handler ctx to drop the sender.
+}
+
+pub fn is_shutdown_requested() -> bool {
+ unsafe { binaryninjacore_sys::BNIsShutdownRequested() }
+}
- if let Ok(p) = env::var("BINJA_DIR") {
- return PathBuf::from(p);
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct InitializationOptions {
+ /// A license to override with, you can use this to make sure you initialize with a specific license.
+ pub license: Option<String>,
+ /// If you need to make sure that you do not check out a license set this to false.
+ ///
+ /// This is really only useful if you have a headless license but are using an enterprise enabled core.
+ pub checkout_license: bool,
+ /// Whether to register the default main thread handler.
+ ///
+ /// Set this to false if you have your own main thread handler.
+ pub register_main_thread_handler: bool,
+ /// How long you want to check out for.
+ pub floating_license_duration: Duration,
+ /// The bundled plugin directory to use.
+ pub bundled_plugin_directory: PathBuf,
+}
+
+impl InitializationOptions {
+ pub fn new() -> Self {
+ Self::default()
}
- extern "C" {
- fn dladdr(addr: *mut raw::c_void, info: *mut DlInfo) -> raw::c_int;
+ /// A license to override with, you can use this to make sure you initialize with a specific license.
+ ///
+ /// This takes the form of a JSON array. The string should be formed like:
+ /// ```json
+ /// [{ /* json object with license data */ }]
+ /// ```
+ pub fn with_license(mut self, license: impl Into<String>) -> Self {
+ self.license = Some(license.into());
+ self
}
- unsafe {
- let mut info: DlInfo = mem::zeroed();
+ /// If you need to make sure that you do not check out a license set this to false.
+ ///
+ /// This is really only useful if you have a headless license but are using an enterprise enabled core.
+ pub fn with_checkout_license(mut self, should_checkout: bool) -> Self {
+ self.checkout_license = should_checkout;
+ self
+ }
- if dladdr(BNSetBundledPluginDirectory as *mut _, &mut info) == 0 {
- panic!("Failed to find libbinaryninjacore path!");
- }
+ /// Whether to register the default main thread handler.
+ ///
+ /// Set this to false if you have your own main thread handler.
+ pub fn with_main_thread_handler(mut self, should_register: bool) -> Self {
+ self.register_main_thread_handler = should_register;
+ self
+ }
- if info.dli_fname.is_null() {
- panic!("Failed to find libbinaryninjacore path!");
- }
+ /// How long you want to check out for, only used if you are using a floating license.
+ pub fn with_floating_license_duration(mut self, duration: Duration) -> Self {
+ self.floating_license_duration = duration;
+ self
+ }
+}
- 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!");
+impl Default for InitializationOptions {
+ fn default() -> Self {
+ Self {
+ license: None,
+ checkout_license: true,
+ register_main_thread_handler: true,
+ floating_license_duration: Duration::from_secs(900),
+ bundled_plugin_directory: bundled_plugin_directory()
+ .expect("Failed to get bundled plugin directory"),
}
-
- path.pop();
- path
}
}
-#[cfg(target_os = "windows")]
-fn binja_path() -> PathBuf {
- PathBuf::from(env::var("PROGRAMFILES").unwrap()).join("Vector35\\BinaryNinja\\")
-}
+/// This initializes the core with the given [`InitializationOptions`].
+pub fn init_with_opts(options: InitializationOptions) -> Result<(), InitializationError> {
+ // If we are the main thread that means there is no main thread, we should register a main thread handler.
+ if options.register_main_thread_handler
+ && is_main_thread()
+ && MAIN_THREAD_HANDLE.lock().unwrap().is_none()
+ {
+ let (sender, receiver) = std::sync::mpsc::channel();
+ let main_thread = HeadlessMainThreadSender::new(sender);
-use binaryninjacore_sys::{BNInitPlugins, BNInitRepoPlugins, BNSetBundledPluginDirectory};
+ // This thread will act as our main thread.
+ let main_thread_handle = std::thread::Builder::new()
+ .name("HeadlessMainThread".to_string())
+ .spawn(move || {
+ // We must register the main thread within said thread.
+ main_thread.register();
+ while let Ok(action) = receiver.recv() {
+ action.execute();
+ }
+ })?;
+
+ // Set the static MAIN_THREAD_HANDLER so that we can close the thread on shutdown.
+ *MAIN_THREAD_HANDLE.lock().unwrap() = Some(main_thread_handle);
+ }
-/// Loads plugins, core architecture, platform, etc.
-///
-/// ⚠️ Important! Must be called at the beginning of scripts. Plugins do not need to call this. ⚠️
-///
-/// You can instead call this through [`Session`] or [`script_helper`]
-pub fn init() {
match crate::product().as_str() {
"Binary Ninja Enterprise Client" | "Binary Ninja Ultimate" => {
- crate::enterprise::checkout_license(Duration::from_secs(900)).expect("Failed to checkout license");
- },
+ if options.checkout_license {
+ // We are allowed to check out a license, so do it!
+ enterprise::checkout_license(options.floating_license_duration)?;
+ }
+ }
_ => {}
}
-
- unsafe {
- let path = binja_path().join("plugins").into_os_string();
- let path = path.into_string().unwrap();
- BNSetBundledPluginDirectory(path.as_str().into_bytes_with_nul().as_ptr() as *mut _);
+ if let Some(license) = options.license {
+ // We were given a license override, use it!
+ set_license(Some(license));
+ }
+
+ set_bundled_plugin_directory(options.bundled_plugin_directory);
+
+ unsafe {
BNInitPlugins(true);
BNInitRepoPlugins();
}
+
+ if !is_license_validated() {
+ // Unfortunately you must have a valid license to use Binary Ninja.
+ Err(InitializationError::InvalidLicense)
+ } else {
+ Ok(())
+ }
}
-/// Unloads plugins, stops all worker threads, and closes open logs
-///
-/// ⚠️ Important! Must be called at the end of scripts. ⚠️
-pub fn shutdown() {
- match crate::product().as_str() {
- "Binary Ninja Enterprise Client" | "Binary Ninja Ultimate" => {
- crate::enterprise::release_license()
- },
- _ => {}
+#[derive(Debug)]
+pub struct HeadlessMainThreadSender {
+ sender: Sender<Ref<MainThreadAction>>,
+}
+
+impl HeadlessMainThreadSender {
+ pub fn new(sender: Sender<Ref<MainThreadAction>>) -> Self {
+ Self { sender }
}
-
- unsafe { binaryninjacore_sys::BNShutdown() };
}
-pub fn is_shutdown_requested() -> bool {
- unsafe { binaryninjacore_sys::BNIsShutdownRequested() }
+impl MainThreadHandler for HeadlessMainThreadSender {
+ fn add_action(&self, action: Ref<MainThreadAction>) {
+ self.sender
+ .send(action)
+ .expect("Failed to send action to main thread");
+ }
+}
+
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
+pub enum LicenseLocation {
+ /// The license used when initializing will be the environment variable `BN_LICENSE`.
+ EnvironmentVariable,
+ /// The license used when initializing will be the file in the Binary Ninja user directory.
+ File,
}
-/// Prelued-postlued helper function (calls [`init`] and [`shutdown`] for you)
-/// ```no_run
-/// # use binaryninja::binaryview::BinaryViewExt;
-/// binaryninja::headless::script_helper(|| {
-/// 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()) {
- init();
- func();
- shutdown();
+/// Attempts to identify the license location type, this follows the same order as core initialization.
+///
+/// This is useful if you want to know whether the core will use your license. If this returns `None`
+/// you should look setting the `BN_LICENSE` environment variable, or calling [`set_license`].
+pub fn license_location() -> Option<LicenseLocation> {
+ match std::env::var("BN_LICENSE") {
+ Ok(_) => Some(LicenseLocation::EnvironmentVariable),
+ Err(_) => {
+ // Check the license_path to see if a file is there.
+ if license_path().exists() {
+ Some(LicenseLocation::File)
+ } else {
+ None
+ }
+ }
+ }
}
/// Wrapper for [`init`] and [`shutdown`]. Instantiating this at the top of your script will initialize everything correctly and then clean itself up at exit as well.
pub struct Session {}
impl Session {
- pub fn new() -> Self {
- init();
- Self {}
+ /// Before calling new you must make sure that the license is retrievable, otherwise the core won't be able to initialize.
+ ///
+ /// If you cannot otherwise provide a license via `BN_LICENSE_FILE` environment variable or the Binary Ninja user directory
+ /// you can call [`Session::new_with_opts`] instead of this function.
+ pub fn new() -> Result<Self, InitializationError> {
+ if license_location().is_some() {
+ // We were able to locate a license, continue with initialization.
+ init()?;
+ Ok(Self {})
+ } else {
+ // There was no license that could be automatically retrieved, you must call [Self::new_with_license].
+ Err(InitializationError::NoLicenseFound)
+ }
+ }
+
+ /// Initialize with options, the same rules apply as [`Session::new`], see [`InitializationOptions::default`] for the regular options passed.
+ ///
+ /// This differs from [`Session::new`] in that it does not check to see if there is a license that the core
+ /// can discover by itself, therefor it is expected that you know where your license is when calling this directly.
+ pub fn new_with_opts(options: InitializationOptions) -> Result<Self, InitializationError> {
+ init_with_opts(options)?;
+ Ok(Self {})
}
/// ```no_run
- /// let headless_session = binaryninja::headless::Session::new();
+ /// let headless_session = binaryninja::headless::Session::new().unwrap();
///
- /// let bv = headless_session.load("/bin/cat").expect("Couldn't open `/bin/cat`");
+ /// let bv = headless_session
+ /// .load("/bin/cat")
+ /// .expect("Couldn't open `/bin/cat`");
/// ```
- pub fn load(&self, filename: &str) -> Option<rc::Ref<binaryview::BinaryView>> {
- crate::load(filename)
+ pub fn load(&self, file_path: impl AsRef<Path>) -> Option<Ref<binary_view::BinaryView>> {
+ crate::load(file_path)
}
/// ```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 settings: Ref<Metadata> =
+ /// HashMap::from([("analysis.linearSweep.autorun", false.into())]).into();
+ /// let headless_session = binaryninja::headless::Session::new().unwrap();
///
- /// let bv = headless_session.load_with_options("/bin/cat", true, Some(settings))
+ /// let bv = headless_session
+ /// .load_with_options("/bin/cat", true, Some(settings))
/// .expect("Couldn't open `/bin/cat`");
/// ```
pub fn load_with_options<O: IntoJson>(
&self,
- filename: &str,
+ file_path: impl AsRef<Path>,
update_analysis_and_wait: bool,
options: Option<O>,
- ) -> Option<rc::Ref<binaryview::BinaryView>> {
- crate::load_with_options(filename, update_analysis_and_wait, options)
- }
-}
-
-impl Default for Session {
- fn default() -> Self {
- Self::new()
+ ) -> Option<Ref<binary_view::BinaryView>> {
+ crate::load_with_options(file_path, update_analysis_and_wait, options)
}
}
diff --git a/rust/src/hlil/mod.rs b/rust/src/high_level_il.rs
index 8a9103ac..adb44a7e 100644
--- a/rust/src/hlil/mod.rs
+++ b/rust/src/high_level_il.rs
@@ -1,3 +1,4 @@
+//! **WARNING** This API is incomplete and subject to change in the near future!
mod block;
mod function;
mod instruction;
diff --git a/rust/src/hlil/block.rs b/rust/src/high_level_il/block.rs
index 0bbedd18..e7f83425 100644
--- a/rust/src/hlil/block.rs
+++ b/rust/src/high_level_il/block.rs
@@ -1,15 +1,13 @@
use std::ops::Range;
-use binaryninjacore_sys::BNGetHighLevelILIndexForInstruction;
-
-use crate::basicblock::{BasicBlock, BlockContext};
+use crate::basic_block::{BasicBlock, BlockContext};
use crate::rc::Ref;
-use super::{HighLevelILFunction, HighLevelILInstruction};
+use super::{HighLevelILFunction, HighLevelILInstruction, HighLevelInstructionIndex};
pub struct HighLevelILBlockIter {
function: Ref<HighLevelILFunction>,
- range: Range<u64>,
+ range: Range<usize>,
}
impl Iterator for HighLevelILBlockIter {
@@ -18,10 +16,9 @@ impl Iterator for HighLevelILBlockIter {
fn next(&mut self) -> Option<Self::Item> {
self.range
.next()
- .map(|i| unsafe {
- BNGetHighLevelILIndexForInstruction(self.function.handle, i as usize)
- })
- .map(|i| HighLevelILInstruction::new(self.function.to_owned(), i))
+ .map(HighLevelInstructionIndex)
+ // TODO: Is this already MAPPED>!>?!? If so we map twice that is BAD!!!!
+ .and_then(|i| self.function.instruction_from_index(i))
}
}
@@ -31,25 +28,27 @@ pub struct HighLevelILBlock {
impl core::fmt::Debug for HighLevelILBlock {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
+ // TODO: Actual basic block please
write!(f, "mlil_bb {:?}", self.function)
}
}
impl BlockContext for HighLevelILBlock {
- type Iter = HighLevelILBlockIter;
type Instruction = HighLevelILInstruction;
+ type InstructionIndex = HighLevelInstructionIndex;
+ type Iter = HighLevelILBlockIter;
fn start(&self, block: &BasicBlock<Self>) -> HighLevelILInstruction {
- let expr_idx = unsafe {
- BNGetHighLevelILIndexForInstruction(self.function.handle, block.raw_start() as usize)
- };
- HighLevelILInstruction::new(self.function.to_owned(), expr_idx)
+ // TODO: Is this start index already mappedd?????
+ self.function
+ .instruction_from_index(block.start_index())
+ .unwrap()
}
fn iter(&self, block: &BasicBlock<Self>) -> HighLevelILBlockIter {
HighLevelILBlockIter {
function: self.function.to_owned(),
- range: block.raw_start()..block.raw_end(),
+ range: block.start_index().0..block.end_index().0,
}
}
}
diff --git a/rust/src/high_level_il/function.rs b/rust/src/high_level_il/function.rs
new file mode 100644
index 00000000..aad16e14
--- /dev/null
+++ b/rust/src/high_level_il/function.rs
@@ -0,0 +1,293 @@
+use std::fmt::{Debug, Formatter};
+use std::hash::{Hash, Hasher};
+
+use binaryninjacore_sys::*;
+
+use super::{HighLevelILBlock, HighLevelILInstruction, HighLevelInstructionIndex};
+use crate::basic_block::BasicBlock;
+use crate::function::{Function, Location};
+use crate::rc::{Array, Ref, RefCountable};
+use crate::variable::{SSAVariable, Variable};
+
+pub struct HighLevelILFunction {
+ pub(crate) full_ast: bool,
+ pub(crate) handle: *mut BNHighLevelILFunction,
+}
+
+impl HighLevelILFunction {
+ pub(crate) unsafe fn ref_from_raw(
+ handle: *mut BNHighLevelILFunction,
+ full_ast: bool,
+ ) -> Ref<Self> {
+ debug_assert!(!handle.is_null());
+ Self { handle, full_ast }.to_owned()
+ }
+
+ pub fn instruction_from_index(
+ &self,
+ index: HighLevelInstructionIndex,
+ ) -> Option<HighLevelILInstruction> {
+ if index.0 >= self.instruction_count() {
+ None
+ } else {
+ Some(HighLevelILInstruction::new(self.to_owned(), index))
+ }
+ }
+
+ pub fn instruction_from_expr_index(
+ &self,
+ expr_index: HighLevelInstructionIndex,
+ ) -> Option<HighLevelILInstruction> {
+ if expr_index.0 >= self.expression_count() {
+ None
+ } else {
+ Some(HighLevelILInstruction::new_expr(
+ self.to_owned(),
+ expr_index,
+ ))
+ }
+ }
+
+ // TODO: This returns an expression index!
+ pub fn root_instruction_index(&self) -> HighLevelInstructionIndex {
+ HighLevelInstructionIndex(unsafe { BNGetHighLevelILRootExpr(self.handle) })
+ }
+
+ pub fn root(&self) -> HighLevelILInstruction {
+ HighLevelILInstruction::new_expr(self.as_ast(), self.root_instruction_index())
+ }
+
+ pub fn set_root(&self, new_root: &HighLevelILInstruction) {
+ unsafe { BNSetHighLevelILRootExpr(self.handle, new_root.expr_index.0) }
+ }
+
+ pub fn instruction_count(&self) -> usize {
+ unsafe { BNGetHighLevelILInstructionCount(self.handle) }
+ }
+
+ pub fn expression_count(&self) -> usize {
+ unsafe { BNGetHighLevelILExprCount(self.handle) }
+ }
+
+ pub fn ssa_form(&self) -> HighLevelILFunction {
+ let ssa = unsafe { BNGetHighLevelILSSAForm(self.handle) };
+ assert!(!ssa.is_null());
+ HighLevelILFunction {
+ handle: ssa,
+ full_ast: self.full_ast,
+ }
+ }
+
+ pub fn function(&self) -> Ref<Function> {
+ unsafe {
+ let func = BNGetHighLevelILOwnerFunction(self.handle);
+ Function::ref_from_raw(func)
+ }
+ }
+
+ pub fn basic_blocks(&self) -> Array<BasicBlock<HighLevelILBlock>> {
+ let mut count = 0;
+ let blocks = unsafe { BNGetHighLevelILBasicBlockList(self.handle, &mut count) };
+ let context = HighLevelILBlock {
+ function: self.to_owned(),
+ };
+ 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()
+ }
+
+ // TODO: Rename to `current_location`?
+ pub fn current_address(&self) -> Location {
+ let addr = unsafe { BNHighLevelILGetCurrentAddress(self.handle) };
+ Location::from(addr)
+ }
+
+ // TODO: Rename to `set_current_location`?
+ pub fn set_current_address(&self, location: impl Into<Location>) {
+ let location = location.into();
+ let arch = location
+ .arch
+ .map(|a| a.handle)
+ .unwrap_or_else(std::ptr::null_mut);
+ unsafe { BNHighLevelILSetCurrentAddress(self.handle, arch, location.addr) }
+ }
+
+ /// 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, variable: SSAVariable) -> Option<HighLevelILInstruction> {
+ let index = unsafe {
+ BNGetHighLevelILSSAVarDefinition(
+ self.handle,
+ &variable.variable.into(),
+ variable.version,
+ )
+ };
+ self.instruction_from_index(HighLevelInstructionIndex(index))
+ }
+
+ pub fn ssa_memory_definition(&self, version: usize) -> Option<HighLevelILInstruction> {
+ let index = unsafe { BNGetHighLevelILSSAMemoryDefinition(self.handle, version) };
+ self.instruction_from_index(HighLevelInstructionIndex(index))
+ }
+
+ /// Gets all the instructions that use the given SSA variable.
+ pub fn ssa_variable_uses(&self, variable: SSAVariable) -> Array<HighLevelILInstruction> {
+ let mut count = 0;
+ let instrs = unsafe {
+ BNGetHighLevelILSSAVarUses(
+ self.handle,
+ &variable.variable.into(),
+ variable.version,
+ &mut count,
+ )
+ };
+ assert!(!instrs.is_null());
+ unsafe { Array::new(instrs, count, self.to_owned()) }
+ }
+
+ pub fn ssa_memory_uses(&self, version: usize) -> Array<HighLevelILInstruction> {
+ let mut count = 0;
+ let instrs = unsafe { BNGetHighLevelILSSAMemoryUses(self.handle, version, &mut count) };
+ assert!(!instrs.is_null());
+ unsafe { Array::new(instrs, count, self.to_owned()) }
+ }
+
+ /// Determines if `variable` is live at any point in the function
+ pub fn is_ssa_variable_live(&self, variable: SSAVariable) -> bool {
+ unsafe {
+ BNIsHighLevelILSSAVarLive(self.handle, &variable.variable.into(), variable.version)
+ }
+ }
+
+ /// Determines if `variable` is live at a given point in the function
+ pub fn is_ssa_variable_live_at(
+ &self,
+ variable: SSAVariable,
+ instr: &HighLevelILInstruction,
+ ) -> bool {
+ unsafe {
+ BNIsHighLevelILSSAVarLiveAt(
+ self.handle,
+ &variable.variable.into(),
+ variable.version,
+ instr.expr_index.0,
+ )
+ }
+ }
+
+ pub fn variable_definitions(&self, variable: Variable) -> Array<HighLevelILInstruction> {
+ let mut count = 0;
+ let defs = unsafe {
+ BNGetHighLevelILVariableDefinitions(self.handle, &variable.into(), &mut count)
+ };
+ assert!(!defs.is_null());
+ unsafe { Array::new(defs, count, self.to_owned()) }
+ }
+
+ pub fn variable_uses(&self, variable: Variable) -> Array<HighLevelILInstruction> {
+ let mut count = 0;
+ let instrs =
+ unsafe { BNGetHighLevelILVariableUses(self.handle, &variable.into(), &mut count) };
+ assert!(!instrs.is_null());
+ unsafe { Array::new(instrs, count, self.to_owned()) }
+ }
+
+ /// Determines if `variable` is live at a given point in the function
+ pub fn is_variable_live_at(&self, variable: Variable, instr: &HighLevelILInstruction) -> bool {
+ unsafe { BNIsHighLevelILVarLiveAt(self.handle, &variable.into(), instr.expr_index.0) }
+ }
+
+ /// This gets just the HLIL variables - you may be interested in the union
+ /// of [`Function::parameter_variables`] and [`HighLevelILFunction::aliased_variables`] as well for all the
+ /// variables used in the function
+ pub fn variables(&self) -> Array<Variable> {
+ let mut count = 0;
+ let variables = unsafe { BNGetHighLevelILVariables(self.handle, &mut count) };
+ assert!(!variables.is_null());
+ unsafe { Array::new(variables, count, ()) }
+ }
+
+ /// This returns a list of Variables that are taken reference to and used
+ /// elsewhere. You may also wish to consider [`HighLevelILFunction::variables`]
+ /// and [`Function::parameter_variables`]
+ pub fn aliased_variables(&self) -> Array<Variable> {
+ let mut count = 0;
+ let variables = unsafe { BNGetHighLevelILAliasedVariables(self.handle, &mut count) };
+ assert!(!variables.is_null());
+ unsafe { Array::new(variables, count, ()) }
+ }
+
+ /// This gets the HLIL SSA variables for a given [`Variable`].
+ pub fn ssa_variables(&self, variable: &Variable) -> Array<SSAVariable> {
+ let mut count = 0;
+ let raw_variable = BNVariable::from(variable);
+ let variables =
+ unsafe { BNGetHighLevelILVariableSSAVersions(self.handle, &raw_variable, &mut count) };
+ unsafe { Array::new(variables, count, *variable) }
+ }
+}
+
+impl Debug for HighLevelILFunction {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("HighLevelILFunction")
+ .field("arch", &self.function().arch())
+ .field("instruction_count", &self.instruction_count())
+ .field("expression_count", &self.expression_count())
+ .field("root", &self.root())
+ .field("root", &self.root())
+ .finish()
+ }
+}
+
+unsafe impl Send for HighLevelILFunction {}
+unsafe impl Sync for HighLevelILFunction {}
+
+impl Eq for HighLevelILFunction {}
+impl PartialEq for HighLevelILFunction {
+ fn eq(&self, rhs: &Self) -> bool {
+ self.function().eq(&rhs.function())
+ }
+}
+
+impl Hash for HighLevelILFunction {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.function().hash(state)
+ }
+}
+
+impl ToOwned for HighLevelILFunction {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for HighLevelILFunction {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: BNNewHighLevelILFunctionReference(handle.handle),
+ full_ast: handle.full_ast,
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeHighLevelILFunction(handle.handle);
+ }
+}
diff --git a/rust/src/hlil/instruction.rs b/rust/src/high_level_il/instruction.rs
index 6b27284d..40ac164b 100644
--- a/rust/src/hlil/instruction.rs
+++ b/rust/src/high_level_il/instruction.rs
@@ -1,151 +1,69 @@
-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, RegisterValue, RegisterValueType, SSAVariable, Variable};
+use binaryninjacore_sys::*;
+use std::fmt;
+use std::fmt::{Debug, Display, Formatter};
use super::operation::*;
use super::{HighLevelILFunction, HighLevelILLiftedInstruction, HighLevelILLiftedInstructionKind};
+use crate::architecture::{CoreIntrinsic, IntrinsicId};
+use crate::confidence::Conf;
+use crate::disassembly::DisassemblyTextLine;
+use crate::operand_iter::OperandIter;
+use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref};
+use crate::types::Type;
+use crate::variable::{ConstantData, RegisterValue, SSAVariable, Variable};
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct HighLevelInstructionIndex(pub usize);
+
+impl HighLevelInstructionIndex {
+ pub fn next(&self) -> Self {
+ Self(self.0 + 1)
+ }
+}
+
+impl From<usize> for HighLevelInstructionIndex {
+ fn from(index: usize) -> Self {
+ Self(index)
+ }
+}
+
+impl From<u64> for HighLevelInstructionIndex {
+ fn from(index: u64) -> Self {
+ Self(index as usize)
+ }
+}
+
+impl Display for HighLevelInstructionIndex {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_fmt(format_args!("{}", self.0))
+ }
+}
#[derive(Clone)]
pub struct HighLevelILInstruction {
pub function: Ref<HighLevelILFunction>,
pub address: u64,
- pub index: usize,
+ pub expr_index: HighLevelInstructionIndex,
pub size: usize,
pub kind: HighLevelILInstructionKind,
}
-#[derive(Copy, Clone)]
-pub enum HighLevelILInstructionKind {
- Nop,
- Break,
- Continue,
- Noret,
- Unreachable,
- Bp,
- Undef,
- Unimpl,
- Adc(BinaryOpCarry),
- Sbb(BinaryOpCarry),
- Rlc(BinaryOpCarry),
- Rrc(BinaryOpCarry),
- Add(BinaryOp),
- Sub(BinaryOp),
- And(BinaryOp),
- Or(BinaryOp),
- Xor(BinaryOp),
- Lsl(BinaryOp),
- Lsr(BinaryOp),
- Asr(BinaryOp),
- Rol(BinaryOp),
- Ror(BinaryOp),
- Mul(BinaryOp),
- MuluDp(BinaryOp),
- MulsDp(BinaryOp),
- Divu(BinaryOp),
- DivuDp(BinaryOp),
- Divs(BinaryOp),
- DivsDp(BinaryOp),
- Modu(BinaryOp),
- ModuDp(BinaryOp),
- Mods(BinaryOp),
- ModsDp(BinaryOp),
- CmpE(BinaryOp),
- CmpNe(BinaryOp),
- CmpSlt(BinaryOp),
- CmpUlt(BinaryOp),
- CmpSle(BinaryOp),
- CmpUle(BinaryOp),
- CmpSge(BinaryOp),
- CmpUge(BinaryOp),
- CmpSgt(BinaryOp),
- CmpUgt(BinaryOp),
- TestBit(BinaryOp),
- AddOverflow(BinaryOp),
- Fadd(BinaryOp),
- Fsub(BinaryOp),
- Fmul(BinaryOp),
- Fdiv(BinaryOp),
- FcmpE(BinaryOp),
- FcmpNe(BinaryOp),
- FcmpLt(BinaryOp),
- FcmpLe(BinaryOp),
- FcmpGe(BinaryOp),
- FcmpGt(BinaryOp),
- FcmpO(BinaryOp),
- FcmpUo(BinaryOp),
- ArrayIndex(ArrayIndex),
- ArrayIndexSsa(ArrayIndexSsa),
- Assign(Assign),
- AssignMemSsa(AssignMemSsa),
- AssignUnpack(AssignUnpack),
- AssignUnpackMemSsa(AssignUnpackMemSsa),
- Block(Block),
- Call(Call),
- Tailcall(Call),
- CallSsa(CallSsa),
- Case(Case),
- Const(Const),
- ConstPtr(Const),
- Import(Const),
- ConstData(ConstData),
- Deref(UnaryOp),
- AddressOf(UnaryOp),
- Neg(UnaryOp),
- Not(UnaryOp),
- Sx(UnaryOp),
- Zx(UnaryOp),
- LowPart(UnaryOp),
- BoolToInt(UnaryOp),
- UnimplMem(UnaryOp),
- Fsqrt(UnaryOp),
- Fneg(UnaryOp),
- Fabs(UnaryOp),
- FloatToInt(UnaryOp),
- IntToFloat(UnaryOp),
- FloatConv(UnaryOp),
- RoundToInt(UnaryOp),
- Floor(UnaryOp),
- Ceil(UnaryOp),
- Ftrunc(UnaryOp),
- DerefFieldSsa(DerefFieldSsa),
- DerefSsa(DerefSsa),
- ExternPtr(ExternPtr),
- FloatConst(FloatConst),
- For(ForLoop),
- ForSsa(ForLoopSsa),
- Goto(Label),
- Label(Label),
- If(If),
- Intrinsic(Intrinsic),
- IntrinsicSsa(IntrinsicSsa),
- Jump(Jump),
- MemPhi(MemPhi),
- Ret(Ret),
- Split(Split),
- StructField(StructField),
- DerefField(StructField),
- Switch(Switch),
- Syscall(Syscall),
- SyscallSsa(SyscallSsa),
- Trap(Trap),
- VarDeclare(Var),
- Var(Var),
- VarInit(VarInit),
- VarInitSsa(VarInitSsa),
- VarPhi(VarPhi),
- VarSsa(VarSsa),
- While(While),
- DoWhile(While),
- WhileSsa(WhileSsa),
- DoWhileSsa(WhileSsa),
-}
impl HighLevelILInstruction {
- pub(crate) fn new(function: Ref<HighLevelILFunction>, index: usize) -> Self {
- let op = unsafe { BNGetHighLevelILByIndex(function.handle, index, function.full_ast) };
+ pub(crate) fn new(
+ function: Ref<HighLevelILFunction>,
+ index: HighLevelInstructionIndex,
+ ) -> Self {
+ let expr_index = unsafe { BNGetHighLevelILIndexForInstruction(function.handle, index.0) };
+ Self::new_expr(function, HighLevelInstructionIndex(expr_index))
+ }
+
+ // TODO: I need HighLevelILExpression YESTERDAY!!!!
+ pub(crate) fn new_expr(
+ function: Ref<HighLevelILFunction>,
+ expr_index: HighLevelInstructionIndex,
+ ) -> Self {
+ let op =
+ unsafe { BNGetHighLevelILByIndex(function.handle, expr_index.0, function.full_ast) };
use BNHighLevelILOperation::*;
use HighLevelILInstructionKind as Op;
let kind = match op.operation {
@@ -627,7 +545,7 @@ impl HighLevelILInstruction {
Self {
function,
address: op.address,
- index,
+ expr_index,
size: op.size,
kind,
}
@@ -748,9 +666,14 @@ impl HighLevelILInstruction {
Import(op) => Lifted::Import(op),
ConstData(op) => Lifted::ConstData(LiftedConstData {
constant_data: ConstantData::new(
- self.function.get_function(),
+ self.function.function(),
RegisterValue {
- state: RegisterValueType::from_raw_value(op.constant_data_kind).unwrap(),
+ // TODO: Replace with a From<u32> for RegisterValueType.
+ // TODO: We might also want to change the type of `op.constant_data_kind`
+ // TODO: To RegisterValueType and do the conversion when creating instruction.
+ state: unsafe {
+ std::mem::transmute::<u32, BNRegisterValueType>(op.constant_data_kind)
+ },
value: op.constant_data_value,
offset: 0,
size: op.size,
@@ -811,11 +734,19 @@ impl HighLevelILInstruction {
cond_false: self.lift_operand(op.cond_false),
}),
Intrinsic(op) => Lifted::Intrinsic(LiftedIntrinsic {
- intrinsic: CoreIntrinsic(self.function.get_function().arch().0, op.intrinsic),
+ intrinsic: CoreIntrinsic::new(
+ self.function.function().arch(),
+ IntrinsicId(op.intrinsic),
+ )
+ .expect("Invalid intrinsic"),
params: self.lift_instruction_list(op.first_param, op.num_params),
}),
IntrinsicSsa(op) => Lifted::IntrinsicSsa(LiftedIntrinsicSsa {
- intrinsic: CoreIntrinsic(self.function.get_function().arch().0, op.intrinsic),
+ intrinsic: CoreIntrinsic::new(
+ self.function.function().arch(),
+ IntrinsicId(op.intrinsic),
+ )
+ .expect("Invalid intrinsic"),
params: self.lift_instruction_list(op.first_param, op.num_params),
dest_memory: op.dest_memory,
src_memory: op.src_memory,
@@ -877,14 +808,69 @@ impl HighLevelILInstruction {
HighLevelILLiftedInstruction {
function: self.function.clone(),
address: self.address,
- index: self.index,
+ expr_index: self.expr_index,
size: self.size,
kind,
}
}
+ /// HLIL text lines
+ pub fn lines(&self) -> Array<DisassemblyTextLine> {
+ let mut count = 0;
+ let lines = unsafe {
+ BNGetHighLevelILExprText(
+ self.function.handle,
+ self.expr_index.0,
+ self.function.full_ast,
+ &mut count,
+ core::ptr::null_mut(),
+ )
+ };
+ unsafe { Array::new(lines, count, ()) }
+ }
+
+ /// Type of expression
+ pub fn expr_type(&self) -> Option<Conf<Ref<Type>>> {
+ let result = unsafe { BNGetHighLevelILExprType(self.function.handle, self.expr_index.0) };
+ (!result.type_.is_null()).then(|| {
+ Conf::new(
+ unsafe { Type::ref_from_raw(result.type_) },
+ result.confidence,
+ )
+ })
+ }
+
+ /// Version of active memory contents in SSA form for this instruction
+ pub fn ssa_memory_version(&self) -> usize {
+ unsafe {
+ BNGetHighLevelILSSAMemoryVersionAtILInstruction(self.function.handle, self.expr_index.0)
+ }
+ }
+
+ pub fn ssa_variable_version(&self, variable: Variable) -> SSAVariable {
+ let version = unsafe {
+ BNGetHighLevelILSSAVarVersionAtILInstruction(
+ self.function.handle,
+ &variable.into(),
+ self.expr_index.0,
+ )
+ };
+ SSAVariable::new(variable, version)
+ }
+
fn lift_operand(&self, expr_idx: usize) -> Box<HighLevelILLiftedInstruction> {
- Box::new(self.function.lifted_instruction_from_idx(expr_idx))
+ // TODO: UGH, if your gonna call it expr_idx, call the instruction and expression!!!!!
+ // TODO: We dont even need to say instruction in the type!
+ // TODO: IF you want to have an instruction type, there needs to be a separate expression type
+ // TODO: See the lowlevelil module.
+ let expr_idx_is_really_instr_idx = HighLevelInstructionIndex(expr_idx);
+ // TODO: Ugh, this is so dumb..... i want HighLevelILLiftedExpression yesterday!!!
+ let operand_instr = self
+ .function
+ .instruction_from_expr_index(expr_idx_is_really_instr_idx)
+ .unwrap();
+ // TODO: Why box it here??!?!?! insane.
+ Box::new(operand_instr.lift())
}
fn lift_binary_op(&self, op: BinaryOp) -> LiftedBinaryOp {
@@ -911,7 +897,7 @@ impl HighLevelILInstruction {
fn lift_label(&self, op: Label) -> LiftedLabel {
LiftedLabel {
target: GotoLabel {
- function: self.function.get_function(),
+ function: self.function.function(),
target: op.target,
},
}
@@ -962,8 +948,25 @@ impl HighLevelILInstruction {
}
}
-impl core::fmt::Debug for HighLevelILInstruction {
- fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
+impl CoreArrayProvider for HighLevelILInstruction {
+ type Raw = usize;
+ type Context = Ref<HighLevelILFunction>;
+ type Wrapped<'a> = Self;
+}
+
+unsafe impl CoreArrayProviderInner for HighLevelILInstruction {
+ unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
+ unsafe { BNFreeILInstructionList(raw) }
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
+ Self::new(context.clone(), HighLevelInstructionIndex(*raw))
+ }
+}
+
+impl Debug for HighLevelILInstruction {
+ fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
+ // TODO: Actual debug impl please!
write!(
f,
"<{} at 0x{:08}>",
@@ -973,6 +976,132 @@ impl core::fmt::Debug for HighLevelILInstruction {
}
}
+#[derive(Debug, Copy, Clone)]
+pub enum HighLevelILInstructionKind {
+ Nop,
+ Break,
+ Continue,
+ Noret,
+ Unreachable,
+ Bp,
+ Undef,
+ Unimpl,
+ Adc(BinaryOpCarry),
+ Sbb(BinaryOpCarry),
+ Rlc(BinaryOpCarry),
+ Rrc(BinaryOpCarry),
+ Add(BinaryOp),
+ Sub(BinaryOp),
+ And(BinaryOp),
+ Or(BinaryOp),
+ Xor(BinaryOp),
+ Lsl(BinaryOp),
+ Lsr(BinaryOp),
+ Asr(BinaryOp),
+ Rol(BinaryOp),
+ Ror(BinaryOp),
+ Mul(BinaryOp),
+ MuluDp(BinaryOp),
+ MulsDp(BinaryOp),
+ Divu(BinaryOp),
+ DivuDp(BinaryOp),
+ Divs(BinaryOp),
+ DivsDp(BinaryOp),
+ Modu(BinaryOp),
+ ModuDp(BinaryOp),
+ Mods(BinaryOp),
+ ModsDp(BinaryOp),
+ CmpE(BinaryOp),
+ CmpNe(BinaryOp),
+ CmpSlt(BinaryOp),
+ CmpUlt(BinaryOp),
+ CmpSle(BinaryOp),
+ CmpUle(BinaryOp),
+ CmpSge(BinaryOp),
+ CmpUge(BinaryOp),
+ CmpSgt(BinaryOp),
+ CmpUgt(BinaryOp),
+ TestBit(BinaryOp),
+ AddOverflow(BinaryOp),
+ Fadd(BinaryOp),
+ Fsub(BinaryOp),
+ Fmul(BinaryOp),
+ Fdiv(BinaryOp),
+ FcmpE(BinaryOp),
+ FcmpNe(BinaryOp),
+ FcmpLt(BinaryOp),
+ FcmpLe(BinaryOp),
+ FcmpGe(BinaryOp),
+ FcmpGt(BinaryOp),
+ FcmpO(BinaryOp),
+ FcmpUo(BinaryOp),
+ ArrayIndex(ArrayIndex),
+ ArrayIndexSsa(ArrayIndexSsa),
+ Assign(Assign),
+ AssignMemSsa(AssignMemSsa),
+ AssignUnpack(AssignUnpack),
+ AssignUnpackMemSsa(AssignUnpackMemSsa),
+ Block(Block),
+ Call(Call),
+ Tailcall(Call),
+ CallSsa(CallSsa),
+ Case(Case),
+ Const(Const),
+ ConstPtr(Const),
+ Import(Const),
+ ConstData(ConstData),
+ Deref(UnaryOp),
+ AddressOf(UnaryOp),
+ Neg(UnaryOp),
+ Not(UnaryOp),
+ Sx(UnaryOp),
+ Zx(UnaryOp),
+ LowPart(UnaryOp),
+ BoolToInt(UnaryOp),
+ UnimplMem(UnaryOp),
+ Fsqrt(UnaryOp),
+ Fneg(UnaryOp),
+ Fabs(UnaryOp),
+ FloatToInt(UnaryOp),
+ IntToFloat(UnaryOp),
+ FloatConv(UnaryOp),
+ RoundToInt(UnaryOp),
+ Floor(UnaryOp),
+ Ceil(UnaryOp),
+ Ftrunc(UnaryOp),
+ DerefFieldSsa(DerefFieldSsa),
+ DerefSsa(DerefSsa),
+ ExternPtr(ExternPtr),
+ FloatConst(FloatConst),
+ For(ForLoop),
+ ForSsa(ForLoopSsa),
+ Goto(Label),
+ Label(Label),
+ If(If),
+ Intrinsic(Intrinsic),
+ IntrinsicSsa(IntrinsicSsa),
+ Jump(Jump),
+ MemPhi(MemPhi),
+ Ret(Ret),
+ Split(Split),
+ StructField(StructField),
+ DerefField(StructField),
+ Switch(Switch),
+ Syscall(Syscall),
+ SyscallSsa(SyscallSsa),
+ Trap(Trap),
+ VarDeclare(Var),
+ Var(Var),
+ VarInit(VarInit),
+ VarInitSsa(VarInitSsa),
+ VarPhi(VarPhi),
+ VarSsa(VarSsa),
+ While(While),
+ DoWhile(While),
+ WhileSsa(WhileSsa),
+ DoWhileSsa(WhileSsa),
+}
+
fn get_float(value: u64, size: usize) -> f64 {
match size {
4 => f32::from_bits(value as u32) as f64,
@@ -983,7 +1112,7 @@ fn get_float(value: u64, size: usize) -> f64 {
}
fn get_var(id: u64) -> Variable {
- unsafe { Variable::from_identifier(id) }
+ Variable::from_identifier(id)
}
fn get_member_index(idx: u64) -> Option<usize> {
diff --git a/rust/src/hlil/lift.rs b/rust/src/high_level_il/lift.rs
index 300a7f1f..0c7c2983 100644
--- a/rust/src/hlil/lift.rs
+++ b/rust/src/high_level_il/lift.rs
@@ -1,9 +1,9 @@
use super::operation::*;
-use super::HighLevelILFunction;
+use super::{HighLevelILFunction, HighLevelInstructionIndex};
use crate::architecture::CoreIntrinsic;
use crate::rc::Ref;
-use crate::types::{ConstantData, SSAVariable, Variable};
+use crate::variable::{ConstantData, SSAVariable, Variable};
#[derive(Clone)]
pub enum HighLevelILLiftedOperand {
@@ -21,11 +21,16 @@ pub enum HighLevelILLiftedOperand {
VarSsaList(Vec<SSAVariable>),
}
+// TODO: UGH, if your gonna call it expr_idx, call the instruction and expression!!!!!
+// TODO: We dont even need to say instruction in the type!
+// TODO: IF you want to have an instruction type, there needs to be a separate expression type
+// TODO: See the lowlevelil module.
#[derive(Clone, Debug, PartialEq)]
pub struct HighLevelILLiftedInstruction {
pub function: Ref<HighLevelILFunction>,
pub address: u64,
- pub index: usize,
+ // TODO: Please i need expression
+ pub expr_index: HighLevelInstructionIndex,
pub size: usize,
pub kind: HighLevelILLiftedInstructionKind,
}
diff --git a/rust/src/hlil/operation.rs b/rust/src/high_level_il/operation.rs
index e762a74b..58accce0 100644
--- a/rust/src/hlil/operation.rs
+++ b/rust/src/high_level_il/operation.rs
@@ -1,12 +1,13 @@
-use binaryninjacore_sys::BNGetGotoLabelName;
+use core::ffi;
+use binaryninjacore_sys::*;
+
+use super::HighLevelILLiftedInstruction;
use crate::architecture::CoreIntrinsic;
use crate::function::Function;
use crate::rc::Ref;
-use crate::string::BnString;
-use crate::types::{ConstantData, SSAVariable, Variable};
-
-use super::HighLevelILLiftedInstruction;
+use crate::string::{BnStrCompatible, BnString};
+use crate::variable::{ConstantData, SSAVariable, Variable};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct GotoLabel {
@@ -18,10 +19,21 @@ impl GotoLabel {
pub fn name(&self) -> BnString {
unsafe { BnString::from_raw(BNGetGotoLabelName(self.function.handle, self.target)) }
}
+
+ fn set_name<S: BnStrCompatible>(&self, name: S) {
+ let raw = name.into_bytes_with_nul();
+ unsafe {
+ BNSetUserGotoLabelName(
+ self.function.handle,
+ self.target,
+ raw.as_ref().as_ptr() as *const ffi::c_char,
+ )
+ }
+ }
}
// ADC, SBB, RLC, RRC
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct BinaryOpCarry {
pub left: usize,
pub right: usize,
@@ -35,7 +47,7 @@ pub struct LiftedBinaryOpCarry {
}
// ADD, SUB, AND, OR, XOR, LSL, LSR, ASR, ROL, ROR, MUL, MULU_DP, MULS_DP, DIVU, DIVU_DP, DIVS, DIVS_DP, MODU, MODU_DP, MODS, MODS_DP, CMP_E, CMP_NE, CMP_SLT, CMP_ULT, CMP_SLE, CMP_ULE, CMP_SGE, CMP_UGE, CMP_SGT, CMP_UGT, TEST_BIT, ADD_OVERFLOW, FADD, FSUB, FMUL, FDIV, FCMP_E, FCMP_NE, FCMP_LT, FCMP_LE, FCMP_GE, FCMP_GT, FCMP_O, FCMP_UO
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct BinaryOp {
pub left: usize,
pub right: usize,
@@ -47,7 +59,7 @@ pub struct LiftedBinaryOp {
}
// ARRAY_INDEX
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct ArrayIndex {
pub src: usize,
pub index: usize,
@@ -59,7 +71,7 @@ pub struct LiftedArrayIndex {
}
// ARRAY_INDEX_SSA
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct ArrayIndexSsa {
pub src: usize,
pub src_memory: u64,
@@ -73,7 +85,7 @@ pub struct LiftedArrayIndexSsa {
}
// ASSIGN
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct Assign {
pub dest: usize,
pub src: usize,
@@ -85,7 +97,7 @@ pub struct LiftedAssign {
}
// ASSIGN_MEM_SSA
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct AssignMemSsa {
pub dest: usize,
pub dest_memory: u64,
@@ -101,7 +113,7 @@ pub struct LiftedAssignMemSsa {
}
// ASSIGN_UNPACK
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct AssignUnpack {
pub first_dest: usize,
pub num_dests: usize,
@@ -114,7 +126,7 @@ pub struct LiftedAssignUnpack {
}
// ASSIGN_UNPACK_MEM_SSA
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct AssignUnpackMemSsa {
pub first_dest: usize,
pub num_dests: usize,
@@ -131,7 +143,7 @@ pub struct LiftedAssignUnpackMemSsa {
}
// BLOCK
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct Block {
pub first_param: usize,
pub num_params: usize,
@@ -142,7 +154,7 @@ pub struct LiftedBlock {
}
// CALL, TAILCALL
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct Call {
pub dest: usize,
pub first_param: usize,
@@ -155,7 +167,7 @@ pub struct LiftedCall {
}
// CALL_SSA
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct CallSsa {
pub dest: usize,
pub first_param: usize,
@@ -172,7 +184,7 @@ pub struct LiftedCallSsa {
}
// CASE
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct Case {
pub first_value: usize,
pub num_values: usize,
@@ -191,19 +203,20 @@ pub struct Const {
}
// CONST_DATA
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct ConstData {
pub constant_data_kind: u32,
pub constant_data_value: i64,
pub size: usize,
}
+
#[derive(Clone, Debug, PartialEq)]
pub struct LiftedConstData {
pub constant_data: ConstantData,
}
// DEREF, ADDRESS_OF, NEG, NOT, SX, ZX, LOW_PART, BOOL_TO_INT, UNIMPL_MEM, FSQRT, FNEG, FABS, FLOAT_TO_INT, INT_TO_FLOAT, FLOAT_CONV, ROUND_TO_INT, FLOOR, CEIL, FTRUNC
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct UnaryOp {
pub src: usize,
}
@@ -213,13 +226,14 @@ pub struct LiftedUnaryOp {
}
// DEREF_FIELD_SSA
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct DerefFieldSsa {
pub src: usize,
pub src_memory: u64,
pub offset: u64,
pub member_index: Option<usize>,
}
+
#[derive(Clone, Debug, PartialEq)]
pub struct LiftedDerefFieldSsa {
pub src: Box<HighLevelILLiftedInstruction>,
@@ -229,11 +243,12 @@ pub struct LiftedDerefFieldSsa {
}
// DEREF_SSA
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct DerefSsa {
pub src: usize,
pub src_memory: u64,
}
+
#[derive(Clone, Debug, PartialEq)]
pub struct LiftedDerefSsa {
pub src: Box<HighLevelILLiftedInstruction>,
@@ -254,13 +269,14 @@ pub struct FloatConst {
}
// FOR
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct ForLoop {
pub init: usize,
pub condition: usize,
pub update: usize,
pub body: usize,
}
+
#[derive(Clone, Debug, PartialEq)]
pub struct LiftedForLoop {
pub init: Box<HighLevelILLiftedInstruction>,
@@ -270,7 +286,7 @@ pub struct LiftedForLoop {
}
// FOR_SSA
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct ForLoopSsa {
pub init: usize,
pub condition_phi: usize,
@@ -288,7 +304,7 @@ pub struct LiftedForLoopSsa {
}
// GOTO, LABEL
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct Label {
pub target: u64,
}
@@ -297,8 +313,18 @@ pub struct LiftedLabel {
pub target: GotoLabel,
}
+impl LiftedLabel {
+ pub fn name(&self) -> BnString {
+ self.target.name()
+ }
+
+ pub fn set_name<S: BnStrCompatible>(&self, name: S) {
+ self.target.set_name(name)
+ }
+}
+
// IF
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct If {
pub condition: usize,
pub cond_true: usize,
@@ -312,7 +338,7 @@ pub struct LiftedIf {
}
// INTRINSIC
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct Intrinsic {
pub intrinsic: u32,
pub first_param: usize,
@@ -325,7 +351,7 @@ pub struct LiftedIntrinsic {
}
// INTRINSIC_SSA
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct IntrinsicSsa {
pub intrinsic: u32,
pub first_param: usize,
@@ -342,7 +368,7 @@ pub struct LiftedIntrinsicSsa {
}
// JUMP
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct Jump {
pub dest: usize,
}
@@ -352,7 +378,7 @@ pub struct LiftedJump {
}
// MEM_PHI
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct MemPhi {
pub dest: u64,
pub first_src: usize,
@@ -365,7 +391,7 @@ pub struct LiftedMemPhi {
}
// RET
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct Ret {
pub first_src: usize,
pub num_srcs: usize,
@@ -376,7 +402,7 @@ pub struct LiftedRet {
}
// SPLIT
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct Split {
pub high: usize,
pub low: usize,
@@ -388,7 +414,7 @@ pub struct LiftedSplit {
}
// STRUCT_FIELD, DEREF_FIELD
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct StructField {
pub src: usize,
pub offset: u64,
@@ -402,7 +428,7 @@ pub struct LiftedStructField {
}
// SWITCH
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct Switch {
pub condition: usize,
pub default: usize,
@@ -417,7 +443,7 @@ pub struct LiftedSwitch {
}
// SYSCALL
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct Syscall {
pub first_param: usize,
pub num_params: usize,
@@ -428,7 +454,7 @@ pub struct LiftedSyscall {
}
// SYSCALL_SSA
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct SyscallSsa {
pub first_param: usize,
pub num_params: usize,
@@ -455,7 +481,7 @@ pub struct Var {
}
// VAR_INIT
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct VarInit {
pub dest: Variable,
pub src: usize,
@@ -467,7 +493,7 @@ pub struct LiftedVarInit {
}
// VAR_INIT_SSA
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct VarInitSsa {
pub dest: SSAVariable,
pub src: usize,
@@ -479,7 +505,7 @@ pub struct LiftedVarInitSsa {
}
// VAR_PHI
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct VarPhi {
pub dest: SSAVariable,
pub first_src: usize,
@@ -498,7 +524,7 @@ pub struct VarSsa {
}
// WHILE, DO_WHILE
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct While {
pub condition: usize,
pub body: usize,
@@ -510,7 +536,7 @@ pub struct LiftedWhile {
}
// WHILE_SSA, DO_WHILE_SSA
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct WhileSsa {
pub condition_phi: usize,
pub condition: usize,
diff --git a/rust/src/hlil/function.rs b/rust/src/hlil/function.rs
deleted file mode 100644
index 25608d71..00000000
--- a/rust/src/hlil/function.rs
+++ /dev/null
@@ -1,152 +0,0 @@
-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;
-
-use crate::basicblock::BasicBlock;
-use crate::function::Function;
-use crate::rc::{Array, Ref, RefCountable};
-
-use super::{HighLevelILBlock, HighLevelILInstruction, HighLevelILLiftedInstruction};
-
-pub struct HighLevelILFunction {
- pub(crate) full_ast: bool,
- pub(crate) handle: *mut BNHighLevelILFunction,
-}
-
-unsafe impl Send for HighLevelILFunction {}
-unsafe impl Sync for HighLevelILFunction {}
-
-impl Eq for HighLevelILFunction {}
-impl PartialEq for HighLevelILFunction {
- fn eq(&self, rhs: &Self) -> bool {
- self.get_function().eq(&rhs.get_function())
- }
-}
-
-impl Hash for HighLevelILFunction {
- fn hash<H: Hasher>(&self, state: &mut H) {
- self.get_function().hash(state)
- }
-}
-
-impl HighLevelILFunction {
- pub(crate) unsafe fn ref_from_raw(
- handle: *mut BNHighLevelILFunction,
- full_ast: bool,
- ) -> Ref<Self> {
- debug_assert!(!handle.is_null());
- Self { handle, full_ast }.to_owned()
- }
-
- pub fn instruction_from_idx(&self, expr_idx: usize) -> HighLevelILInstruction {
- HighLevelILInstruction::new(self.to_owned(), expr_idx)
- }
-
- pub fn lifted_instruction_from_idx(&self, expr_idx: usize) -> HighLevelILLiftedInstruction {
- 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) }
- }
-
- pub fn ssa_form(&self) -> HighLevelILFunction {
- let ssa = unsafe { BNGetHighLevelILSSAForm(self.handle) };
- assert!(!ssa.is_null());
- HighLevelILFunction {
- handle: ssa,
- full_ast: self.full_ast,
- }
- }
-
- pub fn get_function(&self) -> Ref<Function> {
- unsafe {
- let func = BNGetHighLevelILOwnerFunction(self.handle);
- Function::from_raw(func)
- }
- }
-
- pub fn basic_blocks(&self) -> Array<BasicBlock<HighLevelILBlock>> {
- let mut count = 0;
- let blocks = unsafe { BNGetHighLevelILBasicBlockList(self.handle, &mut count) };
- let context = HighLevelILBlock {
- function: self.to_owned(),
- };
-
- 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 {
- type Owned = Ref<Self>;
-
- fn to_owned(&self) -> Self::Owned {
- unsafe { RefCountable::inc_ref(self) }
- }
-}
-
-unsafe impl RefCountable for HighLevelILFunction {
- unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
- Ref::new(Self {
- handle: BNNewHighLevelILFunctionReference(handle.handle),
- full_ast: handle.full_ast,
- })
- }
-
- unsafe fn dec_ref(handle: &Self) {
- BNFreeHighLevelILFunction(handle.handle);
- }
-}
-
-impl core::fmt::Debug for HighLevelILFunction {
- fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
- write!(f, "<hlil func handle {:p}>", self.handle)
- }
-}
diff --git a/rust/src/interaction.rs b/rust/src/interaction.rs
index e0090832..151e9f7c 100644
--- a/rust/src/interaction.rs
+++ b/rust/src/interaction.rs
@@ -19,7 +19,7 @@ use binaryninjacore_sys::*;
use std::ffi::{c_char, c_void, CStr};
use std::path::PathBuf;
-use crate::binaryview::BinaryView;
+use crate::binary_view::BinaryView;
use crate::rc::Ref;
use crate::string::{BnStrCompatible, BnString};
@@ -96,7 +96,11 @@ pub fn get_open_filename_input(prompt: &str, extension: &str) -> Option<PathBuf>
Some(PathBuf::from(string.as_str()))
}
-pub fn get_save_filename_input(prompt: &str, extension: &str, default_name: &str) -> Option<PathBuf> {
+pub fn get_save_filename_input(
+ prompt: &str,
+ extension: &str,
+ default_name: &str,
+) -> Option<PathBuf> {
let mut value: *mut c_char = std::ptr::null_mut();
let result = unsafe {
@@ -214,7 +218,7 @@ impl FormInputBuilder {
}
/// Form Field: Vertical spacing
- pub fn seperator_field(mut self) -> Self {
+ pub fn separator_field(mut self) -> Self {
let mut result = unsafe { std::mem::zeroed::<BNFormInputField>() };
result.type_ = BNFormInputFieldType::SeparatorFormField;
result.hasDefault = false;
@@ -478,10 +482,10 @@ impl FormInputBuilder {
/// };
///
/// let FormResponses::String(last_name) = &responses[0] else {
- /// unreachable!()
+ /// unreachable!()
/// };
/// let FormResponses::String(first_name) = &responses[1] else {
- /// unreachable!()
+ /// unreachable!()
/// };
///
/// println!("{} {} likes {}", &first_name, &last_name, food);
diff --git a/rust/src/lib.rs b/rust/src/lib.rs
index eff9518d..08e31c00 100644
--- a/rust/src/lib.rs
+++ b/rust/src/lib.rs
@@ -12,149 +12,58 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-// TODO : These clippy-allow are bad and needs to be removed
+// TODO: These clippy-allow are bad and needs to be removed
#![allow(clippy::missing_safety_doc)]
#![allow(clippy::result_unit_err)]
#![allow(clippy::type_complexity)]
-#![doc(html_root_url = "https://dev-rust.binary.ninja/binaryninja/")]
-#![doc(html_favicon_url = "/favicon.ico")]
-#![doc(html_logo_url = "/logo.png")]
+#![allow(clippy::too_many_arguments)]
+#![allow(clippy::needless_doctest_main)]
+#![doc(html_root_url = "https://dev-rust.binary.ninja/")]
+#![doc(html_favicon_url = "https://binary.ninja/icons/favicon-32x32.png")]
+#![doc(html_logo_url = "https://binary.ninja/icons/android-chrome-512x512.png")]
#![doc(issue_tracker_base_url = "https://github.com/Vector35/binaryninja-api/issues/")]
-
-//! This crate is the official [Binary Ninja] API wrapper for Rust.
-//!
-//! [Binary Ninja] is an interactive disassembler, decompiler, and binary analysis platform for reverse engineers, malware analysts, vulnerability researchers, and software developers that runs on Windows, macOS, and Linux. Our extensive API can be used to create and customize loaders, add or augment architectures, customize the UI, or automate any workflow (types, patches, decompilation...anything!).
-//!
-//! If you're just getting started with [Binary Ninja], you may wish to check out the [Getting Started Guide]
-//!
-//! If you have questions, we'd love to answer them in [our public Slack], and if you find any issues, please [file an issue] or [submit a PR].
-//!
-//! ---
-//! # Warning
-//! <img align="right" src="../under_construction.png" width="175" height="175">
-//!
-//! > ⚠️ **These bindings are in a very early beta, only have partial support for the core APIs and are still actively under development. Compatibility _will_ break and conventions _will_ change! They are being used for core Binary Ninja features however, so we expect much of what is already there to be reliable enough to build on, just don't be surprised if your plugins/scripts need to hit a moving target.**
-//!
-//! > ⚠️ This project runs on Rust version `1.83.0`
-//!
-//! ---
-//!
-//! # Examples
-//!
-//! There are two distinct ways to use this crate:
-//! 1. [Writing a Plugin](#writing-a-plugin)
-//! 2. [Writing a Script](#writing-a-script)
-//!
-//! ## Writing a Plugin
-//!
-//! Create a new library (`cargo new --lib <plugin-name>`) and include the following in your `Cargo.toml`:
-//!
-//! ```toml
-//! [lib]
-//! crate-type = ["cdylib"]
-//!
-//! [dependencies]
-//! binaryninja = {git = "https://github.com/Vector35/binaryninja-api.git", branch = "dev"}
-//! ```
-//!
-//! In `lib.rs` you'll need to provide a `CorePluginInit` or `UIPluginInit` function for Binary Ninja to call.
-//!
-//! See [`command`] for the different actions you can provide and how to register your plugin with [Binary Ninja].
-//!
-//! ## Writing a Script:
-//!
-//! "Scripts" are binaries (`cargo new --bin <script-name>`), and have some specific requirements:
-//!
-//! ### build.rs
-//!
-//! Because [the only official method of providing linker arguments to a crate is through that crate's `build.rs`], all scripts need to provide their own `build.rs` so they can probably link with Binary Ninja.
-//!
-//! The most up-to-date version of the suggested [`build.rs` is here].
-//!
-//! ### `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.
-//! ```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`");
-//!
-//! // Your code here...
-//! ```
-//!
-//! ### `Cargo.toml`
-//! ```toml
-//! [dependencies]
-//! binaryninja = { git = "https://github.com/Vector35/binaryninja-api.git", branch = "dev"}
-//! ```
-//!
-//! See the [examples] on GitHub for more comprehensive examples.
-//!
-//! [Binary Ninja]: https://binary.ninja/
-//! [Getting Started Guide]: https://docs.binary.ninja/
-//! [our public Slack]: https://join.slack.com/t/binaryninja/shared_invite/zt-3u4vu3ja-IGUF4ZWNlD7ER2ulvICvuQ
-//! [file an issue]: https://github.com/Vector35/binaryninja-api/issues
-//! [submit a PR]: https://github.com/Vector35/binaryninja-api/pulls
-//! [the only official method of providing linker arguments to a crate is through that crate's `build.rs`]: https://github.com/rust-lang/cargo/issues/9554
-//! [`build.rs` is here]: https://github.com/Vector35/binaryninja-api/blob/dev/rust/examples/template/build.rs
-//! [examples]: https://github.com/Vector35/binaryninja-api/tree/dev/rust/examples
-//!
-
-#[macro_use]
-extern crate log;
-#[doc(hidden)]
-pub extern crate binaryninjacore_sys;
-#[cfg(feature = "rayon")]
-extern crate rayon;
-
-// TODO
-// move some options to results
-// replace `fn handle` with `AsRef` bounds
-// possible values
-// arch rework
-// cc possible values
-// bv reorg
-// core fileaccessor (for bv saving)
-// platform cc
+#![doc = include_str!("../README.md")]
#[macro_use]
mod ffi;
mod operand_iter;
pub mod architecture;
-pub mod backgroundtask;
-pub mod basicblock;
-pub mod binaryreader;
-pub mod binaryview;
-pub mod binarywriter;
-pub mod callingconvention;
+pub mod background_task;
+pub mod basic_block;
+pub mod binary_reader;
+pub mod binary_view;
+pub mod binary_writer;
+pub mod calling_convention;
+pub mod collaboration;
pub mod command;
-pub mod custombinaryview;
+pub mod component;
+pub mod confidence;
+pub mod custom_binary_view;
+pub mod data_buffer;
pub mod database;
-pub mod databuffer;
pub mod debuginfo;
pub mod demangle;
pub mod disassembly;
+pub mod download_provider;
pub mod enterprise;
-pub mod component;
-pub mod downloadprovider;
-pub mod externallibrary;
-pub mod fileaccessor;
-pub mod filemetadata;
+pub mod external_library;
+pub mod file_accessor;
+pub mod file_metadata;
pub mod flowgraph;
pub mod function;
-pub mod functionrecognizer;
+pub mod function_recognizer;
pub mod headless;
-pub mod hlil;
+pub mod high_level_il;
pub mod interaction;
-pub mod linearview;
-pub mod llil;
+pub mod linear_view;
pub mod logger;
+pub mod low_level_il;
+pub mod main_thread;
+pub mod medium_level_il;
pub mod metadata;
-pub mod mlil;
pub mod platform;
+pub mod progress;
pub mod project;
pub mod rc;
pub mod references;
@@ -165,124 +74,66 @@ pub mod settings;
pub mod string;
pub mod symbol;
pub mod tags;
-pub mod templatesimplifier;
-pub mod typelibrary;
-pub mod typearchive;
+pub mod template_simplifier;
+pub mod type_archive;
+pub mod type_container;
+pub mod type_library;
+pub mod type_parser;
+pub mod type_printer;
pub mod types;
pub mod update;
+pub mod variable;
+pub mod worker_thread;
pub mod workflow;
-use std::collections::HashMap;
-use std::ffi::{c_void, CStr};
-use std::path::PathBuf;
-use std::ptr;
-use binaryninjacore_sys::{BNBinaryView, BNFileMetadata, BNFunction, BNObjectDestructionCallbacks, BNRegisterObjectDestructionCallbacks, BNUnregisterObjectDestructionCallbacks};
-pub use binaryninjacore_sys::BNBranchType as BranchType;
-pub use binaryninjacore_sys::BNEndianness as Endianness;
-use binaryview::BinaryView;
+use crate::file_metadata::FileMetadata;
+use crate::function::Function;
+use binary_view::BinaryView;
+use binaryninjacore_sys::*;
use metadata::Metadata;
use metadata::MetadataType;
+use rc::Ref;
+use std::collections::HashMap;
+use std::ffi::{c_char, c_void, CStr};
+use std::path::{Path, PathBuf};
use string::BnStrCompatible;
+use string::BnString;
use string::IntoJson;
-use crate::filemetadata::FileMetadata;
-use crate::function::Function;
-// Commented out to suppress unused warnings
-// const BN_MAX_INSTRUCTION_LENGTH: u64 = 256;
-// const BN_DEFAULT_INSTRUCTION_LENGTH: u64 = 16;
-// const BN_DEFAULT_OPCODE_DISPLAY: u64 = 8;
-// const BN_MAX_INSTRUCTION_BRANCHES: u64 = 3;
-// const BN_MAX_STORED_DATA_LENGTH: u64 = 0x3fffffff;
-// const BN_NULL_ID: i64 = -1;
-// const BN_INVALID_REGISTER: usize = 0xffffffff;
-// const BN_AUTOCOERCE_EXTERN_PTR: u64 = 0xfffffffd;
-// const BN_NOCOERCE_EXTERN_PTR: u64 = 0xfffffffe;
-// const BN_INVALID_OPERAND: u64 = 0xffffffff;
-// const BN_MAX_STRING_LENGTH: u64 = 128;
-// const BN_MAX_VARIABLE_OFFSET: u64 = 0x7fffffffff;
-// const BN_MAX_VARIABLE_INDEX: u64 = 0xfffff;
-// const BN_MINIMUM_CONFIDENCE: u8 = 1;
-// const BN_HEURISTIC_CONFIDENCE: u8 = 192;
-
-const BN_FULL_CONFIDENCE: u8 = 255;
-const BN_INVALID_EXPR: usize = usize::MAX;
-
-unsafe extern "C" fn cb_progress_func<F: FnMut(usize, usize) -> bool>(
- ctxt: *mut std::ffi::c_void,
- progress: usize,
- total: usize,
-) -> bool {
- if ctxt.is_null() {
- return true;
- }
- let closure: &mut F = std::mem::transmute(ctxt);
- closure(progress, total)
-}
-
-unsafe extern "C" fn cb_progress_nop(
- _ctxt: *mut std::ffi::c_void,
- _arg1: usize,
- _arg2: usize
-) -> bool {
- true
-}
+use crate::progress::{NoProgressCallback, ProgressCallback};
+pub use binaryninjacore_sys::BNBranchType as BranchType;
+pub use binaryninjacore_sys::BNDataFlowQueryOption as DataFlowQueryOption;
+pub use binaryninjacore_sys::BNEndianness as Endianness;
+pub use binaryninjacore_sys::BNILBranchDependence as ILBranchDependence;
+pub const BN_FULL_CONFIDENCE: u8 = u8::MAX;
+pub 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>(
- filename: S,
-) -> Option<rc::Ref<binaryview::BinaryView>>
-where
- S: BnStrCompatible,
-{
- let filename = filename.into_bytes_with_nul();
- 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,
- Some(cb_progress_nop),
- ptr::null_mut(),
- )
- };
-
- if handle.is_null() {
- None
- } else {
- Some(unsafe { BinaryView::from_raw(handle) })
- }
+pub fn load(file_path: impl AsRef<Path>) -> Option<Ref<BinaryView>> {
+ load_with_progress(file_path, NoProgressCallback)
}
-pub fn load_with_progress<S, F>(
- filename: S,
- mut progress: F,
-) -> Option<rc::Ref<binaryview::BinaryView>>
-where
- S: BnStrCompatible,
- F: FnMut(usize, usize) -> bool,
-{
- let filename = filename.into_bytes_with_nul();
- let options = "\x00";
-
- let progress_ctx = &mut progress as *mut F as *mut std::ffi::c_void;
-
+pub fn load_with_progress<P: ProgressCallback>(
+ file_path: impl AsRef<Path>,
+ mut progress: P,
+) -> Option<Ref<BinaryView>> {
+ let file_path = file_path.as_ref().into_bytes_with_nul();
+ let options = c"";
let handle = unsafe {
- binaryninjacore_sys::BNLoadFilename(
- filename.as_ref().as_ptr() as *mut _,
+ BNLoadFilename(
+ file_path.as_ptr() as *mut _,
true,
- options.as_ptr() as *mut core::ffi::c_char,
- Some(cb_progress_func::<F>),
- progress_ctx,
+ options.as_ptr() as *mut c_char,
+ Some(P::cb_progress_callback),
+ &mut progress as *mut P as *mut c_void,
)
};
if handle.is_null() {
None
} else {
- Some(unsafe { BinaryView::from_raw(handle) })
+ Some(unsafe { BinaryView::ref_from_raw(handle) })
}
}
@@ -303,61 +154,33 @@ where
/// 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, O>(
- filename: S,
+pub fn load_with_options<O>(
+ file_path: impl AsRef<Path>,
update_analysis_and_wait: bool,
options: Option<O>,
-) -> Option<rc::Ref<binaryview::BinaryView>>
+) -> Option<Ref<BinaryView>>
where
- S: BnStrCompatible,
O: IntoJson,
{
- let filename = filename.into_bytes_with_nul();
-
- 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::BNLoadFilename(
- filename.as_ref().as_ptr() as *mut _,
- update_analysis_and_wait,
- options_or_default.as_ptr() as *mut core::ffi::c_char,
- Some(cb_progress_nop),
- core::ptr::null_mut(),
- )
- };
-
- if handle.is_null() {
- None
- } else {
- Some(unsafe { BinaryView::from_raw(handle) })
- }
+ load_with_options_and_progress(
+ file_path,
+ update_analysis_and_wait,
+ options,
+ NoProgressCallback,
+ )
}
-pub fn load_with_options_and_progress<S, O, F>(
- filename: S,
+pub fn load_with_options_and_progress<O, P>(
+ file_path: impl AsRef<Path>,
update_analysis_and_wait: bool,
options: Option<O>,
- progress: Option<F>,
-) -> Option<rc::Ref<binaryview::BinaryView>>
+ mut progress: P,
+) -> Option<Ref<BinaryView>>
where
- S: BnStrCompatible,
O: IntoJson,
- F: FnMut(usize, usize) -> bool,
+ P: ProgressCallback,
{
- let filename = filename.into_bytes_with_nul();
-
+ let file_path = file_path.as_ref().into_bytes_with_nul();
let options_or_default = if let Some(opt) = options {
opt.get_json_string()
.ok()?
@@ -371,26 +194,20 @@ where
.as_ref()
.to_vec()
};
-
- let progress_ctx = match progress {
- Some(mut x) => &mut x as *mut F as *mut std::ffi::c_void,
- None => core::ptr::null_mut()
- };
-
let handle = unsafe {
- binaryninjacore_sys::BNLoadFilename(
- filename.as_ref().as_ptr() as *mut _,
+ BNLoadFilename(
+ file_path.as_ptr() as *mut _,
update_analysis_and_wait,
- options_or_default.as_ptr() as *mut core::ffi::c_char,
- Some(cb_progress_func::<F>),
- progress_ctx,
+ options_or_default.as_ptr() as *mut c_char,
+ Some(P::cb_progress_callback),
+ &mut progress as *mut P as *mut c_void,
)
};
if handle.is_null() {
None
} else {
- Some(unsafe { BinaryView::from_raw(handle) })
+ Some(unsafe { BinaryView::ref_from_raw(handle) })
}
}
@@ -398,50 +215,22 @@ pub fn load_view<O>(
bv: &BinaryView,
update_analysis_and_wait: bool,
options: Option<O>,
-) -> Option<rc::Ref<binaryview::BinaryView>>
+) -> Option<Ref<BinaryView>>
where
O: IntoJson,
{
- 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,
- Some(cb_progress_nop),
- core::ptr::null_mut(),
- )
- };
-
- if handle.is_null() {
- None
- } else {
- Some(unsafe { BinaryView::from_raw(handle) })
- }
+ load_view_with_progress(bv, update_analysis_and_wait, options, NoProgressCallback)
}
-pub fn load_view_with_progress<O, F>(
+pub fn load_view_with_progress<O, P>(
bv: &BinaryView,
update_analysis_and_wait: bool,
options: Option<O>,
- progress: Option<F>,
-) -> Option<rc::Ref<binaryview::BinaryView>>
+ mut progress: P,
+) -> Option<Ref<BinaryView>>
where
O: IntoJson,
- F: FnMut(usize, usize) -> bool,
+ P: ProgressCallback,
{
let options_or_default = if let Some(opt) = options {
opt.get_json_string()
@@ -456,159 +245,128 @@ where
.as_ref()
.to_vec()
};
-
- let progress_ctx = match progress {
- Some(mut x) => &mut x as *mut F as *mut std::ffi::c_void,
- None => core::ptr::null_mut()
- };
-
let handle = unsafe {
- binaryninjacore_sys::BNLoadBinaryView(
+ BNLoadBinaryView(
bv.handle as *mut _,
update_analysis_and_wait,
- options_or_default.as_ptr() as *mut core::ffi::c_char,
- Some(cb_progress_func::<F>),
- progress_ctx,
+ options_or_default.as_ptr() as *mut c_char,
+ Some(P::cb_progress_callback),
+ &mut progress as *mut P as *mut c_void,
)
};
if handle.is_null() {
None
} else {
- Some(unsafe { BinaryView::from_raw(handle) })
+ Some(unsafe { BinaryView::ref_from_raw(handle) })
}
}
-pub fn install_directory() -> Result<PathBuf, ()> {
- let s: *mut std::os::raw::c_char = unsafe { binaryninjacore_sys::BNGetInstallDirectory() };
- if s.is_null() {
- return Err(());
- }
- Ok(PathBuf::from(
- unsafe { string::BnString::from_raw(s) }.to_string(),
- ))
+pub fn install_directory() -> PathBuf {
+ let install_dir_ptr: *mut c_char = unsafe { BNGetInstallDirectory() };
+ assert!(!install_dir_ptr.is_null());
+ let bn_install_dir = unsafe { BnString::from_raw(install_dir_ptr) };
+ PathBuf::from(bn_install_dir.to_string())
}
pub fn bundled_plugin_directory() -> Result<PathBuf, ()> {
- let s: *mut std::os::raw::c_char =
- unsafe { binaryninjacore_sys::BNGetBundledPluginDirectory() };
+ let s: *mut c_char = unsafe { BNGetBundledPluginDirectory() };
if s.is_null() {
return Err(());
}
- Ok(PathBuf::from(
- unsafe { string::BnString::from_raw(s) }.to_string(),
- ))
+ Ok(PathBuf::from(unsafe { BnString::from_raw(s) }.to_string()))
}
-pub fn set_bundled_plugin_directory<S: string::BnStrCompatible>(new_dir: S) {
- unsafe {
- binaryninjacore_sys::BNSetBundledPluginDirectory(
- new_dir.into_bytes_with_nul().as_ref().as_ptr() as *const std::os::raw::c_char,
- )
- };
+pub fn set_bundled_plugin_directory(new_dir: impl AsRef<Path>) {
+ let new_dir = new_dir.as_ref().into_bytes_with_nul();
+ unsafe { BNSetBundledPluginDirectory(new_dir.as_ptr() as *const c_char) };
}
-pub fn user_directory() -> Result<PathBuf, ()> {
- let s: *mut std::os::raw::c_char = unsafe { binaryninjacore_sys::BNGetUserDirectory() };
- if s.is_null() {
- return Err(());
- }
- Ok(PathBuf::from(
- unsafe { string::BnString::from_raw(s) }.to_string(),
- ))
+pub fn user_directory() -> PathBuf {
+ let user_dir_ptr: *mut c_char = unsafe { BNGetUserDirectory() };
+ assert!(!user_dir_ptr.is_null());
+ let bn_user_dir = unsafe { BnString::from_raw(user_dir_ptr) };
+ PathBuf::from(bn_user_dir.to_string())
}
pub fn user_plugin_directory() -> Result<PathBuf, ()> {
- let s: *mut std::os::raw::c_char = unsafe { binaryninjacore_sys::BNGetUserPluginDirectory() };
+ let s: *mut c_char = unsafe { BNGetUserPluginDirectory() };
if s.is_null() {
return Err(());
}
- Ok(PathBuf::from(
- unsafe { string::BnString::from_raw(s) }.to_string(),
- ))
+ Ok(PathBuf::from(unsafe { BnString::from_raw(s) }.to_string()))
}
pub fn repositories_directory() -> Result<PathBuf, ()> {
- let s: *mut std::os::raw::c_char = unsafe { binaryninjacore_sys::BNGetRepositoriesDirectory() };
+ let s: *mut c_char = unsafe { BNGetRepositoriesDirectory() };
if s.is_null() {
return Err(());
}
- Ok(PathBuf::from(
- unsafe { string::BnString::from_raw(s) }.to_string(),
- ))
+ Ok(PathBuf::from(unsafe { BnString::from_raw(s) }.to_string()))
}
-pub fn settings_file_name() -> Result<PathBuf, ()> {
- let s: *mut std::os::raw::c_char = unsafe { binaryninjacore_sys::BNGetSettingsFileName() };
- if s.is_null() {
- return Err(());
- }
- Ok(PathBuf::from(
- unsafe { string::BnString::from_raw(s) }.to_string(),
- ))
+pub fn settings_file_name() -> PathBuf {
+ let settings_file_name_ptr: *mut c_char = unsafe { BNGetSettingsFileName() };
+ assert!(!settings_file_name_ptr.is_null());
+ let bn_settings_file_name = unsafe { BnString::from_raw(settings_file_name_ptr) };
+ PathBuf::from(bn_settings_file_name.to_string())
}
+/// Write the installation directory of the currently running core instance to disk.
+///
+/// This is used to select the most recent installation for running scripts.
pub fn save_last_run() {
- unsafe { binaryninjacore_sys::BNSaveLastRun() };
+ unsafe { BNSaveLastRun() };
}
-pub fn path_relative_to_bundled_plugin_directory<S: string::BnStrCompatible>(
- path: S,
-) -> Result<PathBuf, ()> {
- let s: *mut std::os::raw::c_char = unsafe {
- binaryninjacore_sys::BNGetPathRelativeToBundledPluginDirectory(
- path.into_bytes_with_nul().as_ref().as_ptr() as *const std::os::raw::c_char,
- )
- };
+pub fn path_relative_to_bundled_plugin_directory(path: impl AsRef<Path>) -> Result<PathBuf, ()> {
+ let path_raw = path.as_ref().into_bytes_with_nul();
+ let s: *mut c_char =
+ unsafe { BNGetPathRelativeToBundledPluginDirectory(path_raw.as_ptr() as *const c_char) };
if s.is_null() {
return Err(());
}
- Ok(PathBuf::from(
- unsafe { string::BnString::from_raw(s) }.to_string(),
- ))
+ Ok(PathBuf::from(unsafe { BnString::from_raw(s) }.to_string()))
}
-pub fn path_relative_to_user_plugin_directory<S: string::BnStrCompatible>(
- path: S,
-) -> Result<PathBuf, ()> {
- let s: *mut std::os::raw::c_char = unsafe {
- binaryninjacore_sys::BNGetPathRelativeToUserPluginDirectory(
- path.into_bytes_with_nul().as_ref().as_ptr() as *const std::os::raw::c_char,
- )
- };
+pub fn path_relative_to_user_plugin_directory(path: impl AsRef<Path>) -> Result<PathBuf, ()> {
+ let path_raw = path.as_ref().into_bytes_with_nul();
+ let s: *mut c_char =
+ unsafe { BNGetPathRelativeToUserPluginDirectory(path_raw.as_ptr() as *const c_char) };
if s.is_null() {
return Err(());
}
- Ok(PathBuf::from(
- unsafe { string::BnString::from_raw(s) }.to_string(),
- ))
+ Ok(PathBuf::from(unsafe { BnString::from_raw(s) }.to_string()))
}
-pub fn path_relative_to_user_directory<S: string::BnStrCompatible>(path: S) -> Result<PathBuf, ()> {
- let s: *mut std::os::raw::c_char = unsafe {
- binaryninjacore_sys::BNGetPathRelativeToUserDirectory(
- path.into_bytes_with_nul().as_ref().as_ptr() as *const std::os::raw::c_char,
- )
- };
+pub fn path_relative_to_user_directory(path: impl AsRef<Path>) -> Result<PathBuf, ()> {
+ let path_raw = path.as_ref().into_bytes_with_nul();
+ let s: *mut c_char =
+ unsafe { BNGetPathRelativeToUserDirectory(path_raw.as_ptr() as *const c_char) };
if s.is_null() {
return Err(());
}
- Ok(PathBuf::from(
- unsafe { string::BnString::from_raw(s) }.to_string(),
- ))
+ Ok(PathBuf::from(unsafe { BnString::from_raw(s) }.to_string()))
+}
+
+/// Returns if the running thread is the "main thread"
+///
+/// If there is no registered main thread than this will always return true.
+pub fn is_main_thread() -> bool {
+ unsafe { BNIsMainThread() }
}
pub fn memory_info() -> HashMap<String, u64> {
let mut count = 0;
let mut usage = HashMap::new();
unsafe {
- let info_ptr = binaryninjacore_sys::BNGetMemoryUsageInfo(&mut count);
+ let info_ptr = BNGetMemoryUsageInfo(&mut count);
let info_list = std::slice::from_raw_parts(info_ptr, count);
for info in info_list {
let info_name = CStr::from_ptr(info.name).to_str().unwrap().to_string();
usage.insert(info_name, info.value);
}
- binaryninjacore_sys::BNFreeMemoryUsageInfo(info_ptr, count);
+ BNFreeMemoryUsageInfo(info_ptr, count);
}
usage
}
@@ -619,8 +377,7 @@ pub trait ObjectDestructor: 'static + Sync + Sized {
fn destruct_file_metadata(&self, _metadata: &FileMetadata) {}
fn destruct_function(&self, _func: &Function) {}
- unsafe extern "C" fn cb_destruct_binary_view(ctxt: *mut c_void, view: *mut BNBinaryView)
- {
+ unsafe extern "C" fn cb_destruct_binary_view(ctxt: *mut c_void, view: *mut BNBinaryView) {
ffi_wrap!("ObjectDestructor::destruct_view", {
let view_type = &*(ctxt as *mut Self);
let view = BinaryView { handle: view };
@@ -628,8 +385,7 @@ pub trait ObjectDestructor: 'static + Sync + Sized {
})
}
- unsafe extern "C" fn cb_destruct_file_metadata(ctxt: *mut c_void, file: *mut BNFileMetadata)
- {
+ unsafe extern "C" fn cb_destruct_file_metadata(ctxt: *mut c_void, file: *mut BNFileMetadata) {
ffi_wrap!("ObjectDestructor::destruct_file_metadata", {
let view_type = &*(ctxt as *mut Self);
let file = FileMetadata::from_raw(file);
@@ -637,15 +393,14 @@ pub trait ObjectDestructor: 'static + Sync + Sized {
})
}
- unsafe extern "C" fn cb_destruct_function(ctxt: *mut c_void, func: *mut BNFunction)
- {
+ unsafe extern "C" fn cb_destruct_function(ctxt: *mut c_void, func: *mut BNFunction) {
ffi_wrap!("ObjectDestructor::destruct_function", {
let view_type = &*(ctxt as *mut Self);
let func = Function { handle: func };
view_type.destruct_function(&func);
})
}
-
+
unsafe fn as_callbacks(&'static mut self) -> BNObjectDestructionCallbacks {
BNObjectDestructionCallbacks {
context: std::mem::transmute(&self),
@@ -654,22 +409,22 @@ pub trait ObjectDestructor: 'static + Sync + Sized {
destructFunction: Some(Self::cb_destruct_function),
}
}
-
+
fn register(&'static mut self) {
unsafe { BNRegisterObjectDestructionCallbacks(&mut self.as_callbacks()) };
}
-
+
fn unregister(&'static mut self) {
unsafe { BNUnregisterObjectDestructionCallbacks(&mut self.as_callbacks()) };
}
}
-pub fn version() -> string::BnString {
- unsafe { string::BnString::from_raw(binaryninjacore_sys::BNGetVersionString()) }
+pub fn version() -> BnString {
+ unsafe { BnString::from_raw(BNGetVersionString()) }
}
pub fn build_id() -> u32 {
- unsafe { binaryninjacore_sys::BNGetBuildId() }
+ unsafe { BNGetBuildId() }
}
#[derive(Clone, PartialEq, Eq, Hash)]
@@ -677,115 +432,124 @@ pub struct VersionInfo {
pub major: u32,
pub minor: u32,
pub build: u32,
- pub channel: string::BnString,
+ pub channel: String,
}
-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) },
+impl VersionInfo {
+ pub(crate) fn from_owned_raw(value: BNVersionInfo) -> Self {
+ Self {
+ major: value.major,
+ minor: value.minor,
+ build: value.build,
+ channel: unsafe { BnString::from_raw(value.channel) }.to_string(),
+ }
}
}
-pub fn serial_number() -> string::BnString {
- unsafe { string::BnString::from_raw(binaryninjacore_sys::BNGetSerialNumber()) }
+pub fn version_info() -> VersionInfo {
+ let info_raw = unsafe { BNGetVersionInfo() };
+ VersionInfo::from_owned_raw(info_raw)
+}
+
+pub fn serial_number() -> BnString {
+ unsafe { BnString::from_raw(BNGetSerialNumber()) }
}
pub fn is_license_validated() -> bool {
- unsafe { binaryninjacore_sys::BNIsLicenseValidated() }
+ unsafe { BNIsLicenseValidated() }
+}
+
+pub fn licensed_user_email() -> BnString {
+ unsafe { BnString::from_raw(BNGetLicensedUserEmail()) }
}
-pub fn licensed_user_email() -> string::BnString {
- unsafe { string::BnString::from_raw(binaryninjacore_sys::BNGetLicensedUserEmail()) }
+pub fn license_path() -> PathBuf {
+ user_directory().join("license.dat")
}
pub fn license_count() -> i32 {
- unsafe { binaryninjacore_sys::BNGetLicenseCount() }
+ unsafe { BNGetLicenseCount() }
}
-pub fn set_license<S: string::BnStrCompatible>(license: S) {
- let license = license.into_bytes_with_nul();
+/// Set the license that will be used once the core initializes. You can reset the license by passing `None`.
+///
+/// If not set the normal license retrieval will occur:
+/// 1. Check the BN_LICENSE environment variable
+/// 2. Check the Binary Ninja user directory for license.dat
+pub fn set_license<S: BnStrCompatible + Default>(license: Option<S>) {
+ let license = license.unwrap_or_default().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) }
+ unsafe { BNSetLicense(license_slice.as_ptr() as *const c_char) }
}
-pub fn product() -> string::BnString {
- unsafe { string::BnString::from_raw(binaryninjacore_sys::BNGetProduct()) }
+pub fn product() -> BnString {
+ unsafe { BnString::from_raw(BNGetProduct()) }
}
-pub fn product_type() -> string::BnString {
- unsafe { string::BnString::from_raw(binaryninjacore_sys::BNGetProductType()) }
+pub fn product_type() -> BnString {
+ unsafe { BnString::from_raw(BNGetProductType()) }
}
pub fn license_expiration_time() -> std::time::SystemTime {
- let m = std::time::Duration::from_secs(unsafe {
- binaryninjacore_sys::BNGetLicenseExpirationTime()
- });
+ let m = std::time::Duration::from_secs(unsafe { BNGetLicenseExpirationTime() });
std::time::UNIX_EPOCH + m
}
pub fn is_ui_enabled() -> bool {
- unsafe { binaryninjacore_sys::BNIsUIEnabled() }
+ unsafe { BNIsUIEnabled() }
}
-pub fn is_database<S: string::BnStrCompatible>(filename: S) -> bool {
+pub fn is_database<S: 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) }
+ unsafe { BNIsDatabase(filename_slice.as_ptr() as *const c_char) }
}
pub fn plugin_abi_version() -> u32 {
- binaryninjacore_sys::BN_CURRENT_CORE_ABI_VERSION
+ BN_CURRENT_CORE_ABI_VERSION
}
pub fn plugin_abi_minimum_version() -> u32 {
- binaryninjacore_sys::BN_MINIMUM_CORE_ABI_VERSION
+ BN_MINIMUM_CORE_ABI_VERSION
}
pub fn core_abi_version() -> u32 {
- unsafe { binaryninjacore_sys::BNGetCurrentCoreABIVersion() }
+ unsafe { BNGetCurrentCoreABIVersion() }
}
pub fn core_abi_minimum_version() -> u32 {
- unsafe { binaryninjacore_sys::BNGetMinimumCoreABIVersion() }
+ unsafe { BNGetMinimumCoreABIVersion() }
}
pub fn plugin_ui_abi_version() -> u32 {
- binaryninjacore_sys::BN_CURRENT_UI_ABI_VERSION
+ BN_CURRENT_UI_ABI_VERSION
}
pub fn plugin_ui_abi_minimum_version() -> u32 {
- binaryninjacore_sys::BN_MINIMUM_UI_ABI_VERSION
+ BN_MINIMUM_UI_ABI_VERSION
}
-pub fn add_required_plugin_dependency<S: string::BnStrCompatible>(name: S) {
+pub fn add_required_plugin_dependency<S: BnStrCompatible>(name: S) {
unsafe {
- binaryninjacore_sys::BNAddRequiredPluginDependency(
- name.into_bytes_with_nul().as_ref().as_ptr() as *const std::os::raw::c_char,
- )
+ BNAddRequiredPluginDependency(name.into_bytes_with_nul().as_ref().as_ptr() as *const c_char)
};
}
-pub fn add_optional_plugin_dependency<S: string::BnStrCompatible>(name: S) {
+pub fn add_optional_plugin_dependency<S: BnStrCompatible>(name: S) {
unsafe {
- binaryninjacore_sys::BNAddOptionalPluginDependency(
- name.into_bytes_with_nul().as_ref().as_ptr() as *const std::os::raw::c_char,
- )
+ BNAddOptionalPluginDependency(name.into_bytes_with_nul().as_ref().as_ptr() as *const c_char)
};
}
// Provide ABI version automatically so that the core can verify binary compatibility
-#[cfg(not(feature = "noexports"))]
+#[cfg(not(feature = "no_exports"))]
#[no_mangle]
#[allow(non_snake_case)]
pub extern "C" fn CorePluginABIVersion() -> u32 {
plugin_abi_version()
}
-#[cfg(not(feature = "noexports"))]
+#[cfg(not(feature = "no_exports"))]
#[no_mangle]
pub extern "C" fn UIPluginABIVersion() -> u32 {
plugin_ui_abi_version()
diff --git a/rust/src/linearview.rs b/rust/src/linear_view.rs
index b90d2784..dc3ca597 100644
--- a/rust/src/linearview.rs
+++ b/rust/src/linear_view.rs
@@ -16,7 +16,7 @@
use binaryninjacore_sys::*;
-use crate::binaryview::BinaryView;
+use crate::binary_view::BinaryView;
use crate::disassembly::{DisassemblySettings, DisassemblyTextLine};
use crate::function::Function;
@@ -25,81 +25,65 @@ use std::ops::Deref;
use std::mem;
+pub type LinearDisassemblyLineType = BNLinearDisassemblyLineType;
+
+// TODO: Rename to LinearView?
pub struct LinearViewObject {
pub(crate) handle: *mut BNLinearViewObject,
}
impl LinearViewObject {
- pub(crate) unsafe fn from_raw(handle: *mut BNLinearViewObject) -> Ref<Self> {
+ pub(crate) unsafe fn ref_from_raw(handle: *mut BNLinearViewObject) -> Ref<Self> {
debug_assert!(!handle.is_null());
-
Ref::new(Self { handle })
}
pub fn data_only(view: &BinaryView, settings: &DisassemblySettings) -> Ref<Self> {
unsafe {
- let handle =
- binaryninjacore_sys::BNCreateLinearViewDataOnly(view.handle, settings.handle);
-
- Self::from_raw(handle)
+ let handle = BNCreateLinearViewDataOnly(view.handle, settings.handle);
+ Self::ref_from_raw(handle)
}
}
pub fn disassembly(view: &BinaryView, settings: &DisassemblySettings) -> Ref<Self> {
unsafe {
- let handle =
- binaryninjacore_sys::BNCreateLinearViewDisassembly(view.handle, settings.handle);
-
- Self::from_raw(handle)
+ let handle = BNCreateLinearViewDisassembly(view.handle, settings.handle);
+ Self::ref_from_raw(handle)
}
}
pub fn lifted_il(view: &BinaryView, settings: &DisassemblySettings) -> Ref<Self> {
unsafe {
- let handle =
- binaryninjacore_sys::BNCreateLinearViewLiftedIL(view.handle, settings.handle);
-
- Self::from_raw(handle)
+ let handle = BNCreateLinearViewLiftedIL(view.handle, settings.handle);
+ Self::ref_from_raw(handle)
}
}
pub fn mlil(view: &BinaryView, settings: &DisassemblySettings) -> Ref<Self> {
unsafe {
- let handle =
- binaryninjacore_sys::BNCreateLinearViewMediumLevelIL(view.handle, settings.handle);
-
- Self::from_raw(handle)
+ let handle = BNCreateLinearViewMediumLevelIL(view.handle, settings.handle);
+ Self::ref_from_raw(handle)
}
}
pub fn mlil_ssa(view: &BinaryView, settings: &DisassemblySettings) -> Ref<Self> {
unsafe {
- let handle = binaryninjacore_sys::BNCreateLinearViewMediumLevelILSSAForm(
- view.handle,
- settings.handle,
- );
-
- Self::from_raw(handle)
+ let handle = BNCreateLinearViewMediumLevelILSSAForm(view.handle, settings.handle);
+ Self::ref_from_raw(handle)
}
}
pub fn hlil(view: &BinaryView, settings: &DisassemblySettings) -> Ref<Self> {
unsafe {
- let handle =
- binaryninjacore_sys::BNCreateLinearViewHighLevelIL(view.handle, settings.handle);
-
- Self::from_raw(handle)
+ let handle = BNCreateLinearViewHighLevelIL(view.handle, settings.handle);
+ Self::ref_from_raw(handle)
}
}
pub fn hlil_ssa(view: &BinaryView, settings: &DisassemblySettings) -> Ref<Self> {
unsafe {
- let handle = binaryninjacore_sys::BNCreateLinearViewHighLevelILSSAForm(
- view.handle,
- settings.handle,
- );
-
- Self::from_raw(handle)
+ let handle = BNCreateLinearViewHighLevelILSSAForm(view.handle, settings.handle);
+ Self::ref_from_raw(handle)
}
}
@@ -110,13 +94,13 @@ impl LinearViewObject {
) -> Ref<Self> {
unsafe {
let language = std::ffi::CString::new(language).unwrap();
- let handle = binaryninjacore_sys::BNCreateLinearViewLanguageRepresentation(
+ let handle = BNCreateLinearViewLanguageRepresentation(
view.handle,
settings.handle,
language.as_ptr(),
);
- Self::from_raw(handle)
+ Self::ref_from_raw(handle)
}
}
@@ -125,12 +109,9 @@ impl LinearViewObject {
settings: &DisassemblySettings,
) -> Ref<Self> {
unsafe {
- let handle = binaryninjacore_sys::BNCreateLinearViewSingleFunctionDisassembly(
- function.handle,
- settings.handle,
- );
-
- Self::from_raw(handle)
+ let handle =
+ BNCreateLinearViewSingleFunctionDisassembly(function.handle, settings.handle);
+ Self::ref_from_raw(handle)
}
}
@@ -139,23 +120,16 @@ impl LinearViewObject {
settings: &DisassemblySettings,
) -> Ref<Self> {
unsafe {
- let handle = binaryninjacore_sys::BNCreateLinearViewSingleFunctionLiftedIL(
- function.handle,
- settings.handle,
- );
-
- Self::from_raw(handle)
+ let handle = BNCreateLinearViewSingleFunctionLiftedIL(function.handle, settings.handle);
+ Self::ref_from_raw(handle)
}
}
pub fn single_function_mlil(function: &Function, settings: &DisassemblySettings) -> Ref<Self> {
unsafe {
- let handle = binaryninjacore_sys::BNCreateLinearViewSingleFunctionMediumLevelIL(
- function.handle,
- settings.handle,
- );
-
- Self::from_raw(handle)
+ let handle =
+ BNCreateLinearViewSingleFunctionMediumLevelIL(function.handle, settings.handle);
+ Self::ref_from_raw(handle)
}
}
@@ -164,23 +138,19 @@ impl LinearViewObject {
settings: &DisassemblySettings,
) -> Ref<Self> {
unsafe {
- let handle = binaryninjacore_sys::BNCreateLinearViewSingleFunctionMediumLevelILSSAForm(
+ let handle = BNCreateLinearViewSingleFunctionMediumLevelILSSAForm(
function.handle,
settings.handle,
);
-
- Self::from_raw(handle)
+ Self::ref_from_raw(handle)
}
}
pub fn single_function_hlil(function: &Function, settings: &DisassemblySettings) -> Ref<Self> {
unsafe {
- let handle = binaryninjacore_sys::BNCreateLinearViewSingleFunctionHighLevelIL(
- function.handle,
- settings.handle,
- );
-
- Self::from_raw(handle)
+ let handle =
+ BNCreateLinearViewSingleFunctionHighLevelIL(function.handle, settings.handle);
+ Self::ref_from_raw(handle)
}
}
@@ -189,12 +159,11 @@ impl LinearViewObject {
settings: &DisassemblySettings,
) -> Ref<Self> {
unsafe {
- let handle = binaryninjacore_sys::BNCreateLinearViewSingleFunctionHighLevelILSSAForm(
+ let handle = BNCreateLinearViewSingleFunctionHighLevelILSSAForm(
function.handle,
settings.handle,
);
-
- Self::from_raw(handle)
+ Self::ref_from_raw(handle)
}
}
@@ -205,14 +174,19 @@ impl LinearViewObject {
) -> Ref<Self> {
unsafe {
let language = std::ffi::CString::new(language).unwrap();
- let handle =
- binaryninjacore_sys::BNCreateLinearViewSingleFunctionLanguageRepresentation(
- function.handle,
- settings.handle,
- language.as_ptr(),
- );
+ let handle = BNCreateLinearViewSingleFunctionLanguageRepresentation(
+ function.handle,
+ settings.handle,
+ language.as_ptr(),
+ );
+ Self::ref_from_raw(handle)
+ }
+ }
- Self::from_raw(handle)
+ pub fn create_cursor(&self) -> Ref<LinearViewCursor> {
+ unsafe {
+ let handle = BNCreateLinearViewCursor(self.handle);
+ LinearViewCursor::ref_from_raw(handle)
}
}
}
@@ -242,36 +216,27 @@ unsafe impl Sync for LinearViewObject {}
#[derive(Eq)]
pub struct LinearViewCursor {
- pub(crate) handle: *mut binaryninjacore_sys::BNLinearViewCursor,
+ pub(crate) handle: *mut BNLinearViewCursor,
}
impl LinearViewCursor {
- pub(crate) unsafe fn from_raw(handle: *mut BNLinearViewCursor) -> Ref<Self> {
+ pub(crate) unsafe fn ref_from_raw(handle: *mut BNLinearViewCursor) -> Ref<Self> {
debug_assert!(!handle.is_null());
-
Ref::new(Self { handle })
}
- pub fn new(root: &LinearViewObject) -> Ref<Self> {
- unsafe {
- let handle = BNCreateLinearViewCursor(root.handle);
- Self::from_raw(handle)
- }
- }
-
- /// Gets the current [LinearViewObject] associated with this cursor.
+ /// Gets the current [`LinearViewObject`] associated with this cursor.
pub fn current_object(&self) -> Ref<LinearViewObject> {
unsafe {
let handle = BNGetLinearViewCursorCurrentObject(self.handle);
- LinearViewObject::from_raw(handle)
+ LinearViewObject::ref_from_raw(handle)
}
}
- // FIXME: can we implement clone without shadowing ToOwned?
pub fn duplicate(&self) -> Ref<Self> {
unsafe {
let handle = BNDuplicateLinearViewCursor(self.handle);
- Self::from_raw(handle)
+ Self::ref_from_raw(handle)
}
}
@@ -376,13 +341,12 @@ impl ToOwned for LinearViewCursor {
unsafe impl Send for LinearViewCursor {}
unsafe impl Sync for LinearViewCursor {}
-pub type LinearDisassemblyLineType = BNLinearDisassemblyLineType;
-
pub struct LinearDisassemblyLine {
t: LinearDisassemblyLineType,
// These will be cleaned up by BNFreeLinearDisassemblyLines, so we
// don't drop them in the relevant deconstructors.
+ // TODO: This is insane!
function: mem::ManuallyDrop<Ref<Function>>,
contents: mem::ManuallyDrop<DisassemblyTextLine>,
}
@@ -390,8 +354,9 @@ pub struct LinearDisassemblyLine {
impl LinearDisassemblyLine {
pub(crate) unsafe fn from_raw(raw: &BNLinearDisassemblyLine) -> Self {
let linetype = raw.type_;
- let function = mem::ManuallyDrop::new(Function::from_raw(raw.function));
- let contents = mem::ManuallyDrop::new(DisassemblyTextLine(raw.contents));
+ // TODO: We must remove this behavior.
+ let function = mem::ManuallyDrop::new(Function::ref_from_raw(raw.function));
+ let contents = mem::ManuallyDrop::new(DisassemblyTextLine::from_raw(&raw.contents));
Self {
t: linetype,
function,
@@ -428,10 +393,12 @@ impl CoreArrayProvider for LinearDisassemblyLine {
}
unsafe impl CoreArrayProviderInner for LinearDisassemblyLine {
- unsafe fn free(raw: *mut BNLinearDisassemblyLine, count: usize, _context: &()) {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeLinearDisassemblyLines(raw, count);
}
- unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
- Guard::new(LinearDisassemblyLine::from_raw(raw), _context)
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
+ // TODO: Cant remove this guard until we remove those manual drops... INSANE!
+ Guard::new(Self::from_raw(raw), context)
}
}
diff --git a/rust/src/llil/block.rs b/rust/src/llil/block.rs
deleted file mode 100644
index d904d2d8..00000000
--- a/rust/src/llil/block.rs
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright 2021-2024 Vector 35 Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use std::ops::Range;
-
-use crate::architecture::Architecture;
-use crate::basicblock::{BasicBlock, BlockContext};
-
-use super::*;
-
-pub struct BlockIter<'func, A, M, F>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- F: FunctionForm,
-{
- function: &'func Function<A, M, F>,
- range: Range<u64>,
-}
-
-impl<'func, A, M, F> Iterator for BlockIter<'func, A, M, F>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- F: FunctionForm,
-{
- type Item = Instruction<'func, A, M, F>;
-
- fn next(&mut self) -> Option<Self::Item> {
- self.range.next().map(|i| Instruction {
- function: self.function,
- instr_idx: i as usize,
- })
- }
-}
-
-pub struct Block<'func, A, M, F>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- F: FunctionForm,
-{
- pub(crate) function: &'func Function<A, M, F>,
-}
-
-impl<'func, A, M, F> fmt::Debug for Block<'func, A, M, F>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- F: FunctionForm,
-{
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "llil_bb {:?}", self.function)
- }
-}
-
-impl<'func, A, M, F> BlockContext for Block<'func, A, M, F>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- F: FunctionForm,
-{
- type Iter = BlockIter<'func, A, M, F>;
- type Instruction = Instruction<'func, A, M, F>;
-
- fn start(&self, block: &BasicBlock<Self>) -> Instruction<'func, A, M, F> {
- Instruction {
- function: self.function,
- instr_idx: block.raw_start() as usize,
- }
- }
-
- fn iter(&self, block: &BasicBlock<Self>) -> BlockIter<'func, A, M, F> {
- BlockIter {
- function: self.function,
- range: block.raw_start()..block.raw_end(),
- }
- }
-}
-
-impl<'func, A, M, F> Clone for Block<'func, A, M, F>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- F: FunctionForm,
-{
- fn clone(&self) -> Self {
- Block {
- function: self.function,
- }
- }
-}
diff --git a/rust/src/llil/expression.rs b/rust/src/llil/expression.rs
deleted file mode 100644
index f04368a7..00000000
--- a/rust/src/llil/expression.rs
+++ /dev/null
@@ -1,851 +0,0 @@
-// Copyright 2021-2024 Vector 35 Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use binaryninjacore_sys::BNGetLowLevelILByIndex;
-use binaryninjacore_sys::BNLowLevelILInstruction;
-
-use std::fmt;
-use std::marker::PhantomData;
-
-use super::operation;
-use super::operation::Operation;
-use super::*;
-
-use crate::architecture::Architecture;
-use crate::architecture::RegisterInfo;
-
-// used as a marker for Expressions that can produce a value
-#[derive(Copy, Clone, Debug)]
-pub struct ValueExpr;
-
-// used as a marker for Expressions that can not produce a value
-#[derive(Copy, Clone, Debug)]
-pub struct VoidExpr;
-
-pub trait ExpressionResultType: 'static {}
-impl ExpressionResultType for ValueExpr {}
-impl ExpressionResultType for VoidExpr {}
-
-pub struct Expression<'func, A, M, F, R>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- F: FunctionForm,
- R: ExpressionResultType,
-{
- pub(crate) function: &'func Function<A, M, F>,
- pub(crate) expr_idx: usize,
-
- // tag the 'return' type of this expression
- pub(crate) _ty: PhantomData<R>,
-}
-
-impl<'func, A, M, F, R> Expression<'func, A, M, F, R>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- F: FunctionForm,
- R: ExpressionResultType,
-{
- pub(crate) fn new(function: &'func Function<A, M, F>, expr_idx: usize) -> Self {
- Self {
- function,
- expr_idx,
- _ty: PhantomData,
- }
- }
-
- pub fn index(&self) -> usize {
- self.expr_idx
- }
-}
-
-impl<'func, A, M, V> fmt::Debug for Expression<'func, A, M, NonSSA<V>, ValueExpr>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- V: NonSSAVariant,
-{
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- let op_info = self.info();
- write!(f, "<expr {}: {:?}>", self.expr_idx, op_info)
- }
-}
-
-fn common_info<'func, A, M, F>(
- function: &'func Function<A, M, F>,
- op: BNLowLevelILInstruction,
-) -> ExprInfo<'func, A, M, F>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- F: FunctionForm,
-{
- use binaryninjacore_sys::BNLowLevelILOperation::*;
-
- match op.operation {
- LLIL_CONST => ExprInfo::Const(Operation::new(function, op)),
- LLIL_CONST_PTR => ExprInfo::ConstPtr(Operation::new(function, op)),
-
- LLIL_EXTERN_PTR => ExprInfo::ExternPtr(Operation::new(function, op)),
-
- LLIL_ADD => ExprInfo::Add(Operation::new(function, op)),
- LLIL_ADC => ExprInfo::Adc(Operation::new(function, op)),
- LLIL_SUB => ExprInfo::Sub(Operation::new(function, op)),
- LLIL_SBB => ExprInfo::Sbb(Operation::new(function, op)),
- LLIL_AND => ExprInfo::And(Operation::new(function, op)),
- LLIL_OR => ExprInfo::Or(Operation::new(function, op)),
- LLIL_XOR => ExprInfo::Xor(Operation::new(function, op)),
- LLIL_LSL => ExprInfo::Lsl(Operation::new(function, op)),
- LLIL_LSR => ExprInfo::Lsr(Operation::new(function, op)),
- LLIL_ASR => ExprInfo::Asr(Operation::new(function, op)),
- LLIL_ROL => ExprInfo::Rol(Operation::new(function, op)),
- LLIL_RLC => ExprInfo::Rlc(Operation::new(function, op)),
- LLIL_ROR => ExprInfo::Ror(Operation::new(function, op)),
- LLIL_RRC => ExprInfo::Rrc(Operation::new(function, op)),
- LLIL_MUL => ExprInfo::Mul(Operation::new(function, op)),
-
- LLIL_MULU_DP => ExprInfo::MuluDp(Operation::new(function, op)),
- LLIL_MULS_DP => ExprInfo::MulsDp(Operation::new(function, op)),
-
- LLIL_DIVU => ExprInfo::Divu(Operation::new(function, op)),
- LLIL_DIVS => ExprInfo::Divs(Operation::new(function, op)),
-
- LLIL_DIVU_DP => ExprInfo::DivuDp(Operation::new(function, op)),
- LLIL_DIVS_DP => ExprInfo::DivsDp(Operation::new(function, op)),
-
- LLIL_MODU => ExprInfo::Modu(Operation::new(function, op)),
- LLIL_MODS => ExprInfo::Mods(Operation::new(function, op)),
-
- LLIL_MODU_DP => ExprInfo::ModuDp(Operation::new(function, op)),
- LLIL_MODS_DP => ExprInfo::ModsDp(Operation::new(function, op)),
-
- LLIL_NEG => ExprInfo::Neg(Operation::new(function, op)),
- LLIL_NOT => ExprInfo::Not(Operation::new(function, op)),
-
- LLIL_SX => ExprInfo::Sx(Operation::new(function, op)),
- 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)),
- LLIL_CMP_ULT => ExprInfo::CmpUlt(Operation::new(function, op)),
- LLIL_CMP_SLE => ExprInfo::CmpSle(Operation::new(function, op)),
- LLIL_CMP_ULE => ExprInfo::CmpUle(Operation::new(function, op)),
- LLIL_CMP_SGE => ExprInfo::CmpSge(Operation::new(function, op)),
- LLIL_CMP_UGE => ExprInfo::CmpUge(Operation::new(function, op)),
- LLIL_CMP_SGT => ExprInfo::CmpSgt(Operation::new(function, op)),
- LLIL_CMP_UGT => ExprInfo::CmpUgt(Operation::new(function, op)),
-
- LLIL_BOOL_TO_INT => ExprInfo::BoolToInt(Operation::new(function, op)),
-
- LLIL_FADD => ExprInfo::Fadd(Operation::new(function, op)),
- LLIL_FSUB => ExprInfo::Fsub(Operation::new(function, op)),
- LLIL_FMUL => ExprInfo::Fmul(Operation::new(function, op)),
- LLIL_FDIV => ExprInfo::Fdiv(Operation::new(function, op)),
-
- LLIL_FSQRT => ExprInfo::Fsqrt(Operation::new(function, op)),
- LLIL_FNEG => ExprInfo::Fneg(Operation::new(function, op)),
- LLIL_FABS => ExprInfo::Fabs(Operation::new(function, op)),
- LLIL_FLOAT_TO_INT => ExprInfo::FloatToInt(Operation::new(function, op)),
- LLIL_INT_TO_FLOAT => ExprInfo::IntToFloat(Operation::new(function, op)),
- LLIL_FLOAT_CONV => ExprInfo::FloatConv(Operation::new(function, op)),
- LLIL_ROUND_TO_INT => ExprInfo::RoundToInt(Operation::new(function, op)),
- LLIL_FLOOR => ExprInfo::Floor(Operation::new(function, op)),
- LLIL_CEIL => ExprInfo::Ceil(Operation::new(function, op)),
- LLIL_FTRUNC => ExprInfo::Ftrunc(Operation::new(function, op)),
-
- LLIL_FCMP_E => ExprInfo::FcmpE(Operation::new(function, op)),
- LLIL_FCMP_NE => ExprInfo::FcmpNE(Operation::new(function, op)),
- LLIL_FCMP_LT => ExprInfo::FcmpLT(Operation::new(function, op)),
- LLIL_FCMP_LE => ExprInfo::FcmpLE(Operation::new(function, op)),
- LLIL_FCMP_GT => ExprInfo::FcmpGT(Operation::new(function, op)),
- LLIL_FCMP_GE => ExprInfo::FcmpGE(Operation::new(function, op)),
- LLIL_FCMP_O => ExprInfo::FcmpO(Operation::new(function, op)),
- LLIL_FCMP_UO => ExprInfo::FcmpUO(Operation::new(function, op)),
-
- LLIL_UNIMPL => ExprInfo::Unimpl(Operation::new(function, op)),
- LLIL_UNIMPL_MEM => ExprInfo::UnimplMem(Operation::new(function, op)),
-
- // TODO TEST_BIT ADD_OVERFLOW LLIL_REG_STACK_PUSH LLIL_REG_STACK_POP
- _ => {
- #[cfg(debug_assertions)]
- {
- error!(
- "Got unexpected operation {:?} in value expr at 0x{:x}",
- op.operation, op.address
- );
- }
-
- ExprInfo::Undef(Operation::new(function, op))
- }
- }
-}
-
-use super::VisitorAction;
-
-macro_rules! visit {
- ($f:expr, $($e:expr),*) => {
- if let VisitorAction::Halt = $f($($e,)*) {
- return VisitorAction::Halt;
- }
- }
-}
-
-fn common_visit<'func, A, M, F, CB>(info: &ExprInfo<'func, A, M, F>, f: &mut CB) -> VisitorAction
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- F: FunctionForm,
- CB: FnMut(&Expression<'func, A, M, F, ValueExpr>) -> VisitorAction,
-{
- use self::ExprInfo::*;
-
- match *info {
- CmpE(ref op) | CmpNe(ref op) | CmpSlt(ref op) | CmpUlt(ref op) | CmpSle(ref op)
- | CmpUle(ref op) | CmpSge(ref op) | CmpUge(ref op) | CmpSgt(ref op) | CmpUgt(ref op)
- | FcmpE(ref op) | FcmpNE(ref op) | FcmpLT(ref op) | FcmpLE(ref op) | FcmpGE(ref op)
- | FcmpGT(ref op) | FcmpO(ref op) | FcmpUO(ref op) => {
- visit!(f, &op.left());
- visit!(f, &op.right());
- }
-
- Adc(ref op) | Sbb(ref op) | Rlc(ref op) | Rrc(ref op) => {
- visit!(f, &op.left());
- visit!(f, &op.right());
- visit!(f, &op.carry());
- }
-
- Add(ref op) | Sub(ref op) | And(ref op) | Or(ref op) | Xor(ref op) | Lsl(ref op)
- | Lsr(ref op) | Asr(ref op) | Rol(ref op) | Ror(ref op) | Mul(ref op) | MulsDp(ref op)
- | MuluDp(ref op) | Divu(ref op) | Divs(ref op) | Modu(ref op) | Mods(ref op)
- | Fadd(ref op) | Fsub(ref op) | Fmul(ref op) | Fdiv(ref op) => {
- visit!(f, &op.left());
- visit!(f, &op.right());
- }
-
- DivuDp(ref op) | DivsDp(ref op) | ModuDp(ref op) | ModsDp(ref op) => {
- visit!(f, &op.high());
- visit!(f, &op.low());
- visit!(f, &op.right());
- }
-
- Neg(ref op) | Not(ref op) | Sx(ref op) | Zx(ref op) | LowPart(ref op)
- | BoolToInt(ref op) | Fsqrt(ref op) | Fneg(ref op) | Fabs(ref op) | FloatToInt(ref op)
- | IntToFloat(ref op) | FloatConv(ref op) | RoundToInt(ref op) | Floor(ref op)
- | Ceil(ref op) | Ftrunc(ref op) => {
- visit!(f, &op.operand());
- }
-
- UnimplMem(ref op) => {
- visit!(f, &op.mem_expr());
- }
-
- _ => {}
- };
-
- VisitorAction::Sibling
-}
-
-impl<'func, A, M, V> Expression<'func, A, M, NonSSA<V>, ValueExpr>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- V: NonSSAVariant,
-{
- pub(crate) unsafe fn info_from_op(
- &self,
- op: BNLowLevelILInstruction,
- ) -> ExprInfo<'func, A, M, NonSSA<V>> {
- use binaryninjacore_sys::BNLowLevelILOperation::*;
-
- match op.operation {
- 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
- LLIL_FLAG_GROUP => ExprInfo::FlagGroup(Operation::new(self.function, op)), // TODO lifted only
- _ => common_info(self.function, op),
- }
- }
-
- pub fn info(&self) -> ExprInfo<'func, A, M, NonSSA<V>> {
- unsafe {
- let op = BNGetLowLevelILByIndex(self.function.handle, self.expr_idx);
- self.info_from_op(op)
- }
- }
-
- pub fn visit_tree<F>(&self, f: &mut F) -> VisitorAction
- where
- F: FnMut(&Self, &ExprInfo<'func, A, M, NonSSA<V>>) -> VisitorAction,
- {
- use self::ExprInfo::*;
-
- let info = self.info();
-
- match f(self, &info) {
- VisitorAction::Descend => {}
- action => return action,
- };
-
- match info {
- Load(ref op) => visit!(Self::visit_tree, &op.source_mem_expr(), f),
- _ => {
- let mut fb = |e: &Self| e.visit_tree(f);
- visit!(common_visit, &info, &mut fb);
- }
- };
-
- VisitorAction::Sibling
- }
-}
-
-impl<'func, A, M> Expression<'func, A, M, SSA, ValueExpr>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
-{
- pub(crate) unsafe fn info_from_op(
- &self,
- op: BNLowLevelILInstruction,
- ) -> ExprInfo<'func, A, M, SSA> {
- use binaryninjacore_sys::BNLowLevelILOperation::*;
-
- 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),
- }
- }
-
- pub fn info(&self) -> ExprInfo<'func, A, M, SSA> {
- unsafe {
- let op = BNGetLowLevelILByIndex(self.function.handle, self.expr_idx);
- self.info_from_op(op)
- }
- }
-
- pub fn visit_tree<F>(&self, f: &mut F) -> VisitorAction
- where
- F: FnMut(&Self, &ExprInfo<'func, A, M, SSA>) -> VisitorAction,
- {
- use self::ExprInfo::*;
-
- let info = self.info();
-
- match f(self, &info) {
- VisitorAction::Descend => {}
- action => return action,
- };
-
- match info {
- // TODO ssa
- Load(ref _op) => {} //visit!(Self::visit_tree, &op.source_mem_expr(), f),
- _ => {
- let mut fb = |e: &Self| e.visit_tree(f);
- visit!(common_visit, &info, &mut fb);
- }
- };
-
- VisitorAction::Sibling
- }
-}
-
-impl<'func, A, F> Expression<'func, A, Finalized, F, ValueExpr>
-where
- A: 'func + Architecture,
- F: FunctionForm,
-{
- // TODO possible values
-}
-
-pub enum ExprInfo<'func, A, M, F>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- F: FunctionForm,
-{
- 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>),
- FlagBit(Operation<'func, A, M, F, operation::FlagBit>),
- ExternPtr(Operation<'func, A, M, F, operation::Extern>),
-
- Add(Operation<'func, A, M, F, operation::BinaryOp>),
- Adc(Operation<'func, A, M, F, operation::BinaryOpCarry>),
- Sub(Operation<'func, A, M, F, operation::BinaryOp>),
- Sbb(Operation<'func, A, M, F, operation::BinaryOpCarry>),
- And(Operation<'func, A, M, F, operation::BinaryOp>),
- Or(Operation<'func, A, M, F, operation::BinaryOp>),
- Xor(Operation<'func, A, M, F, operation::BinaryOp>),
- Lsl(Operation<'func, A, M, F, operation::BinaryOp>),
- Lsr(Operation<'func, A, M, F, operation::BinaryOp>),
- Asr(Operation<'func, A, M, F, operation::BinaryOp>),
- Rol(Operation<'func, A, M, F, operation::BinaryOp>),
- Rlc(Operation<'func, A, M, F, operation::BinaryOpCarry>),
- Ror(Operation<'func, A, M, F, operation::BinaryOp>),
- Rrc(Operation<'func, A, M, F, operation::BinaryOpCarry>),
- Mul(Operation<'func, A, M, F, operation::BinaryOp>),
-
- MulsDp(Operation<'func, A, M, F, operation::BinaryOp>),
- MuluDp(Operation<'func, A, M, F, operation::BinaryOp>),
-
- Divu(Operation<'func, A, M, F, operation::BinaryOp>),
- Divs(Operation<'func, A, M, F, operation::BinaryOp>),
-
- DivuDp(Operation<'func, A, M, F, operation::DoublePrecDivOp>),
- DivsDp(Operation<'func, A, M, F, operation::DoublePrecDivOp>),
-
- Modu(Operation<'func, A, M, F, operation::BinaryOp>),
- Mods(Operation<'func, A, M, F, operation::BinaryOp>),
-
- ModuDp(Operation<'func, A, M, F, operation::DoublePrecDivOp>),
- ModsDp(Operation<'func, A, M, F, operation::DoublePrecDivOp>),
-
- Neg(Operation<'func, A, M, F, operation::UnaryOp>),
- Not(Operation<'func, A, M, F, operation::UnaryOp>),
- Sx(Operation<'func, A, M, F, operation::UnaryOp>),
- Zx(Operation<'func, A, M, F, operation::UnaryOp>),
- LowPart(Operation<'func, A, M, F, operation::UnaryOp>),
-
- FlagCond(Operation<'func, A, M, F, operation::FlagCond>),
- FlagGroup(Operation<'func, A, M, F, operation::FlagGroup>),
-
- CmpE(Operation<'func, A, M, F, operation::Condition>),
- CmpNe(Operation<'func, A, M, F, operation::Condition>),
- CmpSlt(Operation<'func, A, M, F, operation::Condition>),
- CmpUlt(Operation<'func, A, M, F, operation::Condition>),
- CmpSle(Operation<'func, A, M, F, operation::Condition>),
- CmpUle(Operation<'func, A, M, F, operation::Condition>),
- CmpSge(Operation<'func, A, M, F, operation::Condition>),
- CmpUge(Operation<'func, A, M, F, operation::Condition>),
- CmpSgt(Operation<'func, A, M, F, operation::Condition>),
- CmpUgt(Operation<'func, A, M, F, operation::Condition>),
-
- //TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO
- BoolToInt(Operation<'func, A, M, F, operation::UnaryOp>),
-
- Fadd(Operation<'func, A, M, F, operation::BinaryOp>),
- Fsub(Operation<'func, A, M, F, operation::BinaryOp>),
- Fmul(Operation<'func, A, M, F, operation::BinaryOp>),
- Fdiv(Operation<'func, A, M, F, operation::BinaryOp>),
- Fsqrt(Operation<'func, A, M, F, operation::UnaryOp>),
- Fneg(Operation<'func, A, M, F, operation::UnaryOp>),
- Fabs(Operation<'func, A, M, F, operation::UnaryOp>),
- FloatToInt(Operation<'func, A, M, F, operation::UnaryOp>),
- IntToFloat(Operation<'func, A, M, F, operation::UnaryOp>),
- FloatConv(Operation<'func, A, M, F, operation::UnaryOp>),
- RoundToInt(Operation<'func, A, M, F, operation::UnaryOp>),
- Floor(Operation<'func, A, M, F, operation::UnaryOp>),
- Ceil(Operation<'func, A, M, F, operation::UnaryOp>),
- Ftrunc(Operation<'func, A, M, F, operation::UnaryOp>),
-
- FcmpE(Operation<'func, A, M, F, operation::Condition>),
- FcmpNE(Operation<'func, A, M, F, operation::Condition>),
- FcmpLT(Operation<'func, A, M, F, operation::Condition>),
- FcmpLE(Operation<'func, A, M, F, operation::Condition>),
- FcmpGE(Operation<'func, A, M, F, operation::Condition>),
- FcmpGT(Operation<'func, A, M, F, operation::Condition>),
- FcmpO(Operation<'func, A, M, F, operation::Condition>),
- FcmpUO(Operation<'func, A, M, F, operation::Condition>),
-
- // TODO ADD_OVERFLOW
- Unimpl(Operation<'func, A, M, F, operation::NoArgs>),
- UnimplMem(Operation<'func, A, M, F, operation::UnimplMem>),
-
- Undef(Operation<'func, A, M, F, operation::NoArgs>),
-}
-
-impl<'func, A, M, F> ExprInfo<'func, A, M, F>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- F: FunctionForm,
-{
- /// Returns the size of the result of this expression
- ///
- /// If the expression is malformed or is `Unimpl` there
- /// is no meaningful size associated with the result.
- pub fn size(&self) -> Option<usize> {
- use self::ExprInfo::*;
-
- match *self {
- Undef(..) | Unimpl(..) => None,
-
- FlagCond(..) | FlagGroup(..) | CmpE(..) | CmpNe(..) | CmpSlt(..) | CmpUlt(..)
- | CmpSle(..) | CmpUle(..) | CmpSge(..) | CmpUge(..) | CmpSgt(..) | CmpUgt(..) => {
- Some(0)
- }
-
- _ => Some(self.raw_struct().size),
- //TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO
- }
- }
-
- pub fn address(&self) -> u64 {
- self.raw_struct().address
- }
-
- /// Determines if the expressions represent the same operation
- ///
- /// It does not examine the operands for equality.
- pub fn is_same_op_as(&self, other: &Self) -> bool {
- use self::ExprInfo::*;
-
- match (self, other) {
- (&Reg(..), &Reg(..)) => true,
- _ => self.raw_struct().operation == other.raw_struct().operation,
- }
- }
-
- pub fn as_cmp_op(&self) -> Option<&Operation<'func, A, M, F, operation::Condition>> {
- use self::ExprInfo::*;
-
- match *self {
- CmpE(ref op) | CmpNe(ref op) | CmpSlt(ref op) | CmpUlt(ref op) | CmpSle(ref op)
- | CmpUle(ref op) | CmpSge(ref op) | CmpUge(ref op) | CmpSgt(ref op)
- | CmpUgt(ref op) | FcmpE(ref op) | FcmpNE(ref op) | FcmpLT(ref op) | FcmpLE(ref op)
- | FcmpGE(ref op) | FcmpGT(ref op) | FcmpO(ref op) | FcmpUO(ref op) => Some(op),
- _ => None,
- }
- }
-
- pub fn as_binary_op(&self) -> Option<&Operation<'func, A, M, F, operation::BinaryOp>> {
- use self::ExprInfo::*;
-
- match *self {
- Add(ref op) | Sub(ref op) | And(ref op) | Or(ref op) | Xor(ref op) | Lsl(ref op)
- | Lsr(ref op) | Asr(ref op) | Rol(ref op) | Ror(ref op) | Mul(ref op)
- | MulsDp(ref op) | MuluDp(ref op) | Divu(ref op) | Divs(ref op) | Modu(ref op)
- | Mods(ref op) | Fadd(ref op) | Fsub(ref op) | Fmul(ref op) | Fdiv(ref op) => Some(op),
- _ => None,
- }
- }
-
- pub fn as_binary_op_carry(
- &self,
- ) -> Option<&Operation<'func, A, M, F, operation::BinaryOpCarry>> {
- use self::ExprInfo::*;
-
- match *self {
- Adc(ref op) | Sbb(ref op) | Rlc(ref op) | Rrc(ref op) => Some(op),
- _ => None,
- }
- }
-
- pub fn as_double_prec_div_op(
- &self,
- ) -> Option<&Operation<'func, A, M, F, operation::DoublePrecDivOp>> {
- use self::ExprInfo::*;
-
- match *self {
- DivuDp(ref op) | DivsDp(ref op) | ModuDp(ref op) | ModsDp(ref op) => Some(op),
- _ => None,
- }
- }
-
- pub fn as_unary_op(&self) -> Option<&Operation<'func, A, M, F, operation::UnaryOp>> {
- use self::ExprInfo::*;
-
- match *self {
- Neg(ref op) | Not(ref op) | Sx(ref op) | Zx(ref op) | LowPart(ref op)
- | BoolToInt(ref op) | Fsqrt(ref op) | Fneg(ref op) | Fabs(ref op)
- | FloatToInt(ref op) | IntToFloat(ref op) | FloatConv(ref op) | RoundToInt(ref op)
- | Floor(ref op) | Ceil(ref op) | Ftrunc(ref op) => Some(op),
- _ => None,
- }
- }
-
- pub(crate) fn raw_struct(&self) -> &BNLowLevelILInstruction {
- use self::ExprInfo::*;
-
- match *self {
- Undef(ref op) => &op.op,
-
- Unimpl(ref op) => &op.op,
-
- FlagCond(ref op) => &op.op,
- FlagGroup(ref op) => &op.op,
-
- CmpE(ref op) | CmpNe(ref op) | CmpSlt(ref op) | CmpUlt(ref op) | CmpSle(ref op)
- | CmpUle(ref op) | CmpSge(ref op) | CmpUge(ref op) | CmpSgt(ref op)
- | CmpUgt(ref op) | FcmpE(ref op) | FcmpNE(ref op) | FcmpLT(ref op) | FcmpLE(ref op)
- | FcmpGE(ref op) | FcmpGT(ref op) | FcmpO(ref op) | FcmpUO(ref op) => &op.op,
-
- Load(ref op) => &op.op,
-
- Pop(ref op) => &op.op,
-
- Reg(ref op) => &op.op,
-
- RegSplit(ref op) => &op.op,
-
- Flag(ref op) => &op.op,
-
- FlagBit(ref op) => &op.op,
-
- Const(ref op) | ConstPtr(ref op) => &op.op,
-
- ExternPtr(ref op) => &op.op,
-
- Adc(ref op) | Sbb(ref op) | Rlc(ref op) | Rrc(ref op) => &op.op,
-
- Add(ref op) | Sub(ref op) | And(ref op) | Or(ref op) | Xor(ref op) | Lsl(ref op)
- | Lsr(ref op) | Asr(ref op) | Rol(ref op) | Ror(ref op) | Mul(ref op)
- | MulsDp(ref op) | MuluDp(ref op) | Divu(ref op) | Divs(ref op) | Modu(ref op)
- | Mods(ref op) | Fadd(ref op) | Fsub(ref op) | Fmul(ref op) | Fdiv(ref op) => &op.op,
-
- DivuDp(ref op) | DivsDp(ref op) | ModuDp(ref op) | ModsDp(ref op) => &op.op,
-
- Neg(ref op) | Not(ref op) | Sx(ref op) | Zx(ref op) | LowPart(ref op)
- | BoolToInt(ref op) | Fsqrt(ref op) | Fneg(ref op) | Fabs(ref op)
- | FloatToInt(ref op) | IntToFloat(ref op) | FloatConv(ref op) | RoundToInt(ref op)
- | Floor(ref op) | Ceil(ref op) | Ftrunc(ref op) => &op.op,
-
- UnimplMem(ref op) => &op.op,
- //TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO
- }
- }
-}
-
-impl<'func, A> ExprInfo<'func, A, Mutable, NonSSA<LiftedNonSSA>>
-where
- A: 'func + Architecture,
-{
- pub fn flag_write(&self) -> Option<A::FlagWrite> {
- use self::ExprInfo::*;
-
- match *self {
- Undef(ref _op) => None,
-
- Unimpl(ref _op) => None,
-
- FlagCond(ref _op) => None,
- FlagGroup(ref _op) => None,
-
- CmpE(ref _op) | CmpNe(ref _op) | CmpSlt(ref _op) | CmpUlt(ref _op)
- | CmpSle(ref _op) | CmpUle(ref _op) | CmpSge(ref _op) | CmpUge(ref _op)
- | CmpSgt(ref _op) | CmpUgt(ref _op) | FcmpE(ref _op) | FcmpNE(ref _op)
- | FcmpLT(ref _op) | FcmpLE(ref _op) | FcmpGE(ref _op) | FcmpGT(ref _op)
- | FcmpO(ref _op) | FcmpUO(ref _op) => None,
-
- Load(ref op) => op.flag_write(),
-
- Pop(ref op) => op.flag_write(),
-
- Reg(ref op) => op.flag_write(),
-
- RegSplit(ref op) => op.flag_write(),
-
- Flag(ref op) => op.flag_write(),
-
- FlagBit(ref op) => op.flag_write(),
-
- Const(ref op) | ConstPtr(ref op) => op.flag_write(),
-
- ExternPtr(ref op) => op.flag_write(),
-
- Adc(ref op) | Sbb(ref op) | Rlc(ref op) | Rrc(ref op) => op.flag_write(),
-
- Add(ref op) | Sub(ref op) | And(ref op) | Or(ref op) | Xor(ref op) | Lsl(ref op)
- | Lsr(ref op) | Asr(ref op) | Rol(ref op) | Ror(ref op) | Mul(ref op)
- | MulsDp(ref op) | MuluDp(ref op) | Divu(ref op) | Divs(ref op) | Modu(ref op)
- | Mods(ref op) | Fadd(ref op) | Fsub(ref op) | Fmul(ref op) | Fdiv(ref op) => {
- op.flag_write()
- }
-
- DivuDp(ref op) | DivsDp(ref op) | ModuDp(ref op) | ModsDp(ref op) => op.flag_write(),
-
- Neg(ref op) | Not(ref op) | Sx(ref op) | Zx(ref op) | LowPart(ref op)
- | BoolToInt(ref op) | Fsqrt(ref op) | Fneg(ref op) | Fabs(ref op)
- | FloatToInt(ref op) | IntToFloat(ref op) | FloatConv(ref op) | RoundToInt(ref op)
- | Floor(ref op) | Ceil(ref op) | Ftrunc(ref op) => op.flag_write(),
-
- UnimplMem(ref op) => op.flag_write(),
- //TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO
- }
- }
-}
-
-impl<'func, A, M, V> fmt::Debug for ExprInfo<'func, A, M, NonSSA<V>>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- V: NonSSAVariant,
-{
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- use self::ExprInfo::*;
-
- match *self {
- Undef(..) => f.write_str("undefined"),
-
- Unimpl(..) => f.write_str("unimplemented"),
-
- FlagCond(..) => f.write_str("some_flag_cond"),
- FlagGroup(..) => f.write_str("some_flag_group"),
-
- CmpE(ref op) | CmpNe(ref op) | CmpSlt(ref op) | CmpUlt(ref op) | CmpSle(ref op)
- | CmpUle(ref op) | CmpSge(ref op) | CmpUge(ref op) | CmpSgt(ref op)
- | CmpUgt(ref op) | FcmpE(ref op) | FcmpNE(ref op) | FcmpLT(ref op) | FcmpLE(ref op)
- | FcmpGE(ref op) | FcmpGT(ref op) | FcmpO(ref op) | FcmpUO(ref op) => {
- let left = op.left();
- let right = op.right();
-
- write!(
- f,
- "{:?}({}, {:?}, {:?})",
- op.op.operation,
- op.size(),
- left,
- right
- )
- }
-
- Load(ref op) => {
- let source = op.source_mem_expr();
- let size = op.size();
-
- write!(f, "[{:?}].{}", source, size)
- }
-
- Pop(ref op) => write!(f, "pop.{}", op.size()),
-
- Reg(ref op) => {
- let reg = op.source_reg();
- let size = op.size();
-
- let size = match reg {
- Register::Temp(_) => Some(size),
- Register::ArchReg(ref r) if r.info().size() != size => Some(size),
- _ => None,
- };
-
- match size {
- Some(s) => write!(f, "{:?}.{}", reg, s),
- _ => write!(f, "{:?}", reg),
- }
- }
-
- 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
-
- Const(ref op) | ConstPtr(ref op) => write!(f, "0x{:x}", op.value()),
-
- ExternPtr(ref op) => write!(f, "0x{:x}", op.value()),
-
- Adc(ref op) | Sbb(ref op) | Rlc(ref op) | Rrc(ref op) => {
- let left = op.left();
- let right = op.right();
- let carry = op.carry();
-
- write!(
- f,
- "{:?}({}, {:?}, {:?}, carry: {:?})",
- op.op.operation,
- op.size(),
- left,
- right,
- carry
- )
- }
-
- Add(ref op) | Sub(ref op) | And(ref op) | Or(ref op) | Xor(ref op) | Lsl(ref op)
- | Lsr(ref op) | Asr(ref op) | Rol(ref op) | Ror(ref op) | Mul(ref op)
- | MulsDp(ref op) | MuluDp(ref op) | Divu(ref op) | Divs(ref op) | Modu(ref op)
- | Mods(ref op) | Fadd(ref op) | Fsub(ref op) | Fmul(ref op) | Fdiv(ref op) => {
- let left = op.left();
- let right = op.right();
-
- write!(
- f,
- "{:?}({}, {:?}, {:?})",
- op.op.operation,
- op.size(),
- left,
- right
- )
- }
-
- DivuDp(ref op) | DivsDp(ref op) | ModuDp(ref op) | ModsDp(ref op) => {
- let high = op.high();
- let low = op.low();
- let right = op.right();
-
- write!(
- f,
- "{:?}({}, {:?}:{:?},{:?})",
- op.op.operation,
- op.size(),
- high,
- low,
- right
- )
- }
-
- Neg(ref op) | Not(ref op) | Sx(ref op) | Zx(ref op) | LowPart(ref op)
- | BoolToInt(ref op) | Fsqrt(ref op) | Fneg(ref op) | Fabs(ref op)
- | FloatToInt(ref op) | IntToFloat(ref op) | FloatConv(ref op) | RoundToInt(ref op)
- | Floor(ref op) | Ceil(ref op) | Ftrunc(ref op) => write!(
- f,
- "{:?}({}, {:?})",
- op.op.operation,
- op.size(),
- op.operand()
- ),
-
- UnimplMem(ref op) => write!(f, "unimplemented_mem({:?})", op.mem_expr()),
- //TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO
- }
- }
-}
diff --git a/rust/src/llil/instruction.rs b/rust/src/llil/instruction.rs
deleted file mode 100644
index 62e50453..00000000
--- a/rust/src/llil/instruction.rs
+++ /dev/null
@@ -1,209 +0,0 @@
-// Copyright 2021-2024 Vector 35 Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use binaryninjacore_sys::BNGetLowLevelILByIndex;
-use binaryninjacore_sys::BNGetLowLevelILIndexForInstruction;
-use binaryninjacore_sys::BNLowLevelILInstruction;
-
-use super::operation;
-use super::operation::Operation;
-use super::*;
-
-use crate::architecture::Architecture;
-
-pub struct Instruction<'func, A, M, F>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- F: FunctionForm,
-{
- pub(crate) function: &'func Function<A, M, F>,
- pub(crate) instr_idx: usize,
-}
-
-fn common_info<'func, A, M, F>(
- function: &'func Function<A, M, F>,
- op: BNLowLevelILInstruction,
-) -> Option<InstrInfo<'func, A, M, F>>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- F: FunctionForm,
-{
- use binaryninjacore_sys::BNLowLevelILOperation::*;
-
- match op.operation {
- LLIL_NOP => InstrInfo::Nop(Operation::new(function, op)).into(),
- LLIL_JUMP => InstrInfo::Jump(Operation::new(function, op)).into(),
- LLIL_JUMP_TO => InstrInfo::JumpTo(Operation::new(function, op)).into(),
- LLIL_RET => InstrInfo::Ret(Operation::new(function, op)).into(),
- LLIL_NORET => InstrInfo::NoRet(Operation::new(function, op)).into(),
- LLIL_IF => InstrInfo::If(Operation::new(function, op)).into(),
- LLIL_GOTO => InstrInfo::Goto(Operation::new(function, op)).into(),
- LLIL_BP => InstrInfo::Bp(Operation::new(function, op)).into(),
- LLIL_TRAP => InstrInfo::Trap(Operation::new(function, op)).into(),
- LLIL_UNDEF => InstrInfo::Undef(Operation::new(function, op)).into(),
- _ => None,
- }
-}
-
-use super::VisitorAction;
-
-macro_rules! visit {
- ($f:expr, $($e:expr),*) => {
- if let VisitorAction::Halt = $f($($e,)*) {
- return VisitorAction::Halt;
- }
- }
-}
-
-fn common_visit<'func, A, M, F, CB>(info: &InstrInfo<'func, A, M, F>, f: &mut CB) -> VisitorAction
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- F: FunctionForm,
- CB: FnMut(&Expression<'func, A, M, F, ValueExpr>) -> VisitorAction,
-{
- use self::InstrInfo::*;
-
- match *info {
- Jump(ref op) => visit!(f, &op.target()),
- JumpTo(ref op) => visit!(f, &op.target()),
- Ret(ref op) => visit!(f, &op.target()),
- If(ref op) => visit!(f, &op.condition()),
- Value(ref e, _) => visit!(f, e),
- _ => {}
- };
-
- VisitorAction::Sibling
-}
-
-impl<'func, A, M, V> Instruction<'func, A, M, NonSSA<V>>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- V: NonSSAVariant,
-{
- pub fn address(&self) -> u64 {
- let expr_idx =
- unsafe { BNGetLowLevelILIndexForInstruction(self.function.handle, self.instr_idx) };
- let op = unsafe { BNGetLowLevelILByIndex(self.function.handle, expr_idx) };
- op.address
- }
-
- pub fn info(&self) -> InstrInfo<'func, A, M, NonSSA<V>> {
- use binaryninjacore_sys::BNLowLevelILOperation::*;
-
- let expr_idx =
- unsafe { BNGetLowLevelILIndexForInstruction(self.function.handle, self.instr_idx) };
- let op = unsafe { BNGetLowLevelILByIndex(self.function.handle, expr_idx) };
-
- match op.operation {
- LLIL_SET_REG => InstrInfo::SetReg(Operation::new(self.function, op)),
- LLIL_SET_REG_SPLIT => InstrInfo::SetRegSplit(Operation::new(self.function, op)),
- LLIL_SET_FLAG => InstrInfo::SetFlag(Operation::new(self.function, op)),
- LLIL_STORE => InstrInfo::Store(Operation::new(self.function, op)),
- LLIL_PUSH => InstrInfo::Push(Operation::new(self.function, op)),
- LLIL_CALL | LLIL_CALL_STACK_ADJUST => {
- InstrInfo::Call(Operation::new(self.function, op))
- }
- LLIL_TAILCALL => InstrInfo::TailCall(Operation::new(self.function, op)),
- LLIL_SYSCALL => InstrInfo::Syscall(Operation::new(self.function, op)),
- LLIL_INTRINSIC => InstrInfo::Intrinsic(Operation::new(self.function, op)),
- _ => {
- common_info(self.function, op).unwrap_or_else(|| {
- // Hopefully this is a bare value. If it isn't (expression
- // from wrong function form or similar) it won't really cause
- // any problems as it'll come back as undefined when queried.
- let expr = Expression::new(self.function, expr_idx);
-
- let info = unsafe { expr.info_from_op(op) };
-
- InstrInfo::Value(expr, info)
- })
- }
- }
- }
-
- pub fn visit_tree<F>(&self, f: &mut F) -> VisitorAction
- where
- F: FnMut(
- &Expression<'func, A, M, NonSSA<V>, ValueExpr>,
- &ExprInfo<'func, A, M, NonSSA<V>>,
- ) -> VisitorAction,
- {
- use self::InstrInfo::*;
- let info = self.info();
-
- let fb = &mut |e: &Expression<'func, A, M, NonSSA<V>, ValueExpr>| e.visit_tree(f);
-
- match info {
- SetReg(ref op) => visit!(fb, &op.source_expr()),
- SetRegSplit(ref op) => visit!(fb, &op.source_expr()),
- SetFlag(ref op) => visit!(fb, &op.source_expr()),
- Store(ref op) => {
- visit!(fb, &op.dest_mem_expr());
- visit!(fb, &op.source_expr());
- }
- Push(ref op) => visit!(fb, &op.operand()),
- Call(ref op) | TailCall(ref op) => visit!(fb, &op.target()),
- Intrinsic(ref _op) => {
- // TODO: Use this when we support expression lists
- // for expr in op.source_exprs() {
- // visit!(fb, expr);
- // }
- }
- _ => visit!(common_visit, &info, fb),
- }
-
- VisitorAction::Sibling
- }
-}
-
-pub enum InstrInfo<'func, A, M, F>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- F: FunctionForm,
-{
- Nop(Operation<'func, A, M, F, operation::NoArgs>),
- SetReg(Operation<'func, A, M, F, operation::SetReg>),
- SetRegSplit(Operation<'func, A, M, F, operation::SetRegSplit>),
- SetFlag(Operation<'func, A, M, F, operation::SetFlag>),
- Store(Operation<'func, A, M, F, operation::Store>),
- Push(Operation<'func, A, M, F, operation::UnaryOp>), // TODO needs a real op
-
- Jump(Operation<'func, A, M, F, operation::Jump>),
- JumpTo(Operation<'func, A, M, F, operation::JumpTo>),
-
- Call(Operation<'func, A, M, F, operation::Call>),
- TailCall(Operation<'func, A, M, F, operation::Call>),
-
- Ret(Operation<'func, A, M, F, operation::Ret>),
- NoRet(Operation<'func, A, M, F, operation::NoArgs>),
-
- If(Operation<'func, A, M, F, operation::If>),
- Goto(Operation<'func, A, M, F, operation::Goto>),
-
- Syscall(Operation<'func, A, M, F, operation::Syscall>),
- Intrinsic(Operation<'func, A, M, F, operation::Intrinsic>),
- Bp(Operation<'func, A, M, F, operation::NoArgs>),
- Trap(Operation<'func, A, M, F, operation::Trap>),
- Undef(Operation<'func, A, M, F, operation::NoArgs>),
-
- Value(
- Expression<'func, A, M, F, ValueExpr>,
- ExprInfo<'func, A, M, F>,
- ),
-}
diff --git a/rust/src/llil/mod.rs b/rust/src/llil/mod.rs
deleted file mode 100644
index b9424e86..00000000
--- a/rust/src/llil/mod.rs
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright 2021-2024 Vector 35 Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use std::fmt;
-
-// TODO : provide some way to forbid emitting register reads for certain registers
-// also writing for certain registers (e.g. zero register must prohibit il.set_reg and il.reg
-// (replace with nop or const(0) respectively)
-// requirements on load/store memory address sizes?
-// can reg/set_reg be used with sizes that differ from what is in BNRegisterInfo?
-
-use crate::architecture::Architecture;
-use crate::architecture::Register as ArchReg;
-use crate::function::Location;
-
-mod block;
-mod expression;
-mod function;
-mod instruction;
-mod lifting;
-pub mod operation;
-
-pub use self::expression::*;
-pub use self::function::*;
-pub use self::instruction::*;
-pub use self::lifting::get_default_flag_cond_llil;
-pub use self::lifting::get_default_flag_write_llil;
-pub use self::lifting::{
- ExpressionBuilder, FlagWriteOp, Label, Liftable, LiftableWithSize, RegisterOrConstant,
-};
-
-pub use self::block::Block as LowLevelBlock;
-pub use self::block::BlockIter as LowLevelBlockIter;
-
-pub type Lifter<Arch> = Function<Arch, Mutable, NonSSA<LiftedNonSSA>>;
-pub type LiftedFunction<Arch> = Function<Arch, Finalized, NonSSA<LiftedNonSSA>>;
-pub type LiftedExpr<'a, Arch> = Expression<'a, Arch, Mutable, NonSSA<LiftedNonSSA>, ValueExpr>;
-pub type RegularFunction<Arch> = Function<Arch, Finalized, NonSSA<RegularNonSSA>>;
-pub type SSAFunction<Arch> = Function<Arch, Finalized, SSA>;
-
-#[derive(Copy, Clone, PartialEq, Eq)]
-pub enum Register<R: ArchReg> {
- ArchReg(R),
- Temp(u32),
-}
-
-impl<R: ArchReg> Register<R> {
- fn id(&self) -> u32 {
- match *self {
- Register::ArchReg(ref r) => r.id(),
- Register::Temp(id) => 0x8000_0000 | id,
- }
- }
-}
-
-impl<R: ArchReg> fmt::Debug for Register<R> {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match *self {
- Register::ArchReg(ref r) => write!(f, "{}", r.name().as_ref()),
- Register::Temp(id) => write!(f, "temp{}", id),
- }
- }
-}
-
-#[derive(Copy, Clone, Debug)]
-pub enum SSARegister<R: ArchReg> {
- Full(Register<R>, u32), // no such thing as partial access to a temp register, I think
- Partial(R, u32, R), // partial accesses only possible for arch registers, I think
-}
-
-impl<R: ArchReg> SSARegister<R> {
- pub fn version(&self) -> u32 {
- match *self {
- SSARegister::Full(_, ver) | SSARegister::Partial(_, ver, _) => ver,
- }
- }
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
-pub enum VisitorAction {
- Descend,
- Sibling,
- Halt,
-}
diff --git a/rust/src/llil/operation.rs b/rust/src/llil/operation.rs
deleted file mode 100644
index b65dfa8c..00000000
--- a/rust/src/llil/operation.rs
+++ /dev/null
@@ -1,825 +0,0 @@
-// Copyright 2021-2024 Vector 35 Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-use binaryninjacore_sys::{BNGetLowLevelILByIndex, BNLowLevelILInstruction};
-
-use std::collections::BTreeMap;
-use std::marker::PhantomData;
-use std::mem;
-
-use super::*;
-
-pub struct Operation<'func, A, M, F, O>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- F: FunctionForm,
- O: OperationArguments,
-{
- pub(crate) function: &'func Function<A, M, F>,
- pub(crate) op: BNLowLevelILInstruction,
- _args: PhantomData<O>,
-}
-
-impl<'func, A, M, F, O> Operation<'func, A, M, F, O>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- F: FunctionForm,
- O: OperationArguments,
-{
- pub(crate) fn new(function: &'func Function<A, M, F>, op: BNLowLevelILInstruction) -> Self {
- Self {
- function,
- op,
- _args: PhantomData,
- }
- }
-
- pub fn address(&self) -> u64 {
- self.op.address
- }
-}
-
-impl<'func, A, M, O> Operation<'func, A, M, NonSSA<LiftedNonSSA>, O>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- O: OperationArguments,
-{
- pub fn flag_write(&self) -> Option<A::FlagWrite> {
- match self.op.flags {
- 0 => None,
- id => self.function.arch().flag_write_from_id(id),
- }
- }
-}
-
-// LLIL_NOP, LLIL_NORET, LLIL_BP, LLIL_UNDEF, LLIL_UNIMPL
-pub struct NoArgs;
-
-// LLIL_POP
-pub struct Pop;
-
-impl<'func, A, M, F> Operation<'func, A, M, F, Pop>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- F: FunctionForm,
-{
- pub fn size(&self) -> usize {
- self.op.size
- }
-}
-
-// LLIL_SYSCALL, LLIL_SYSCALL_SSA
-pub struct Syscall;
-
-// LLIL_INTRINSIC, LLIL_INTRINSIC_SSA
-pub struct Intrinsic;
-
-impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, Intrinsic>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- V: NonSSAVariant,
-{
- // TODO: Support register and expression lists
- pub fn intrinsic(&self) -> Option<A::Intrinsic> {
- let raw_id = self.op.operands[2] as u32;
- self.function.arch().intrinsic_from_id(raw_id)
- }
-}
-
-// LLIL_SET_REG, LLIL_SET_REG_SSA, LLIL_SET_REG_PARTIAL_SSA
-pub struct SetReg;
-
-impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, SetReg>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- V: NonSSAVariant,
-{
- pub fn size(&self) -> usize {
- self.op.size
- }
-
- pub fn dest_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_SET_REG @ 0x{:x}",
- self.op.address
- );
-
- Register::Temp(0)
- })
- }
- }
-
- pub fn source_expr(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> {
- Expression::new(self.function, self.op.operands[1] as usize)
- }
-}
-
-// LLIL_SET_REG_SPLIT, LLIL_SET_REG_SPLIT_SSA
-pub struct SetRegSplit;
-
-impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, SetRegSplit>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- V: NonSSAVariant,
-{
- pub fn size(&self) -> usize {
- self.op.size
- }
-
- pub fn dest_reg_high(&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_SET_REG_SPLIT @ 0x{:x}",
- self.op.address
- );
-
- Register::Temp(0)
- })
- }
- }
-
- pub fn dest_reg_low(&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_SET_REG_SPLIT @ 0x{:x}",
- self.op.address
- );
-
- Register::Temp(0)
- })
- }
- }
-
- pub fn source_expr(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> {
- Expression::new(self.function, self.op.operands[2] as usize)
- }
-}
-
-// LLIL_SET_FLAG, LLIL_SET_FLAG_SSA
-pub struct SetFlag;
-
-impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, SetFlag>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- V: NonSSAVariant,
-{
- pub fn source_expr(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> {
- Expression::new(self.function, self.op.operands[1] as usize)
- }
-}
-
-// LLIL_LOAD, LLIL_LOAD_SSA
-pub struct Load;
-
-impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, Load>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- V: NonSSAVariant,
-{
- pub fn size(&self) -> usize {
- self.op.size
- }
-
- pub fn source_mem_expr(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> {
- Expression::new(self.function, self.op.operands[0] as usize)
- }
-}
-
-// LLIL_STORE, LLIL_STORE_SSA
-pub struct Store;
-
-impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, Store>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- V: NonSSAVariant,
-{
- pub fn size(&self) -> usize {
- self.op.size
- }
-
- pub fn dest_mem_expr(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> {
- Expression::new(self.function, self.op.operands[0] as usize)
- }
-
- pub fn source_expr(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> {
- Expression::new(self.function, self.op.operands[1] as usize)
- }
-}
-
-// LLIL_REG, LLIL_REG_SSA, LLIL_REG_SSA_PARTIAL
-pub struct Reg;
-
-impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, Reg>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- V: NonSSAVariant,
-{
- pub fn size(&self) -> usize {
- self.op.size
- }
-
- pub fn source_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)
- })
- }
- }
-}
-
-// 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;
-
-// LLIL_FLAG_BIT, LLIL_FLAG_BIT_SSA
-pub struct FlagBit;
-
-// LLIL_JUMP
-pub struct Jump;
-
-impl<'func, A, M, F> Operation<'func, A, M, F, Jump>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- F: FunctionForm,
-{
- pub fn target(&self) -> Expression<'func, A, M, F, ValueExpr> {
- Expression::new(self.function, self.op.operands[0] as usize)
- }
-}
-
-// 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,
- M: FunctionMutability,
- F: FunctionForm,
-{
- pub fn target(&self) -> Expression<'func, A, M, F, ValueExpr> {
- Expression::new(self.function, self.op.operands[0] as usize)
- }
-
- 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
-pub struct Call;
-
-impl<'func, A, M, V> Operation<'func, A, M, NonSSA<V>, Call>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- V: NonSSAVariant,
-{
- pub fn target(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> {
- Expression::new(self.function, self.op.operands[0] as usize)
- }
-
- pub fn stack_adjust(&self) -> Option<u64> {
- use binaryninjacore_sys::BNLowLevelILOperation::LLIL_CALL_STACK_ADJUST;
-
- if self.op.operation == LLIL_CALL_STACK_ADJUST {
- Some(self.op.operands[1])
- } else {
- None
- }
- }
-}
-
-// LLIL_RET
-pub struct Ret;
-
-impl<'func, A, M, F> Operation<'func, A, M, F, Ret>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- F: FunctionForm,
-{
- pub fn target(&self) -> Expression<'func, A, M, F, ValueExpr> {
- Expression::new(self.function, self.op.operands[0] as usize)
- }
-}
-
-// LLIL_IF
-pub struct If;
-
-impl<'func, A, M, F> Operation<'func, A, M, F, If>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- F: FunctionForm,
-{
- pub fn condition(&self) -> Expression<'func, A, M, F, ValueExpr> {
- Expression::new(self.function, self.op.operands[0] as usize)
- }
-
- pub fn true_target(&self) -> Instruction<'func, A, M, F> {
- Instruction {
- function: self.function,
- instr_idx: self.op.operands[1] as usize,
- }
- }
-
- 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
-pub struct Goto;
-
-impl<'func, A, M, F> Operation<'func, A, M, F, Goto>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- F: FunctionForm,
-{
- pub fn target(&self) -> Instruction<'func, A, M, F> {
- Instruction {
- function: self.function,
- instr_idx: self.op.operands[0] as usize,
- }
- }
-
- pub fn target_idx(&self) -> usize {
- self.op.operands[0] as usize
- }
-}
-
-// LLIL_FLAG_COND
-pub struct FlagCond;
-
-// LLIL_FLAG_GROUP
-pub struct FlagGroup;
-
-impl<'func, A, M> Operation<'func, A, M, NonSSA<LiftedNonSSA>, FlagGroup>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
-{
- pub fn flag_group(&self) -> A::FlagGroup {
- let id = self.op.operands[0] as u32;
- self.function.arch().flag_group_from_id(id).unwrap()
- }
-}
-
-// LLIL_TRAP
-pub struct Trap;
-
-impl<'func, A, M, F> Operation<'func, A, M, F, Trap>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- F: FunctionForm,
-{
- pub fn vector(&self) -> u64 {
- self.op.operands[0]
- }
-}
-
-// LLIL_REG_PHI
-pub struct RegPhi;
-
-// LLIL_FLAG_PHI
-pub struct FlagPhi;
-
-// LLIL_MEM_PHI
-pub struct MemPhi;
-
-// LLIL_CONST, LLIL_CONST_PTR
-pub struct Const;
-
-impl<'func, A, M, F> Operation<'func, A, M, F, Const>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- F: FunctionForm,
-{
- pub fn size(&self) -> usize {
- self.op.size
- }
-
- pub fn value(&self) -> u64 {
- #[cfg(debug_assertions)]
- {
- let raw = self.op.operands[0] as i64;
-
- let is_safe = match raw.overflowing_shr(self.op.size as u32 * 8) {
- (_, true) => true,
- (res, false) => [-1, 0].contains(&res),
- };
-
- if !is_safe {
- error!(
- "il expr @ {:x} contains constant 0x{:x} as {} byte value (doesn't fit!)",
- self.op.address, self.op.operands[0], self.op.size
- );
- }
- }
-
- let mut mask = -1i64 as u64;
-
- if self.op.size < mem::size_of::<u64>() {
- mask <<= self.op.size * 8;
- mask = !mask;
- }
-
- self.op.operands[0] & mask
- }
-}
-
-// LLIL_EXTERN_PTR
-pub struct Extern;
-
-impl<'func, A, M, F> Operation<'func, A, M, F, Extern>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- F: FunctionForm,
-{
- pub fn size(&self) -> usize {
- self.op.size
- }
-
- pub fn value(&self) -> u64 {
- #[cfg(debug_assertions)]
- {
- let raw = self.op.operands[0] as i64;
-
- let is_safe = match raw.overflowing_shr(self.op.size as u32 * 8) {
- (_, true) => true,
- (res, false) => [-1, 0].contains(&res),
- };
-
- if !is_safe {
- error!(
- "il expr @ {:x} contains extern 0x{:x} as {} byte value (doesn't fit!)",
- self.op.address, self.op.operands[0], self.op.size
- );
- }
- }
-
- let mut mask = -1i64 as u64;
-
- if self.op.size < mem::size_of::<u64>() {
- mask <<= self.op.size * 8;
- mask = !mask;
- }
-
- self.op.operands[0] & mask
- }
-}
-
-// LLIL_ADD, LLIL_SUB, LLIL_AND, LLIL_OR
-// LLIL_XOR, LLIL_LSL, LLIL_LSR, LLIL_ASR
-// LLIL_ROL, LLIL_ROR, LLIL_MUL, LLIL_MULU_DP,
-// LLIL_MULS_DP, LLIL_DIVU, LLIL_DIVS, LLIL_MODU,
-// LLIL_MODS
-pub struct BinaryOp;
-
-impl<'func, A, M, F> Operation<'func, A, M, F, BinaryOp>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- F: FunctionForm,
-{
- pub fn size(&self) -> usize {
- self.op.size
- }
-
- pub fn left(&self) -> Expression<'func, A, M, F, ValueExpr> {
- Expression::new(self.function, self.op.operands[0] as usize)
- }
-
- pub fn right(&self) -> Expression<'func, A, M, F, ValueExpr> {
- Expression::new(self.function, self.op.operands[1] as usize)
- }
-}
-
-// LLIL_ADC, LLIL_SBB, LLIL_RLC, LLIL_RRC
-pub struct BinaryOpCarry;
-
-impl<'func, A, M, F> Operation<'func, A, M, F, BinaryOpCarry>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- F: FunctionForm,
-{
- pub fn size(&self) -> usize {
- self.op.size
- }
-
- pub fn left(&self) -> Expression<'func, A, M, F, ValueExpr> {
- Expression::new(self.function, self.op.operands[0] as usize)
- }
-
- pub fn right(&self) -> Expression<'func, A, M, F, ValueExpr> {
- Expression::new(self.function, self.op.operands[1] as usize)
- }
-
- pub fn carry(&self) -> Expression<'func, A, M, F, ValueExpr> {
- Expression::new(self.function, self.op.operands[2] as usize)
- }
-}
-
-// LLIL_DIVS_DP, LLIL_DIVU_DP, LLIL_MODU_DP, LLIL_MODS_DP
-pub struct DoublePrecDivOp;
-
-impl<'func, A, M, F> Operation<'func, A, M, F, DoublePrecDivOp>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- F: FunctionForm,
-{
- pub fn size(&self) -> usize {
- self.op.size
- }
-
- pub fn high(&self) -> Expression<'func, A, M, F, ValueExpr> {
- Expression::new(self.function, self.op.operands[0] as usize)
- }
-
- pub fn low(&self) -> Expression<'func, A, M, F, ValueExpr> {
- Expression::new(self.function, self.op.operands[1] as usize)
- }
-
- pub fn right(&self) -> Expression<'func, A, M, F, ValueExpr> {
- Expression::new(self.function, self.op.operands[2] as usize)
- }
-}
-
-// LLIL_PUSH, LLIL_NEG, LLIL_NOT, LLIL_SX,
-// LLIL_ZX, LLIL_LOW_PART, LLIL_BOOL_TO_INT, LLIL_UNIMPL_MEM
-pub struct UnaryOp;
-
-impl<'func, A, M, F> Operation<'func, A, M, F, UnaryOp>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- F: FunctionForm,
-{
- pub fn size(&self) -> usize {
- self.op.size
- }
-
- pub fn operand(&self) -> Expression<'func, A, M, F, ValueExpr> {
- Expression::new(self.function, self.op.operands[0] as usize)
- }
-}
-
-// LLIL_CMP_X
-pub struct Condition;
-
-impl<'func, A, M, F> Operation<'func, A, M, F, Condition>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- F: FunctionForm,
-{
- pub fn size(&self) -> usize {
- self.op.size
- }
-
- pub fn left(&self) -> Expression<'func, A, M, F, ValueExpr> {
- Expression::new(self.function, self.op.operands[0] as usize)
- }
-
- pub fn right(&self) -> Expression<'func, A, M, F, ValueExpr> {
- Expression::new(self.function, self.op.operands[1] as usize)
- }
-}
-
-// LLIL_UNIMPL_MEM
-pub struct UnimplMem;
-
-impl<'func, A, M, F> Operation<'func, A, M, F, UnimplMem>
-where
- A: 'func + Architecture,
- M: FunctionMutability,
- F: FunctionForm,
-{
- pub fn size(&self) -> usize {
- self.op.size
- }
-
- pub fn mem_expr(&self) -> Expression<'func, A, M, F, ValueExpr> {
- Expression::new(self.function, self.op.operands[0] as usize)
- }
-}
-
-// TODO TEST_BIT
-
-pub trait OperationArguments: 'static {}
-
-impl OperationArguments for NoArgs {}
-impl OperationArguments for Pop {}
-impl OperationArguments for Syscall {}
-impl OperationArguments for Intrinsic {}
-impl OperationArguments for SetReg {}
-impl OperationArguments for SetRegSplit {}
-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 {}
-impl OperationArguments for JumpTo {}
-impl OperationArguments for Call {}
-impl OperationArguments for Ret {}
-impl OperationArguments for If {}
-impl OperationArguments for Goto {}
-impl OperationArguments for FlagCond {}
-impl OperationArguments for FlagGroup {}
-impl OperationArguments for Trap {}
-impl OperationArguments for RegPhi {}
-impl OperationArguments for FlagPhi {}
-impl OperationArguments for MemPhi {}
-impl OperationArguments for Const {}
-impl OperationArguments for Extern {}
-impl OperationArguments for BinaryOp {}
-impl OperationArguments for BinaryOpCarry {}
-impl OperationArguments for DoublePrecDivOp {}
-impl OperationArguments for UnaryOp {}
-impl OperationArguments for Condition {}
-impl OperationArguments for UnimplMem {}
diff --git a/rust/src/logger.rs b/rust/src/logger.rs
index 487761e3..c928287f 100644
--- a/rust/src/logger.rs
+++ b/rust/src/logger.rs
@@ -27,14 +27,14 @@
//! true
//! }
//! ```
-//!
pub use binaryninjacore_sys::BNLogLevel as Level;
use binaryninjacore_sys::{
BNFreeLogger, BNLogCreateLogger, BNLogListener, BNLogger, BNLoggerGetName,
- BNLoggerGetSessionId, BNUpdateLogListeners,
+ BNLoggerGetSessionId, BNNewLoggerReference, BNUpdateLogListeners,
};
+use crate::rc::{Ref, RefCountable};
use crate::string::BnString;
use log;
use log::LevelFilter;
@@ -44,26 +44,41 @@ use std::ptr::NonNull;
const LOGGER_DEFAULT_SESSION_ID: usize = 0;
+#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Logger {
handle: NonNull<BNLogger>,
level: LevelFilter,
}
impl Logger {
- pub fn new(name: &str) -> Logger {
+ pub fn new(name: &str) -> Ref<Logger> {
Self::new_with_session(name, LOGGER_DEFAULT_SESSION_ID)
}
- pub fn new_with_session(name: &str, session_id: usize) -> Logger {
+ pub fn new_with_session(name: &str, session_id: usize) -> Ref<Logger> {
let name_raw = CString::new(name).unwrap();
let handle = unsafe { BNLogCreateLogger(name_raw.as_ptr(), session_id) };
- Logger {
- handle: NonNull::new(handle).unwrap(),
- level: LevelFilter::Debug,
+ unsafe {
+ Ref::new(Logger {
+ handle: NonNull::new(handle).unwrap(),
+ level: LevelFilter::Debug,
+ })
}
}
- pub fn with_level(mut self, level: LevelFilter) -> Logger {
+ pub fn name(&self) -> BnString {
+ unsafe { BnString::from_raw(BNLoggerGetName(self.handle.as_ptr())) }
+ }
+
+ pub fn session_id(&self) -> usize {
+ unsafe { BNLoggerGetSessionId(self.handle.as_ptr()) }
+ }
+}
+
+// NOTE: Due to the ref counted core object, we must impl on the ref counted object.
+// NOTE: If we wanted to be less specific than we would need Ref to impl Copy
+impl Ref<Logger> {
+ pub fn with_level(mut self, level: LevelFilter) -> Ref<Logger> {
self.level = level;
self
}
@@ -76,29 +91,36 @@ impl Logger {
log::set_max_level(self.level);
let _ = log::set_boxed_logger(Box::new(self));
}
+}
- pub fn name(&self) -> BnString {
- unsafe { BnString::from_raw(BNLoggerGetName(self.handle.as_ptr())) }
+impl Default for Ref<Logger> {
+ fn default() -> Self {
+ Logger::new("Default")
}
+}
- pub fn session_id(&self) -> usize {
- unsafe { BNLoggerGetSessionId(self.handle.as_ptr()) }
+impl ToOwned for Logger {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
}
}
-impl Default for Logger {
- fn default() -> Self {
- Logger::new("Default")
+unsafe impl RefCountable for Logger {
+ unsafe fn inc_ref(logger: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: NonNull::new(BNNewLoggerReference(logger.handle.as_ptr())).unwrap(),
+ level: logger.level,
+ })
}
-}
-impl Drop for Logger {
- fn drop(&mut self) {
- unsafe { BNFreeLogger(self.handle.as_ptr()) };
+ unsafe fn dec_ref(logger: &Self) {
+ BNFreeLogger(logger.handle.as_ptr());
}
}
-impl log::Log for Logger {
+impl log::Log for Ref<Logger> {
fn enabled(&self, _metadata: &log::Metadata) -> bool {
true
}
@@ -122,7 +144,7 @@ impl log::Log for Logger {
BNLog(
self.session_id(),
level,
- logger_name.into_raw(),
+ logger_name.as_ptr(),
0,
percent_s.as_ptr(),
msg.as_ptr(),
diff --git a/rust/src/low_level_il.rs b/rust/src/low_level_il.rs
new file mode 100644
index 00000000..5df7252b
--- /dev/null
+++ b/rust/src/low_level_il.rs
@@ -0,0 +1,99 @@
+// Copyright 2021-2024 Vector 35 Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use std::fmt;
+
+// TODO : provide some way to forbid emitting register reads for certain registers
+// also writing for certain registers (e.g. zero register must prohibit il.set_reg and il.reg
+// (replace with nop or const(0) respectively)
+// requirements on load/store memory address sizes?
+// can reg/set_reg be used with sizes that differ from what is in BNRegisterInfo?
+
+use crate::architecture::Register as ArchReg;
+use crate::architecture::{Architecture, RegisterId};
+use crate::function::Location;
+
+mod block;
+pub mod expression;
+pub mod function;
+pub mod instruction;
+pub mod lifting;
+pub mod operation;
+
+use self::expression::*;
+use self::function::*;
+use self::instruction::*;
+
+pub type MutableLiftedILFunction<Arch> = LowLevelILFunction<Arch, Mutable, NonSSA<LiftedNonSSA>>;
+pub type LiftedILFunction<Arch> = LowLevelILFunction<Arch, Finalized, NonSSA<LiftedNonSSA>>;
+pub type MutableLiftedILExpr<'a, Arch, ReturnType> =
+ LowLevelILExpression<'a, Arch, Mutable, NonSSA<LiftedNonSSA>, ReturnType>;
+pub type RegularLowLevelILFunction<Arch> =
+ LowLevelILFunction<Arch, Finalized, NonSSA<RegularNonSSA>>;
+pub type RegularLowLevelILInstruction<'a, Arch> =
+ LowLevelILInstruction<'a, Arch, Finalized, NonSSA<RegularNonSSA>>;
+pub type RegularLowLevelILInstructionKind<'a, Arch> =
+ LowLevelILInstructionKind<'a, Arch, Finalized, NonSSA<RegularNonSSA>>;
+pub type RegularLowLevelILExpression<'a, Arch, ReturnType> =
+ LowLevelILExpression<'a, Arch, Finalized, NonSSA<RegularNonSSA>, ReturnType>;
+pub type RegularLowLevelILExpressionKind<'a, Arch> =
+ LowLevelILExpressionKind<'a, Arch, Finalized, NonSSA<RegularNonSSA>>;
+pub type LowLevelILSSAFunction<Arch> = LowLevelILFunction<Arch, Finalized, SSA>;
+
+#[derive(Copy, Clone, PartialEq, Eq)]
+pub enum LowLevelILRegister<R: ArchReg> {
+ ArchReg(R),
+ // TODO: Might want to be changed to TempRegisterId.
+ // TODO: If we do that then we would need to get rid of `Register::id()`
+ Temp(u32),
+}
+
+impl<R: ArchReg> LowLevelILRegister<R> {
+ fn id(&self) -> RegisterId {
+ match *self {
+ LowLevelILRegister::ArchReg(ref r) => r.id(),
+ LowLevelILRegister::Temp(id) => RegisterId(0x8000_0000 | id),
+ }
+ }
+}
+
+impl<R: ArchReg> fmt::Debug for LowLevelILRegister<R> {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ LowLevelILRegister::ArchReg(ref r) => write!(f, "{}", r.name().as_ref()),
+ LowLevelILRegister::Temp(id) => write!(f, "temp{}", id),
+ }
+ }
+}
+
+#[derive(Copy, Clone, Debug)]
+pub enum LowLevelILSSARegister<R: ArchReg> {
+ Full(LowLevelILRegister<R>, u32), // no such thing as partial access to a temp register, I think
+ Partial(R, u32, R), // partial accesses only possible for arch registers, I think
+}
+
+impl<R: ArchReg> LowLevelILSSARegister<R> {
+ pub fn version(&self) -> u32 {
+ match *self {
+ LowLevelILSSARegister::Full(_, ver) | LowLevelILSSARegister::Partial(_, ver, _) => ver,
+ }
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub enum VisitorAction {
+ Descend,
+ Sibling,
+ Halt,
+}
diff --git a/rust/src/low_level_il/block.rs b/rust/src/low_level_il/block.rs
new file mode 100644
index 00000000..8096c1aa
--- /dev/null
+++ b/rust/src/low_level_il/block.rs
@@ -0,0 +1,108 @@
+// Copyright 2021-2024 Vector 35 Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use std::fmt::Debug;
+use std::ops::Range;
+
+use crate::architecture::Architecture;
+use crate::basic_block::{BasicBlock, BlockContext};
+
+use super::*;
+
+#[derive(Copy)]
+pub struct LowLevelILBlock<'func, A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub(crate) function: &'func LowLevelILFunction<A, M, F>,
+}
+
+impl<'func, A, M, F> BlockContext for LowLevelILBlock<'func, A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ type Instruction = LowLevelILInstruction<'func, A, M, F>;
+ type InstructionIndex = LowLevelInstructionIndex;
+ type Iter = LowLevelILBlockIter<'func, A, M, F>;
+
+ fn start(&self, block: &BasicBlock<Self>) -> LowLevelILInstruction<'func, A, M, F> {
+ self.function
+ .instruction_from_index(block.start_index())
+ .unwrap()
+ }
+
+ fn iter(&self, block: &BasicBlock<Self>) -> LowLevelILBlockIter<'func, A, M, F> {
+ LowLevelILBlockIter {
+ function: self.function,
+ range: (block.start_index().0)..(block.end_index().0),
+ }
+ }
+}
+
+impl<'func, A, M, F> Debug for LowLevelILBlock<'func, A, M, F>
+where
+ A: 'func + Architecture + Debug,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("LowLevelILBlock")
+ .field("function", &self.function)
+ .finish()
+ }
+}
+
+impl<'func, A, M, F> Clone for LowLevelILBlock<'func, A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn clone(&self) -> Self {
+ LowLevelILBlock {
+ function: self.function,
+ }
+ }
+}
+
+pub struct LowLevelILBlockIter<'func, A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ function: &'func LowLevelILFunction<A, M, F>,
+ // TODO: Once step_trait is stable we can do Range<InstructionIndex>
+ range: Range<usize>,
+}
+
+impl<'func, A, M, F> Iterator for LowLevelILBlockIter<'func, A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ type Item = LowLevelILInstruction<'func, A, M, F>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.range
+ .next()
+ .map(LowLevelInstructionIndex)
+ .and_then(|idx| self.function.instruction_from_index(idx))
+ }
+}
diff --git a/rust/src/low_level_il/expression.rs b/rust/src/low_level_il/expression.rs
new file mode 100644
index 00000000..2fc9f93d
--- /dev/null
+++ b/rust/src/low_level_il/expression.rs
@@ -0,0 +1,715 @@
+// Copyright 2021-2024 Vector 35 Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use binaryninjacore_sys::BNGetLowLevelILByIndex;
+use binaryninjacore_sys::BNLowLevelILInstruction;
+
+use super::operation;
+use super::operation::Operation;
+use super::VisitorAction;
+use super::*;
+use crate::architecture::Architecture;
+use std::fmt;
+use std::fmt::{Display, Formatter};
+use std::marker::PhantomData;
+
+/// Used as a marker for an [`LowLevelILExpression`] that **can** produce a value.
+#[derive(Copy, Clone, Debug)]
+pub struct ValueExpr;
+
+/// Used as a marker for an [`LowLevelILExpression`] that can **not** produce a value.
+#[derive(Copy, Clone, Debug)]
+pub struct VoidExpr;
+
+pub trait ExpressionResultType: 'static {}
+impl ExpressionResultType for ValueExpr {}
+impl ExpressionResultType for VoidExpr {}
+
+#[repr(transparent)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct LowLevelExpressionIndex(pub usize);
+
+impl Display for LowLevelExpressionIndex {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_fmt(format_args!("{}", self.0))
+ }
+}
+
+// TODO: Probably want to rename this with a LowLevelIL prefix to avoid collisions when we add handlers for other ILs
+pub trait ExpressionHandler<'func, A, M, F>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn kind(&self) -> LowLevelILExpressionKind<'func, A, M, F>;
+
+ fn visit_tree<T>(&self, f: &mut T) -> VisitorAction
+ where
+ T: FnMut(&LowLevelILExpression<'func, A, M, F, ValueExpr>) -> VisitorAction;
+}
+
+pub struct LowLevelILExpression<'func, A, M, F, R>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+ R: ExpressionResultType,
+{
+ pub(crate) function: &'func LowLevelILFunction<A, M, F>,
+ pub index: LowLevelExpressionIndex,
+
+ // tag the 'return' type of this expression
+ pub(crate) _ty: PhantomData<R>,
+}
+
+impl<'func, A, M, F, R> LowLevelILExpression<'func, A, M, F, R>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+ R: ExpressionResultType,
+{
+ pub(crate) fn new(
+ function: &'func LowLevelILFunction<A, M, F>,
+ index: LowLevelExpressionIndex,
+ ) -> Self {
+ // TODO: Validate expression here?
+ Self {
+ function,
+ index,
+ _ty: PhantomData,
+ }
+ }
+}
+
+impl<'func, A, M, F, R> fmt::Debug for LowLevelILExpression<'func, A, M, F, R>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+ R: ExpressionResultType,
+{
+ fn fmt(&self, f: &mut Formatter) -> fmt::Result {
+ f.debug_struct("Expression")
+ .field("index", &self.index)
+ .finish()
+ }
+}
+
+impl<'func, A, M> ExpressionHandler<'func, A, M, SSA>
+ for LowLevelILExpression<'func, A, M, SSA, ValueExpr>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+{
+ fn kind(&self) -> LowLevelILExpressionKind<'func, A, M, SSA> {
+ #[allow(unused_imports)]
+ use binaryninjacore_sys::BNLowLevelILOperation::*;
+ let op = unsafe { BNGetLowLevelILByIndex(self.function.handle, self.index.0) };
+ #[allow(clippy::match_single_binding)]
+ match op.operation {
+ // Any invalid ops for SSA will be checked here.
+ // SAFETY: We have checked for illegal operations.
+ _ => unsafe { LowLevelILExpressionKind::from_raw(self.function, op) },
+ }
+ }
+
+ fn visit_tree<T>(&self, f: &mut T) -> VisitorAction
+ where
+ T: FnMut(&LowLevelILExpression<'func, A, M, SSA, ValueExpr>) -> VisitorAction,
+ {
+ // Visit the current expression.
+ match f(self) {
+ VisitorAction::Descend => {
+ // Recursively visit sub expressions.
+ self.kind().visit_sub_expressions(|e| e.visit_tree(f))
+ }
+ action => action,
+ }
+ }
+}
+
+impl<'func, A, M> ExpressionHandler<'func, A, M, NonSSA<LiftedNonSSA>>
+ for LowLevelILExpression<'func, A, M, NonSSA<LiftedNonSSA>, ValueExpr>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+{
+ fn kind(&self) -> LowLevelILExpressionKind<'func, A, M, NonSSA<LiftedNonSSA>> {
+ #[allow(unused_imports)]
+ use binaryninjacore_sys::BNLowLevelILOperation::*;
+ let op = unsafe { BNGetLowLevelILByIndex(self.function.handle, self.index.0) };
+ #[allow(clippy::match_single_binding)]
+ match op.operation {
+ // Any invalid ops for Lifted IL will be checked here.
+ // SAFETY: We have checked for illegal operations.
+ _ => unsafe { LowLevelILExpressionKind::from_raw(self.function, op) },
+ }
+ }
+
+ fn visit_tree<T>(&self, f: &mut T) -> VisitorAction
+ where
+ T: FnMut(
+ &LowLevelILExpression<'func, A, M, NonSSA<LiftedNonSSA>, ValueExpr>,
+ ) -> VisitorAction,
+ {
+ // Visit the current expression.
+ match f(self) {
+ VisitorAction::Descend => {
+ // Recursively visit sub expressions.
+ self.kind().visit_sub_expressions(|e| e.visit_tree(f))
+ }
+ action => action,
+ }
+ }
+}
+
+impl<'func, A, M> ExpressionHandler<'func, A, M, NonSSA<RegularNonSSA>>
+ for LowLevelILExpression<'func, A, M, NonSSA<RegularNonSSA>, ValueExpr>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+{
+ fn kind(&self) -> LowLevelILExpressionKind<'func, A, M, NonSSA<RegularNonSSA>> {
+ use binaryninjacore_sys::BNLowLevelILOperation::*;
+ let op = unsafe { BNGetLowLevelILByIndex(self.function.handle, self.index.0) };
+ match op.operation {
+ // Any invalid ops for Non-Lifted IL will be checked here.
+ LLIL_FLAG_COND => unreachable!("LLIL_FLAG_COND is only valid in Lifted IL"),
+ LLIL_FLAG_GROUP => unreachable!("LLIL_FLAG_GROUP is only valid in Lifted IL"),
+ // SAFETY: We have checked for illegal operations.
+ _ => unsafe { LowLevelILExpressionKind::from_raw(self.function, op) },
+ }
+ }
+
+ fn visit_tree<T>(&self, f: &mut T) -> VisitorAction
+ where
+ T: FnMut(
+ &LowLevelILExpression<'func, A, M, NonSSA<RegularNonSSA>, ValueExpr>,
+ ) -> VisitorAction,
+ {
+ // Visit the current expression.
+ match f(self) {
+ VisitorAction::Descend => {
+ // Recursively visit sub expressions.
+ self.kind().visit_sub_expressions(|e| e.visit_tree(f))
+ }
+ action => action,
+ }
+ }
+}
+
+impl<'func, A, F> LowLevelILExpression<'func, A, Finalized, F, ValueExpr>
+where
+ A: 'func + Architecture,
+ F: FunctionForm,
+{
+ // TODO possible values
+}
+
+#[derive(Debug)]
+pub enum LowLevelILExpressionKind<'func, A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ 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>),
+ FlagBit(Operation<'func, A, M, F, operation::FlagBit>),
+ ExternPtr(Operation<'func, A, M, F, operation::Extern>),
+
+ Add(Operation<'func, A, M, F, operation::BinaryOp>),
+ Adc(Operation<'func, A, M, F, operation::BinaryOpCarry>),
+ Sub(Operation<'func, A, M, F, operation::BinaryOp>),
+ Sbb(Operation<'func, A, M, F, operation::BinaryOpCarry>),
+ And(Operation<'func, A, M, F, operation::BinaryOp>),
+ Or(Operation<'func, A, M, F, operation::BinaryOp>),
+ Xor(Operation<'func, A, M, F, operation::BinaryOp>),
+ Lsl(Operation<'func, A, M, F, operation::BinaryOp>),
+ Lsr(Operation<'func, A, M, F, operation::BinaryOp>),
+ Asr(Operation<'func, A, M, F, operation::BinaryOp>),
+ Rol(Operation<'func, A, M, F, operation::BinaryOp>),
+ Rlc(Operation<'func, A, M, F, operation::BinaryOpCarry>),
+ Ror(Operation<'func, A, M, F, operation::BinaryOp>),
+ Rrc(Operation<'func, A, M, F, operation::BinaryOpCarry>),
+ Mul(Operation<'func, A, M, F, operation::BinaryOp>),
+
+ MulsDp(Operation<'func, A, M, F, operation::BinaryOp>),
+ MuluDp(Operation<'func, A, M, F, operation::BinaryOp>),
+
+ Divu(Operation<'func, A, M, F, operation::BinaryOp>),
+ Divs(Operation<'func, A, M, F, operation::BinaryOp>),
+
+ DivuDp(Operation<'func, A, M, F, operation::DoublePrecDivOp>),
+ DivsDp(Operation<'func, A, M, F, operation::DoublePrecDivOp>),
+
+ Modu(Operation<'func, A, M, F, operation::BinaryOp>),
+ Mods(Operation<'func, A, M, F, operation::BinaryOp>),
+
+ ModuDp(Operation<'func, A, M, F, operation::DoublePrecDivOp>),
+ ModsDp(Operation<'func, A, M, F, operation::DoublePrecDivOp>),
+
+ Neg(Operation<'func, A, M, F, operation::UnaryOp>),
+ Not(Operation<'func, A, M, F, operation::UnaryOp>),
+ Sx(Operation<'func, A, M, F, operation::UnaryOp>),
+ Zx(Operation<'func, A, M, F, operation::UnaryOp>),
+ LowPart(Operation<'func, A, M, F, operation::UnaryOp>),
+
+ // Valid only in Lifted IL
+ FlagCond(Operation<'func, A, M, NonSSA<LiftedNonSSA>, operation::FlagCond>),
+ // Valid only in Lifted IL
+ FlagGroup(Operation<'func, A, M, NonSSA<LiftedNonSSA>, operation::FlagGroup>),
+
+ CmpE(Operation<'func, A, M, F, operation::Condition>),
+ CmpNe(Operation<'func, A, M, F, operation::Condition>),
+ CmpSlt(Operation<'func, A, M, F, operation::Condition>),
+ CmpUlt(Operation<'func, A, M, F, operation::Condition>),
+ CmpSle(Operation<'func, A, M, F, operation::Condition>),
+ CmpUle(Operation<'func, A, M, F, operation::Condition>),
+ CmpSge(Operation<'func, A, M, F, operation::Condition>),
+ CmpUge(Operation<'func, A, M, F, operation::Condition>),
+ CmpSgt(Operation<'func, A, M, F, operation::Condition>),
+ CmpUgt(Operation<'func, A, M, F, operation::Condition>),
+
+ //TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO
+ BoolToInt(Operation<'func, A, M, F, operation::UnaryOp>),
+
+ Fadd(Operation<'func, A, M, F, operation::BinaryOp>),
+ Fsub(Operation<'func, A, M, F, operation::BinaryOp>),
+ Fmul(Operation<'func, A, M, F, operation::BinaryOp>),
+ Fdiv(Operation<'func, A, M, F, operation::BinaryOp>),
+ Fsqrt(Operation<'func, A, M, F, operation::UnaryOp>),
+ Fneg(Operation<'func, A, M, F, operation::UnaryOp>),
+ Fabs(Operation<'func, A, M, F, operation::UnaryOp>),
+ FloatToInt(Operation<'func, A, M, F, operation::UnaryOp>),
+ IntToFloat(Operation<'func, A, M, F, operation::UnaryOp>),
+ FloatConv(Operation<'func, A, M, F, operation::UnaryOp>),
+ RoundToInt(Operation<'func, A, M, F, operation::UnaryOp>),
+ Floor(Operation<'func, A, M, F, operation::UnaryOp>),
+ Ceil(Operation<'func, A, M, F, operation::UnaryOp>),
+ Ftrunc(Operation<'func, A, M, F, operation::UnaryOp>),
+
+ FcmpE(Operation<'func, A, M, F, operation::Condition>),
+ FcmpNE(Operation<'func, A, M, F, operation::Condition>),
+ FcmpLT(Operation<'func, A, M, F, operation::Condition>),
+ FcmpLE(Operation<'func, A, M, F, operation::Condition>),
+ FcmpGE(Operation<'func, A, M, F, operation::Condition>),
+ FcmpGT(Operation<'func, A, M, F, operation::Condition>),
+ FcmpO(Operation<'func, A, M, F, operation::Condition>),
+ FcmpUO(Operation<'func, A, M, F, operation::Condition>),
+
+ // TODO ADD_OVERFLOW
+ Unimpl(Operation<'func, A, M, F, operation::NoArgs>),
+ UnimplMem(Operation<'func, A, M, F, operation::UnimplMem>),
+
+ Undef(Operation<'func, A, M, F, operation::NoArgs>),
+}
+
+impl<'func, A, M, F> LowLevelILExpressionKind<'func, A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ // TODO: Document what "unchecked" means and how to consume this safely.
+ pub(crate) unsafe fn from_raw(
+ function: &'func LowLevelILFunction<A, M, F>,
+ op: BNLowLevelILInstruction,
+ ) -> Self {
+ use binaryninjacore_sys::BNLowLevelILOperation::*;
+
+ match op.operation {
+ LLIL_LOAD | LLIL_LOAD_SSA => {
+ LowLevelILExpressionKind::Load(Operation::new(function, op))
+ }
+ LLIL_POP => LowLevelILExpressionKind::Pop(Operation::new(function, op)),
+ LLIL_REG | LLIL_REG_SSA | LLIL_REG_SSA_PARTIAL => {
+ LowLevelILExpressionKind::Reg(Operation::new(function, op))
+ }
+ LLIL_REG_SPLIT | LLIL_REG_SPLIT_SSA => {
+ LowLevelILExpressionKind::RegSplit(Operation::new(function, op))
+ }
+ LLIL_CONST => LowLevelILExpressionKind::Const(Operation::new(function, op)),
+ LLIL_CONST_PTR => LowLevelILExpressionKind::ConstPtr(Operation::new(function, op)),
+ LLIL_FLAG | LLIL_FLAG_SSA => {
+ LowLevelILExpressionKind::Flag(Operation::new(function, op))
+ }
+ LLIL_FLAG_BIT | LLIL_FLAG_BIT_SSA => {
+ LowLevelILExpressionKind::FlagBit(Operation::new(function, op))
+ }
+ LLIL_EXTERN_PTR => LowLevelILExpressionKind::ExternPtr(Operation::new(function, op)),
+
+ LLIL_ADD => LowLevelILExpressionKind::Add(Operation::new(function, op)),
+ LLIL_ADC => LowLevelILExpressionKind::Adc(Operation::new(function, op)),
+ LLIL_SUB => LowLevelILExpressionKind::Sub(Operation::new(function, op)),
+ LLIL_SBB => LowLevelILExpressionKind::Sbb(Operation::new(function, op)),
+ LLIL_AND => LowLevelILExpressionKind::And(Operation::new(function, op)),
+ LLIL_OR => LowLevelILExpressionKind::Or(Operation::new(function, op)),
+ LLIL_XOR => LowLevelILExpressionKind::Xor(Operation::new(function, op)),
+ LLIL_LSL => LowLevelILExpressionKind::Lsl(Operation::new(function, op)),
+ LLIL_LSR => LowLevelILExpressionKind::Lsr(Operation::new(function, op)),
+ LLIL_ASR => LowLevelILExpressionKind::Asr(Operation::new(function, op)),
+ LLIL_ROL => LowLevelILExpressionKind::Rol(Operation::new(function, op)),
+ LLIL_RLC => LowLevelILExpressionKind::Rlc(Operation::new(function, op)),
+ LLIL_ROR => LowLevelILExpressionKind::Ror(Operation::new(function, op)),
+ LLIL_RRC => LowLevelILExpressionKind::Rrc(Operation::new(function, op)),
+ LLIL_MUL => LowLevelILExpressionKind::Mul(Operation::new(function, op)),
+
+ LLIL_MULU_DP => LowLevelILExpressionKind::MuluDp(Operation::new(function, op)),
+ LLIL_MULS_DP => LowLevelILExpressionKind::MulsDp(Operation::new(function, op)),
+
+ LLIL_DIVU => LowLevelILExpressionKind::Divu(Operation::new(function, op)),
+ LLIL_DIVS => LowLevelILExpressionKind::Divs(Operation::new(function, op)),
+
+ LLIL_DIVU_DP => LowLevelILExpressionKind::DivuDp(Operation::new(function, op)),
+ LLIL_DIVS_DP => LowLevelILExpressionKind::DivsDp(Operation::new(function, op)),
+
+ LLIL_MODU => LowLevelILExpressionKind::Modu(Operation::new(function, op)),
+ LLIL_MODS => LowLevelILExpressionKind::Mods(Operation::new(function, op)),
+
+ LLIL_MODU_DP => LowLevelILExpressionKind::ModuDp(Operation::new(function, op)),
+ LLIL_MODS_DP => LowLevelILExpressionKind::ModsDp(Operation::new(function, op)),
+
+ LLIL_NEG => LowLevelILExpressionKind::Neg(Operation::new(function, op)),
+ LLIL_NOT => LowLevelILExpressionKind::Not(Operation::new(function, op)),
+
+ LLIL_SX => LowLevelILExpressionKind::Sx(Operation::new(function, op)),
+ LLIL_ZX => LowLevelILExpressionKind::Zx(Operation::new(function, op)),
+ LLIL_LOW_PART => LowLevelILExpressionKind::LowPart(Operation::new(function, op)),
+
+ LLIL_CMP_E => LowLevelILExpressionKind::CmpE(Operation::new(function, op)),
+ LLIL_CMP_NE => LowLevelILExpressionKind::CmpNe(Operation::new(function, op)),
+ LLIL_CMP_SLT => LowLevelILExpressionKind::CmpSlt(Operation::new(function, op)),
+ LLIL_CMP_ULT => LowLevelILExpressionKind::CmpUlt(Operation::new(function, op)),
+ LLIL_CMP_SLE => LowLevelILExpressionKind::CmpSle(Operation::new(function, op)),
+ LLIL_CMP_ULE => LowLevelILExpressionKind::CmpUle(Operation::new(function, op)),
+ LLIL_CMP_SGE => LowLevelILExpressionKind::CmpSge(Operation::new(function, op)),
+ LLIL_CMP_UGE => LowLevelILExpressionKind::CmpUge(Operation::new(function, op)),
+ LLIL_CMP_SGT => LowLevelILExpressionKind::CmpSgt(Operation::new(function, op)),
+ LLIL_CMP_UGT => LowLevelILExpressionKind::CmpUgt(Operation::new(function, op)),
+
+ LLIL_BOOL_TO_INT => LowLevelILExpressionKind::BoolToInt(Operation::new(function, op)),
+
+ LLIL_FADD => LowLevelILExpressionKind::Fadd(Operation::new(function, op)),
+ LLIL_FSUB => LowLevelILExpressionKind::Fsub(Operation::new(function, op)),
+ LLIL_FMUL => LowLevelILExpressionKind::Fmul(Operation::new(function, op)),
+ LLIL_FDIV => LowLevelILExpressionKind::Fdiv(Operation::new(function, op)),
+
+ LLIL_FSQRT => LowLevelILExpressionKind::Fsqrt(Operation::new(function, op)),
+ LLIL_FNEG => LowLevelILExpressionKind::Fneg(Operation::new(function, op)),
+ LLIL_FABS => LowLevelILExpressionKind::Fabs(Operation::new(function, op)),
+ LLIL_FLOAT_TO_INT => LowLevelILExpressionKind::FloatToInt(Operation::new(function, op)),
+ LLIL_INT_TO_FLOAT => LowLevelILExpressionKind::IntToFloat(Operation::new(function, op)),
+ LLIL_FLOAT_CONV => LowLevelILExpressionKind::FloatConv(Operation::new(function, op)),
+ LLIL_ROUND_TO_INT => LowLevelILExpressionKind::RoundToInt(Operation::new(function, op)),
+ LLIL_FLOOR => LowLevelILExpressionKind::Floor(Operation::new(function, op)),
+ LLIL_CEIL => LowLevelILExpressionKind::Ceil(Operation::new(function, op)),
+ LLIL_FTRUNC => LowLevelILExpressionKind::Ftrunc(Operation::new(function, op)),
+
+ LLIL_FCMP_E => LowLevelILExpressionKind::FcmpE(Operation::new(function, op)),
+ LLIL_FCMP_NE => LowLevelILExpressionKind::FcmpNE(Operation::new(function, op)),
+ LLIL_FCMP_LT => LowLevelILExpressionKind::FcmpLT(Operation::new(function, op)),
+ LLIL_FCMP_LE => LowLevelILExpressionKind::FcmpLE(Operation::new(function, op)),
+ LLIL_FCMP_GT => LowLevelILExpressionKind::FcmpGT(Operation::new(function, op)),
+ LLIL_FCMP_GE => LowLevelILExpressionKind::FcmpGE(Operation::new(function, op)),
+ LLIL_FCMP_O => LowLevelILExpressionKind::FcmpO(Operation::new(function, op)),
+ LLIL_FCMP_UO => LowLevelILExpressionKind::FcmpUO(Operation::new(function, op)),
+
+ LLIL_UNIMPL => LowLevelILExpressionKind::Unimpl(Operation::new(function, op)),
+ LLIL_UNIMPL_MEM => LowLevelILExpressionKind::UnimplMem(Operation::new(function, op)),
+
+ // TODO TEST_BIT ADD_OVERFLOW LLIL_REG_STACK_PUSH LLIL_REG_STACK_POP
+ _ => {
+ #[cfg(debug_assertions)]
+ log::error!(
+ "Got unexpected operation {:?} in value expr at 0x{:x}",
+ op.operation,
+ op.address
+ );
+
+ LowLevelILExpressionKind::Undef(Operation::new(function, op))
+ }
+ }
+ }
+
+ /// Returns the size of the result of this expression
+ ///
+ /// If the expression is malformed or is `Unimpl` there
+ /// is no meaningful size associated with the result.
+ pub fn size(&self) -> Option<usize> {
+ use self::LowLevelILExpressionKind::*;
+
+ match *self {
+ Undef(..) | Unimpl(..) => None,
+
+ FlagCond(..) | FlagGroup(..) | CmpE(..) | CmpNe(..) | CmpSlt(..) | CmpUlt(..)
+ | CmpSle(..) | CmpUle(..) | CmpSge(..) | CmpUge(..) | CmpSgt(..) | CmpUgt(..) => {
+ Some(0)
+ }
+
+ _ => Some(self.raw_struct().size),
+ //TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO
+ }
+ }
+
+ pub fn address(&self) -> u64 {
+ self.raw_struct().address
+ }
+
+ /// Determines if the expressions represent the same operation
+ ///
+ /// It does not examine the operands for equality.
+ pub fn is_same_op_as(&self, other: &Self) -> bool {
+ use self::LowLevelILExpressionKind::*;
+
+ match (self, other) {
+ (&Reg(..), &Reg(..)) => true,
+ _ => self.raw_struct().operation == other.raw_struct().operation,
+ }
+ }
+
+ pub fn as_cmp_op(&self) -> Option<&Operation<'func, A, M, F, operation::Condition>> {
+ use self::LowLevelILExpressionKind::*;
+
+ match *self {
+ CmpE(ref op) | CmpNe(ref op) | CmpSlt(ref op) | CmpUlt(ref op) | CmpSle(ref op)
+ | CmpUle(ref op) | CmpSge(ref op) | CmpUge(ref op) | CmpSgt(ref op)
+ | CmpUgt(ref op) | FcmpE(ref op) | FcmpNE(ref op) | FcmpLT(ref op) | FcmpLE(ref op)
+ | FcmpGE(ref op) | FcmpGT(ref op) | FcmpO(ref op) | FcmpUO(ref op) => Some(op),
+ _ => None,
+ }
+ }
+
+ pub fn as_binary_op(&self) -> Option<&Operation<'func, A, M, F, operation::BinaryOp>> {
+ use self::LowLevelILExpressionKind::*;
+
+ match *self {
+ Add(ref op) | Sub(ref op) | And(ref op) | Or(ref op) | Xor(ref op) | Lsl(ref op)
+ | Lsr(ref op) | Asr(ref op) | Rol(ref op) | Ror(ref op) | Mul(ref op)
+ | MulsDp(ref op) | MuluDp(ref op) | Divu(ref op) | Divs(ref op) | Modu(ref op)
+ | Mods(ref op) | Fadd(ref op) | Fsub(ref op) | Fmul(ref op) | Fdiv(ref op) => Some(op),
+ _ => None,
+ }
+ }
+
+ pub fn as_binary_op_carry(
+ &self,
+ ) -> Option<&Operation<'func, A, M, F, operation::BinaryOpCarry>> {
+ use self::LowLevelILExpressionKind::*;
+
+ match *self {
+ Adc(ref op) | Sbb(ref op) | Rlc(ref op) | Rrc(ref op) => Some(op),
+ _ => None,
+ }
+ }
+
+ pub fn as_double_prec_div_op(
+ &self,
+ ) -> Option<&Operation<'func, A, M, F, operation::DoublePrecDivOp>> {
+ use self::LowLevelILExpressionKind::*;
+
+ match *self {
+ DivuDp(ref op) | DivsDp(ref op) | ModuDp(ref op) | ModsDp(ref op) => Some(op),
+ _ => None,
+ }
+ }
+
+ pub fn as_unary_op(&self) -> Option<&Operation<'func, A, M, F, operation::UnaryOp>> {
+ use self::LowLevelILExpressionKind::*;
+
+ match *self {
+ Neg(ref op) | Not(ref op) | Sx(ref op) | Zx(ref op) | LowPart(ref op)
+ | BoolToInt(ref op) | Fsqrt(ref op) | Fneg(ref op) | Fabs(ref op)
+ | FloatToInt(ref op) | IntToFloat(ref op) | FloatConv(ref op) | RoundToInt(ref op)
+ | Floor(ref op) | Ceil(ref op) | Ftrunc(ref op) => Some(op),
+ _ => None,
+ }
+ }
+
+ pub fn visit_sub_expressions<T>(&self, mut visitor: T) -> VisitorAction
+ where
+ T: FnMut(LowLevelILExpression<'func, A, M, F, ValueExpr>) -> VisitorAction,
+ {
+ use LowLevelILExpressionKind::*;
+
+ macro_rules! visit {
+ ($expr:expr) => {
+ if let VisitorAction::Halt = visitor($expr) {
+ return VisitorAction::Halt;
+ }
+ };
+ }
+
+ match self {
+ CmpE(ref op) | CmpNe(ref op) | CmpSlt(ref op) | CmpUlt(ref op) | CmpSle(ref op)
+ | CmpUle(ref op) | CmpSge(ref op) | CmpUge(ref op) | CmpSgt(ref op)
+ | CmpUgt(ref op) | FcmpE(ref op) | FcmpNE(ref op) | FcmpLT(ref op) | FcmpLE(ref op)
+ | FcmpGE(ref op) | FcmpGT(ref op) | FcmpO(ref op) | FcmpUO(ref op) => {
+ visit!(op.left());
+ visit!(op.right());
+ }
+ Adc(ref op) | Sbb(ref op) | Rlc(ref op) | Rrc(ref op) => {
+ visit!(op.left());
+ visit!(op.right());
+ visit!(op.carry());
+ }
+ Add(ref op) | Sub(ref op) | And(ref op) | Or(ref op) | Xor(ref op) | Lsl(ref op)
+ | Lsr(ref op) | Asr(ref op) | Rol(ref op) | Ror(ref op) | Mul(ref op)
+ | MulsDp(ref op) | MuluDp(ref op) | Divu(ref op) | Divs(ref op) | Modu(ref op)
+ | Mods(ref op) | Fadd(ref op) | Fsub(ref op) | Fmul(ref op) | Fdiv(ref op) => {
+ visit!(op.left());
+ visit!(op.right());
+ }
+ DivuDp(ref op) | DivsDp(ref op) | ModuDp(ref op) | ModsDp(ref op) => {
+ visit!(op.high());
+ visit!(op.low());
+ visit!(op.right());
+ }
+ Neg(ref op) | Not(ref op) | Sx(ref op) | Zx(ref op) | LowPart(ref op)
+ | BoolToInt(ref op) | Fsqrt(ref op) | Fneg(ref op) | Fabs(ref op)
+ | FloatToInt(ref op) | IntToFloat(ref op) | FloatConv(ref op) | RoundToInt(ref op)
+ | Floor(ref op) | Ceil(ref op) | Ftrunc(ref op) => {
+ visit!(op.operand());
+ }
+ UnimplMem(ref op) => {
+ visit!(op.mem_expr());
+ }
+ Load(ref op) => {
+ visit!(op.source_mem_expr());
+ }
+ // Do not have any sub expressions.
+ Pop(_) | Reg(_) | RegSplit(_) | Const(_) | ConstPtr(_) | Flag(_) | FlagBit(_)
+ | ExternPtr(_) | FlagCond(_) | FlagGroup(_) | Unimpl(_) | Undef(_) => {}
+ }
+
+ VisitorAction::Sibling
+ }
+
+ pub(crate) fn raw_struct(&self) -> &BNLowLevelILInstruction {
+ use self::LowLevelILExpressionKind::*;
+
+ match *self {
+ Undef(ref op) => &op.op,
+
+ Unimpl(ref op) => &op.op,
+
+ FlagCond(ref op) => &op.op,
+ FlagGroup(ref op) => &op.op,
+
+ CmpE(ref op) | CmpNe(ref op) | CmpSlt(ref op) | CmpUlt(ref op) | CmpSle(ref op)
+ | CmpUle(ref op) | CmpSge(ref op) | CmpUge(ref op) | CmpSgt(ref op)
+ | CmpUgt(ref op) | FcmpE(ref op) | FcmpNE(ref op) | FcmpLT(ref op) | FcmpLE(ref op)
+ | FcmpGE(ref op) | FcmpGT(ref op) | FcmpO(ref op) | FcmpUO(ref op) => &op.op,
+
+ Load(ref op) => &op.op,
+
+ Pop(ref op) => &op.op,
+
+ Reg(ref op) => &op.op,
+
+ RegSplit(ref op) => &op.op,
+
+ Flag(ref op) => &op.op,
+
+ FlagBit(ref op) => &op.op,
+
+ Const(ref op) | ConstPtr(ref op) => &op.op,
+
+ ExternPtr(ref op) => &op.op,
+
+ Adc(ref op) | Sbb(ref op) | Rlc(ref op) | Rrc(ref op) => &op.op,
+
+ Add(ref op) | Sub(ref op) | And(ref op) | Or(ref op) | Xor(ref op) | Lsl(ref op)
+ | Lsr(ref op) | Asr(ref op) | Rol(ref op) | Ror(ref op) | Mul(ref op)
+ | MulsDp(ref op) | MuluDp(ref op) | Divu(ref op) | Divs(ref op) | Modu(ref op)
+ | Mods(ref op) | Fadd(ref op) | Fsub(ref op) | Fmul(ref op) | Fdiv(ref op) => &op.op,
+
+ DivuDp(ref op) | DivsDp(ref op) | ModuDp(ref op) | ModsDp(ref op) => &op.op,
+
+ Neg(ref op) | Not(ref op) | Sx(ref op) | Zx(ref op) | LowPart(ref op)
+ | BoolToInt(ref op) | Fsqrt(ref op) | Fneg(ref op) | Fabs(ref op)
+ | FloatToInt(ref op) | IntToFloat(ref op) | FloatConv(ref op) | RoundToInt(ref op)
+ | Floor(ref op) | Ceil(ref op) | Ftrunc(ref op) => &op.op,
+
+ UnimplMem(ref op) => &op.op,
+ //TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO
+ }
+ }
+}
+
+impl<'func, A> LowLevelILExpressionKind<'func, A, Mutable, NonSSA<LiftedNonSSA>>
+where
+ A: 'func + Architecture,
+{
+ pub fn flag_write(&self) -> Option<A::FlagWrite> {
+ use self::LowLevelILExpressionKind::*;
+
+ match *self {
+ Undef(ref _op) => None,
+
+ Unimpl(ref _op) => None,
+
+ FlagCond(ref _op) => None,
+ FlagGroup(ref _op) => None,
+
+ CmpE(ref _op) | CmpNe(ref _op) | CmpSlt(ref _op) | CmpUlt(ref _op)
+ | CmpSle(ref _op) | CmpUle(ref _op) | CmpSge(ref _op) | CmpUge(ref _op)
+ | CmpSgt(ref _op) | CmpUgt(ref _op) | FcmpE(ref _op) | FcmpNE(ref _op)
+ | FcmpLT(ref _op) | FcmpLE(ref _op) | FcmpGE(ref _op) | FcmpGT(ref _op)
+ | FcmpO(ref _op) | FcmpUO(ref _op) => None,
+
+ Load(ref op) => op.flag_write(),
+
+ Pop(ref op) => op.flag_write(),
+
+ Reg(ref op) => op.flag_write(),
+
+ RegSplit(ref op) => op.flag_write(),
+
+ Flag(ref op) => op.flag_write(),
+
+ FlagBit(ref op) => op.flag_write(),
+
+ Const(ref op) | ConstPtr(ref op) => op.flag_write(),
+
+ ExternPtr(ref op) => op.flag_write(),
+
+ Adc(ref op) | Sbb(ref op) | Rlc(ref op) | Rrc(ref op) => op.flag_write(),
+
+ Add(ref op) | Sub(ref op) | And(ref op) | Or(ref op) | Xor(ref op) | Lsl(ref op)
+ | Lsr(ref op) | Asr(ref op) | Rol(ref op) | Ror(ref op) | Mul(ref op)
+ | MulsDp(ref op) | MuluDp(ref op) | Divu(ref op) | Divs(ref op) | Modu(ref op)
+ | Mods(ref op) | Fadd(ref op) | Fsub(ref op) | Fmul(ref op) | Fdiv(ref op) => {
+ op.flag_write()
+ }
+
+ DivuDp(ref op) | DivsDp(ref op) | ModuDp(ref op) | ModsDp(ref op) => op.flag_write(),
+
+ Neg(ref op) | Not(ref op) | Sx(ref op) | Zx(ref op) | LowPart(ref op)
+ | BoolToInt(ref op) | Fsqrt(ref op) | Fneg(ref op) | Fabs(ref op)
+ | FloatToInt(ref op) | IntToFloat(ref op) | FloatConv(ref op) | RoundToInt(ref op)
+ | Floor(ref op) | Ceil(ref op) | Ftrunc(ref op) => op.flag_write(),
+
+ UnimplMem(ref op) => op.flag_write(),
+ //TestBit(Operation<'func, A, M, F, operation::TestBit>), // TODO
+ }
+ }
+}
diff --git a/rust/src/llil/function.rs b/rust/src/low_level_il/function.rs
index abc501e5..fea467d0 100644
--- a/rust/src/llil/function.rs
+++ b/rust/src/low_level_il/function.rs
@@ -18,10 +18,14 @@ use binaryninjacore_sys::BNLowLevelILFunction;
use binaryninjacore_sys::BNNewLowLevelILFunctionReference;
use std::borrow::Borrow;
+use std::fmt::Debug;
+use std::hash::{Hash, Hasher};
use std::marker::PhantomData;
use crate::architecture::CoreArchitecture;
-use crate::basicblock::BasicBlock;
+use crate::basic_block::BasicBlock;
+use crate::function::Function;
+use crate::low_level_il::block::LowLevelILBlock;
use crate::rc::*;
use super::*;
@@ -53,92 +57,82 @@ pub trait FunctionForm: 'static {}
impl FunctionForm for SSA {}
impl<V: NonSSAVariant> FunctionForm for NonSSA<V> {}
-pub struct Function<A: Architecture, M: FunctionMutability, F: FunctionForm> {
- pub(crate) borrower: A::Handle,
+pub struct LowLevelILFunction<A: Architecture, M: FunctionMutability, F: FunctionForm> {
+ pub(crate) arch_handle: A::Handle,
pub(crate) handle: *mut BNLowLevelILFunction,
_arch: PhantomData<*mut A>,
_mutability: PhantomData<M>,
_form: PhantomData<F>,
}
-unsafe impl<A: Architecture, M: FunctionMutability, F: FunctionForm> Send for Function<A, M, F> {}
-unsafe impl<A: Architecture, M: FunctionMutability, F: FunctionForm> Sync for Function<A, M, F> {}
-
-impl<A: Architecture, M: FunctionMutability, F: FunctionForm> Eq for Function<A, M, F> {}
-impl<A: Architecture, M: FunctionMutability, F: FunctionForm> PartialEq for Function<A, M, F> {
- fn eq(&self, rhs: &Self) -> bool {
- self.get_function().eq(&rhs.get_function())
- }
-}
-
-use std::hash::{Hash, Hasher};
-impl<A: Architecture, M: FunctionMutability, F: FunctionForm> Hash for Function<A, M, F> {
- fn hash<H: Hasher>(&self, state: &mut H) {
- self.get_function().hash(state)
- }
-}
-
-impl<'func, A, M, F> Function<A, M, F>
+impl<A, M, F> LowLevelILFunction<A, M, F>
where
- A: 'func + Architecture,
+ A: Architecture,
M: FunctionMutability,
F: FunctionForm,
{
- pub(crate) unsafe fn from_raw(borrower: A::Handle, handle: *mut BNLowLevelILFunction) -> Self {
+ pub(crate) unsafe fn from_raw(
+ arch_handle: A::Handle,
+ handle: *mut BNLowLevelILFunction,
+ ) -> Self {
debug_assert!(!handle.is_null());
Self {
- borrower,
+ arch_handle,
handle,
_arch: PhantomData,
_mutability: PhantomData,
_form: PhantomData,
}
}
-
+
pub(crate) unsafe fn ref_from_raw(
- borrower: A::Handle,
+ arch_handle: A::Handle,
handle: *mut BNLowLevelILFunction,
) -> Ref<Self> {
- Ref::new(Self::from_raw(borrower, handle))
+ debug_assert!(!handle.is_null());
+ Ref::new(Self::from_raw(arch_handle, handle))
}
pub(crate) fn arch(&self) -> &A {
- self.borrower.borrow()
+ self.arch_handle.borrow()
}
- pub fn instruction_at<L: Into<Location>>(&self, loc: L) -> Option<Instruction<A, M, F>> {
- use binaryninjacore_sys::BNGetLowLevelILInstructionCount;
- use binaryninjacore_sys::BNLowLevelILGetInstructionStart;
+ pub fn instruction_at<L: Into<Location>>(
+ &self,
+ loc: L,
+ ) -> Option<LowLevelILInstruction<A, M, F>> {
+ Some(LowLevelILInstruction::new(
+ self,
+ self.instruction_index_at(loc)?,
+ ))
+ }
+ pub fn instruction_index_at<L: Into<Location>>(
+ &self,
+ loc: L,
+ ) -> Option<LowLevelInstructionIndex> {
+ use binaryninjacore_sys::BNLowLevelILGetInstructionStart;
let loc: Location = loc.into();
- let arch_handle = loc.arch.unwrap_or_else(|| *self.arch().as_ref());
-
- unsafe {
- let instr_idx = BNLowLevelILGetInstructionStart(self.handle, arch_handle.0, loc.addr);
-
- if instr_idx >= BNGetLowLevelILInstructionCount(self.handle) {
- None
- } else {
- Some(Instruction {
- function: self,
- instr_idx,
- })
- }
+ let arch = loc.arch.unwrap_or_else(|| *self.arch().as_ref());
+ let instr_idx =
+ unsafe { BNLowLevelILGetInstructionStart(self.handle, arch.handle, loc.addr) };
+ // `instr_idx` will equal self.instruction_count() if the instruction is not valid.
+ if instr_idx >= self.instruction_count() {
+ None
+ } else {
+ Some(LowLevelInstructionIndex(instr_idx))
}
}
- pub fn instruction_from_idx(&self, instr_idx: usize) -> Instruction<A, M, F> {
- unsafe {
- use binaryninjacore_sys::BNGetLowLevelILInstructionCount;
- if instr_idx >= BNGetLowLevelILInstructionCount(self.handle) {
- panic!("instruction index {} out of bounds", instr_idx);
- }
-
- Instruction {
- function: self,
- instr_idx,
- }
+ pub fn instruction_from_index(
+ &self,
+ index: LowLevelInstructionIndex,
+ ) -> Option<LowLevelILInstruction<A, M, F>> {
+ if index.0 >= self.instruction_count() {
+ None
+ } else {
+ Some(LowLevelILInstruction::new(self, index))
}
}
@@ -149,10 +143,17 @@ where
}
}
- pub fn get_function(&self) -> Ref<crate::function::Function> {
+ pub fn expression_count(&self) -> usize {
+ unsafe {
+ use binaryninjacore_sys::BNGetLowLevelILExprCount;
+ BNGetLowLevelILExprCount(self.handle)
+ }
+ }
+
+ pub fn function(&self) -> Ref<Function> {
unsafe {
let func = BNGetLowLevelILOwnerFunction(self.handle);
- crate::function::Function::from_raw(func)
+ Function::ref_from_raw(func)
}
}
}
@@ -160,58 +161,48 @@ where
// LLIL basic blocks are not available until the function object
// is finalized, so ensure we can't try requesting basic blocks
// during lifting
-impl<'func, A, F> Function<A, Finalized, F>
+impl<A, F> LowLevelILFunction<A, Finalized, F>
where
- A: 'func + Architecture,
+ A: Architecture,
F: FunctionForm,
{
- pub fn basic_blocks(&self) -> Array<BasicBlock<LowLevelBlock<A, Finalized, F>>> {
+ pub fn basic_blocks(&self) -> Array<BasicBlock<LowLevelILBlock<A, Finalized, F>>> {
use binaryninjacore_sys::BNGetLowLevelILBasicBlockList;
unsafe {
let mut count = 0;
let blocks = BNGetLowLevelILBasicBlockList(self.handle, &mut count);
- let context = LowLevelBlock { function: self };
-
+ let context = LowLevelILBlock { function: self };
Array::new(blocks, count, context)
}
}
}
// Allow instantiating Lifted IL functions for querying Lifted IL from Architectures
-impl Function<CoreArchitecture, Mutable, NonSSA<LiftedNonSSA>> {
- pub fn new(
- arch: CoreArchitecture,
- source_func: Option<crate::function::Function>,
- ) -> Result<Ref<Self>, ()> {
+impl LowLevelILFunction<CoreArchitecture, Mutable, NonSSA<LiftedNonSSA>> {
+ // TODO: Document what happens when you pass None for `source_func`.
+ // TODO: Doing so would construct a LowLevelILFunction with no basic blocks
+ // TODO: Document why you would want to do that.
+ pub fn new(arch: CoreArchitecture, source_func: Option<Function>) -> Ref<Self> {
use binaryninjacore_sys::BNCreateLowLevelILFunction;
- use std::ptr::null_mut;
let handle = unsafe {
match source_func {
- Some(func) => BNCreateLowLevelILFunction(arch.0, func.handle),
- None => BNCreateLowLevelILFunction(arch.0, null_mut()),
+ Some(func) => BNCreateLowLevelILFunction(arch.handle, func.handle),
+ None => BNCreateLowLevelILFunction(arch.handle, std::ptr::null_mut()),
}
};
- if handle.is_null() {
- return Err(());
- }
- Ok(unsafe {
- Ref::new(Self {
- borrower: arch,
- handle,
- _arch: PhantomData,
- _mutability: PhantomData,
- _form: PhantomData,
- })
- })
+ // BNCreateLowLevelILFunction should always return a valid object.
+ assert!(!handle.is_null());
+
+ unsafe { Self::ref_from_raw(arch, handle) }
}
}
-impl<'func, A, M, F> ToOwned for Function<A, M, F>
+impl<A, M, F> ToOwned for LowLevelILFunction<A, M, F>
where
- A: 'func + Architecture,
+ A: Architecture,
M: FunctionMutability,
F: FunctionForm,
{
@@ -222,15 +213,15 @@ where
}
}
-unsafe impl<'func, A, M, F> RefCountable for Function<A, M, F>
+unsafe impl<A, M, F> RefCountable for LowLevelILFunction<A, M, F>
where
- A: 'func + Architecture,
+ A: Architecture,
M: FunctionMutability,
F: FunctionForm,
{
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
Ref::new(Self {
- borrower: handle.borrower.clone(),
+ arch_handle: handle.arch_handle.clone(),
handle: BNNewLowLevelILFunctionReference(handle.handle),
_arch: PhantomData,
_mutability: PhantomData,
@@ -243,13 +234,42 @@ where
}
}
-impl<'func, A, M, F> fmt::Debug for Function<A, M, F>
+impl<A, M, F> Debug for LowLevelILFunction<A, M, F>
where
- A: 'func + Architecture,
+ A: Architecture + Debug,
M: FunctionMutability,
F: FunctionForm,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(f, "<llil func handle {:p}>", self.handle)
+ f.debug_struct("LowLevelILFunction")
+ .field("arch", &self.arch())
+ .field("instruction_count", &self.instruction_count())
+ .field("expression_count", &self.expression_count())
+ .finish()
+ }
+}
+
+unsafe impl<A: Architecture, M: FunctionMutability, F: FunctionForm> Send
+ for LowLevelILFunction<A, M, F>
+{
+}
+unsafe impl<A: Architecture, M: FunctionMutability, F: FunctionForm> Sync
+ for LowLevelILFunction<A, M, F>
+{
+}
+
+impl<A: Architecture, M: FunctionMutability, F: FunctionForm> Eq for LowLevelILFunction<A, M, F> {}
+
+impl<A: Architecture, M: FunctionMutability, F: FunctionForm> PartialEq
+ for LowLevelILFunction<A, M, F>
+{
+ fn eq(&self, rhs: &Self) -> bool {
+ self.function().eq(&rhs.function())
+ }
+}
+
+impl<A: Architecture, M: FunctionMutability, F: FunctionForm> Hash for LowLevelILFunction<A, M, F> {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.function().hash(state)
}
}
diff --git a/rust/src/low_level_il/instruction.rs b/rust/src/low_level_il/instruction.rs
new file mode 100644
index 00000000..5c3010ea
--- /dev/null
+++ b/rust/src/low_level_il/instruction.rs
@@ -0,0 +1,348 @@
+// Copyright 2021-2024 Vector 35 Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use super::operation;
+use super::operation::Operation;
+use super::VisitorAction;
+use super::*;
+use crate::architecture::Architecture;
+use binaryninjacore_sys::BNGetLowLevelILByIndex;
+use binaryninjacore_sys::BNGetLowLevelILIndexForInstruction;
+use binaryninjacore_sys::BNLowLevelILInstruction;
+use std::fmt::{Debug, Display, Formatter};
+
+#[repr(transparent)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct LowLevelInstructionIndex(pub usize);
+
+impl LowLevelInstructionIndex {
+ pub fn next(&self) -> Self {
+ Self(self.0 + 1)
+ }
+}
+
+impl From<usize> for LowLevelInstructionIndex {
+ fn from(index: usize) -> Self {
+ Self(index)
+ }
+}
+
+impl From<u64> for LowLevelInstructionIndex {
+ fn from(index: u64) -> Self {
+ Self(index as usize)
+ }
+}
+
+impl Display for LowLevelInstructionIndex {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_fmt(format_args!("{}", self.0))
+ }
+}
+
+// TODO: Probably want to rename this with a LowLevelIL prefix to avoid collisions when we add handlers for other ILs
+pub trait InstructionHandler<'func, A, M, F>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn kind(&self) -> LowLevelILInstructionKind<'func, A, M, F>;
+
+ /// Visit the sub expressions of this instruction.
+ ///
+ /// NOTE: This does not visit the root expression, i.e. the instruction.
+ fn visit_tree<T>(&self, f: &mut T) -> VisitorAction
+ where
+ T: FnMut(&LowLevelILExpression<'func, A, M, F, ValueExpr>) -> VisitorAction;
+}
+
+pub struct LowLevelILInstruction<'func, A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub(crate) function: &'func LowLevelILFunction<A, M, F>,
+ pub index: LowLevelInstructionIndex,
+}
+
+impl<'func, A, M, F> LowLevelILInstruction<'func, A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ // TODO: Should we check the instruction count here with BNGetLowLevelILInstructionCount?
+ // TODO: If we _can_ then this should become an Option<Self> methinks
+ pub fn new(
+ function: &'func LowLevelILFunction<A, M, F>,
+ index: LowLevelInstructionIndex,
+ ) -> Self {
+ Self { function, index }
+ }
+
+ pub fn address(&self) -> u64 {
+ self.into_raw().address
+ }
+
+ // TODO: Document the difference between the self.index and the expr_idx.
+ pub fn expr_idx(&self) -> LowLevelExpressionIndex {
+ let idx = unsafe { BNGetLowLevelILIndexForInstruction(self.function.handle, self.index.0) };
+ LowLevelExpressionIndex(idx)
+ }
+
+ pub fn into_raw(&self) -> BNLowLevelILInstruction {
+ unsafe { BNGetLowLevelILByIndex(self.function.handle, self.expr_idx().0) }
+ }
+}
+
+impl<'func, A, M, F> Debug for LowLevelILInstruction<'func, A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Instruction")
+ .field("index", &self.index)
+ .field("expr_idx", &self.expr_idx())
+ .field("address", &self.address())
+ .finish()
+ }
+}
+
+impl<'func, A, M> InstructionHandler<'func, A, M, SSA> for LowLevelILInstruction<'func, A, M, SSA>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+{
+ fn kind(&self) -> LowLevelILInstructionKind<'func, A, M, SSA> {
+ #[allow(unused_imports)]
+ use binaryninjacore_sys::BNLowLevelILOperation::*;
+ let raw_op = self.into_raw();
+ #[allow(clippy::match_single_binding)]
+ match raw_op.operation {
+ // Any invalid ops for Non-Lifted IL will be checked here.
+ // SAFETY: We have checked for illegal operations.
+ _ => unsafe {
+ LowLevelILInstructionKind::from_raw(self.function, self.expr_idx(), raw_op)
+ },
+ }
+ }
+
+ fn visit_tree<T>(&self, f: &mut T) -> VisitorAction
+ where
+ T: FnMut(&LowLevelILExpression<'func, A, M, SSA, ValueExpr>) -> VisitorAction,
+ {
+ // Recursively visit sub expressions.
+ self.kind().visit_sub_expressions(|e| e.visit_tree(f))
+ }
+}
+
+impl<'func, A, M> InstructionHandler<'func, A, M, NonSSA<LiftedNonSSA>>
+ for LowLevelILInstruction<'func, A, M, NonSSA<LiftedNonSSA>>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+{
+ fn kind(&self) -> LowLevelILInstructionKind<'func, A, M, NonSSA<LiftedNonSSA>> {
+ #[allow(unused_imports)]
+ use binaryninjacore_sys::BNLowLevelILOperation::*;
+ let raw_op = self.into_raw();
+ #[allow(clippy::match_single_binding)]
+ match raw_op.operation {
+ // Any invalid ops for Non-Lifted IL will be checked here.
+ // SAFETY: We have checked for illegal operations.
+ _ => unsafe {
+ LowLevelILInstructionKind::from_raw(self.function, self.expr_idx(), raw_op)
+ },
+ }
+ }
+
+ fn visit_tree<T>(&self, f: &mut T) -> VisitorAction
+ where
+ T: FnMut(
+ &LowLevelILExpression<'func, A, M, NonSSA<LiftedNonSSA>, ValueExpr>,
+ ) -> VisitorAction,
+ {
+ // Recursively visit sub expressions.
+ self.kind().visit_sub_expressions(|e| e.visit_tree(f))
+ }
+}
+
+impl<'func, A, M> InstructionHandler<'func, A, M, NonSSA<RegularNonSSA>>
+ for LowLevelILInstruction<'func, A, M, NonSSA<RegularNonSSA>>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+{
+ fn kind(&self) -> LowLevelILInstructionKind<'func, A, M, NonSSA<RegularNonSSA>> {
+ #[allow(unused_imports)]
+ use binaryninjacore_sys::BNLowLevelILOperation::*;
+ let raw_op = self.into_raw();
+ #[allow(clippy::match_single_binding)]
+ match raw_op.operation {
+ // Any invalid ops for Non-Lifted IL will be checked here.
+ // SAFETY: We have checked for illegal operations.
+ _ => unsafe {
+ LowLevelILInstructionKind::from_raw(self.function, self.expr_idx(), raw_op)
+ },
+ }
+ }
+
+ fn visit_tree<T>(&self, f: &mut T) -> VisitorAction
+ where
+ T: FnMut(
+ &LowLevelILExpression<'func, A, M, NonSSA<RegularNonSSA>, ValueExpr>,
+ ) -> VisitorAction,
+ {
+ // Recursively visit sub expressions.
+ self.kind().visit_sub_expressions(|e| e.visit_tree(f))
+ }
+}
+
+#[derive(Debug)]
+pub enum LowLevelILInstructionKind<'func, A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ Nop(Operation<'func, A, M, F, operation::NoArgs>),
+ SetReg(Operation<'func, A, M, F, operation::SetReg>),
+ SetRegSplit(Operation<'func, A, M, F, operation::SetRegSplit>),
+ SetFlag(Operation<'func, A, M, F, operation::SetFlag>),
+ Store(Operation<'func, A, M, F, operation::Store>),
+ // TODO needs a real op
+ Push(Operation<'func, A, M, F, operation::UnaryOp>),
+
+ Jump(Operation<'func, A, M, F, operation::Jump>),
+ JumpTo(Operation<'func, A, M, F, operation::JumpTo>),
+
+ Call(Operation<'func, A, M, F, operation::Call>),
+ TailCall(Operation<'func, A, M, F, operation::Call>),
+
+ Ret(Operation<'func, A, M, F, operation::Ret>),
+ NoRet(Operation<'func, A, M, F, operation::NoArgs>),
+
+ If(Operation<'func, A, M, F, operation::If>),
+ Goto(Operation<'func, A, M, F, operation::Goto>),
+
+ Syscall(Operation<'func, A, M, F, operation::Syscall>),
+ Intrinsic(Operation<'func, A, M, F, operation::Intrinsic>),
+ Bp(Operation<'func, A, M, F, operation::NoArgs>),
+ Trap(Operation<'func, A, M, F, operation::Trap>),
+ Undef(Operation<'func, A, M, F, operation::NoArgs>),
+
+ /// The instruction is an expression.
+ Value(LowLevelILExpression<'func, A, M, F, ValueExpr>),
+}
+
+impl<'func, A, M, F> LowLevelILInstructionKind<'func, A, M, F>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub(crate) unsafe fn from_raw(
+ function: &'func LowLevelILFunction<A, M, F>,
+ expr_index: LowLevelExpressionIndex,
+ op: BNLowLevelILInstruction,
+ ) -> Self {
+ use binaryninjacore_sys::BNLowLevelILOperation::*;
+
+ match op.operation {
+ LLIL_NOP => LowLevelILInstructionKind::Nop(Operation::new(function, op)),
+ LLIL_SET_REG | LLIL_SET_REG_SSA => {
+ LowLevelILInstructionKind::SetReg(Operation::new(function, op))
+ }
+ LLIL_SET_REG_SPLIT | LLIL_SET_REG_SPLIT_SSA => {
+ LowLevelILInstructionKind::SetRegSplit(Operation::new(function, op))
+ }
+ LLIL_SET_FLAG | LLIL_SET_FLAG_SSA => {
+ LowLevelILInstructionKind::SetFlag(Operation::new(function, op))
+ }
+ LLIL_STORE | LLIL_STORE_SSA => {
+ LowLevelILInstructionKind::Store(Operation::new(function, op))
+ }
+ LLIL_PUSH => LowLevelILInstructionKind::Push(Operation::new(function, op)),
+
+ LLIL_JUMP => LowLevelILInstructionKind::Jump(Operation::new(function, op)),
+ LLIL_JUMP_TO => LowLevelILInstructionKind::JumpTo(Operation::new(function, op)),
+
+ LLIL_CALL | LLIL_CALL_STACK_ADJUST | LLIL_CALL_SSA => {
+ LowLevelILInstructionKind::Call(Operation::new(function, op))
+ }
+ LLIL_TAILCALL | LLIL_TAILCALL_SSA => {
+ LowLevelILInstructionKind::TailCall(Operation::new(function, op))
+ }
+
+ LLIL_RET => LowLevelILInstructionKind::Ret(Operation::new(function, op)),
+ LLIL_NORET => LowLevelILInstructionKind::NoRet(Operation::new(function, op)),
+
+ LLIL_IF => LowLevelILInstructionKind::If(Operation::new(function, op)),
+ LLIL_GOTO => LowLevelILInstructionKind::Goto(Operation::new(function, op)),
+
+ LLIL_SYSCALL | LLIL_SYSCALL_SSA => {
+ LowLevelILInstructionKind::Syscall(Operation::new(function, op))
+ }
+ LLIL_INTRINSIC | LLIL_INTRINSIC_SSA => {
+ LowLevelILInstructionKind::Intrinsic(Operation::new(function, op))
+ }
+ LLIL_BP => LowLevelILInstructionKind::Bp(Operation::new(function, op)),
+ LLIL_TRAP => LowLevelILInstructionKind::Trap(Operation::new(function, op)),
+ LLIL_UNDEF => LowLevelILInstructionKind::Undef(Operation::new(function, op)),
+ _ => LowLevelILInstructionKind::Value(LowLevelILExpression::new(function, expr_index)),
+ }
+ }
+
+ fn visit_sub_expressions<T>(&self, mut visitor: T) -> VisitorAction
+ where
+ T: FnMut(&LowLevelILExpression<'func, A, M, F, ValueExpr>) -> VisitorAction,
+ {
+ use LowLevelILInstructionKind::*;
+
+ macro_rules! visit {
+ ($expr:expr) => {
+ if let VisitorAction::Halt = visitor($expr) {
+ return VisitorAction::Halt;
+ }
+ };
+ }
+
+ match self {
+ SetReg(ref op) => visit!(&op.source_expr()),
+ SetRegSplit(ref op) => visit!(&op.source_expr()),
+ SetFlag(ref op) => visit!(&op.source_expr()),
+ Store(ref op) => {
+ visit!(&op.dest_mem_expr());
+ visit!(&op.source_expr());
+ }
+ Push(ref op) => visit!(&op.operand()),
+ Jump(ref op) => visit!(&op.target()),
+ JumpTo(ref op) => visit!(&op.target()),
+ Call(ref op) | TailCall(ref op) => visit!(&op.target()),
+ Ret(ref op) => visit!(&op.target()),
+ If(ref op) => visit!(&op.condition()),
+ Intrinsic(ref _op) => {
+ // TODO: Visit when we support expression lists
+ }
+ Value(e) => visit!(e),
+ // Do not have any sub expressions.
+ Nop(_) | NoRet(_) | Goto(_) | Syscall(_) | Bp(_) | Trap(_) | Undef(_) => {}
+ }
+
+ VisitorAction::Sibling
+ }
+}
diff --git a/rust/src/llil/lifting.rs b/rust/src/low_level_il/lifting.rs
index 840e029b..430cb0f7 100644
--- a/rust/src/llil/lifting.rs
+++ b/rust/src/low_level_il/lifting.rs
@@ -13,52 +13,52 @@
// limitations under the License.
use std::marker::PhantomData;
-use std::mem;
-use crate::architecture::Architecture;
+use binaryninjacore_sys::{BNAddLowLevelILLabelForAddress, BNLowLevelILOperation};
+use binaryninjacore_sys::{BNLowLevelILLabel, BNRegisterOrConstant};
+
+use super::*;
use crate::architecture::Register as ArchReg;
+use crate::architecture::{Architecture, FlagWriteId, RegisterId};
use crate::architecture::{
Flag, FlagClass, FlagCondition, FlagGroup, FlagRole, FlagWrite, Intrinsic,
};
+use crate::function::Location;
-use super::*;
-
-pub trait Liftable<'func, A: 'func + Architecture> {
+pub trait LiftableLowLevelIL<'func, A: 'func + Architecture> {
type Result: ExpressionResultType;
fn lift(
- il: &'func Function<A, Mutable, NonSSA<LiftedNonSSA>>,
+ il: &'func MutableLiftedILFunction<A>,
expr: Self,
- ) -> Expression<'func, A, Mutable, NonSSA<LiftedNonSSA>, Self::Result>;
+ ) -> MutableLiftedILExpr<'func, A, Self::Result>;
}
-pub trait LiftableWithSize<'func, A: 'func + Architecture>:
- Liftable<'func, A, Result = ValueExpr>
+pub trait LiftableLowLevelILWithSize<'func, A: 'func + Architecture>:
+ LiftableLowLevelIL<'func, A, Result = ValueExpr>
{
fn lift_with_size(
- il: &'func Function<A, Mutable, NonSSA<LiftedNonSSA>>,
+ il: &'func MutableLiftedILFunction<A>,
expr: Self,
size: usize,
- ) -> Expression<'func, A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr>;
+ ) -> MutableLiftedILExpr<'func, A, ValueExpr>;
}
-use binaryninjacore_sys::BNRegisterOrConstant;
-
#[derive(Copy, Clone)]
-pub enum RegisterOrConstant<R: ArchReg> {
- Register(usize, Register<R>),
+pub enum LowLevelILRegisterOrConstant<R: ArchReg> {
+ Register(usize, LowLevelILRegister<R>),
Constant(usize, u64),
}
-impl<R: ArchReg> RegisterOrConstant<R> {
- pub(crate) fn into_api(self) -> BNRegisterOrConstant {
- match self {
- RegisterOrConstant::Register(_, r) => BNRegisterOrConstant {
+impl<R: ArchReg> From<LowLevelILRegisterOrConstant<R>> for BNRegisterOrConstant {
+ fn from(value: LowLevelILRegisterOrConstant<R>) -> Self {
+ match value {
+ LowLevelILRegisterOrConstant::Register(_, r) => Self {
constant: false,
- reg: r.id(),
+ reg: r.id().0,
value: 0,
},
- RegisterOrConstant::Constant(_, value) => BNRegisterOrConstant {
+ LowLevelILRegisterOrConstant::Constant(_, value) => Self {
constant: true,
reg: 0,
value,
@@ -69,79 +69,179 @@ impl<R: ArchReg> RegisterOrConstant<R> {
// TODO flesh way out
#[derive(Copy, Clone)]
-pub enum FlagWriteOp<R: ArchReg> {
- SetReg(usize, RegisterOrConstant<R>),
- SetRegSplit(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+pub enum LowLevelILFlagWriteOp<R: ArchReg> {
+ SetReg(usize, LowLevelILRegisterOrConstant<R>),
+ SetRegSplit(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
- Sub(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
- Add(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+ Sub(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ Add(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
- Load(usize, RegisterOrConstant<R>),
+ Load(usize, LowLevelILRegisterOrConstant<R>),
- Push(usize, RegisterOrConstant<R>),
- Neg(usize, RegisterOrConstant<R>),
- Not(usize, RegisterOrConstant<R>),
- Sx(usize, RegisterOrConstant<R>),
- Zx(usize, RegisterOrConstant<R>),
- LowPart(usize, RegisterOrConstant<R>),
- BoolToInt(usize, RegisterOrConstant<R>),
- FloatToInt(usize, RegisterOrConstant<R>),
+ Push(usize, LowLevelILRegisterOrConstant<R>),
+ Neg(usize, LowLevelILRegisterOrConstant<R>),
+ Not(usize, LowLevelILRegisterOrConstant<R>),
+ Sx(usize, LowLevelILRegisterOrConstant<R>),
+ Zx(usize, LowLevelILRegisterOrConstant<R>),
+ LowPart(usize, LowLevelILRegisterOrConstant<R>),
+ BoolToInt(usize, LowLevelILRegisterOrConstant<R>),
+ FloatToInt(usize, LowLevelILRegisterOrConstant<R>),
- Store(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+ Store(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
- And(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
- Or(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
- Xor(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
- Lsl(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
- Lsr(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
- Asr(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
- Rol(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
- Ror(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
- Mul(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
- MuluDp(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
- MulsDp(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
- Divu(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
- Divs(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
- Modu(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
- Mods(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
- DivuDp(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
- DivsDp(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
- ModuDp(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
- ModsDp(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+ And(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ Or(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ Xor(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ Lsl(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ Lsr(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ Asr(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ Rol(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ Ror(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ Mul(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ MuluDp(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ MulsDp(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ Divu(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ Divs(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ Modu(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ Mods(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ DivuDp(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ DivsDp(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ ModuDp(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ ModsDp(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
- TestBit(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
- AddOverflow(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+ TestBit(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
+ AddOverflow(
+ usize,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ ),
Adc(
usize,
- RegisterOrConstant<R>,
- RegisterOrConstant<R>,
- RegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
),
Sbb(
usize,
- RegisterOrConstant<R>,
- RegisterOrConstant<R>,
- RegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
),
Rlc(
usize,
- RegisterOrConstant<R>,
- RegisterOrConstant<R>,
- RegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
),
Rrc(
usize,
- RegisterOrConstant<R>,
- RegisterOrConstant<R>,
- RegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
+ LowLevelILRegisterOrConstant<R>,
),
Pop(usize),
// TODO: floating point stuff, llil comparison ops that set flags, intrinsics
}
-impl<R: ArchReg> FlagWriteOp<R> {
+impl<R: ArchReg> LowLevelILFlagWriteOp<R> {
pub(crate) fn from_op<A>(
arch: &A,
size: usize,
@@ -152,28 +252,30 @@ impl<R: ArchReg> FlagWriteOp<R> {
A: Architecture<Register = R>,
R: ArchReg<InfoType = A::RegisterInfo>,
{
- use self::FlagWriteOp::*;
+ use self::LowLevelILFlagWriteOp::*;
use binaryninjacore_sys::BNLowLevelILOperation::*;
fn build_op<A, R>(
arch: &A,
size: usize,
operand: &BNRegisterOrConstant,
- ) -> RegisterOrConstant<R>
+ ) -> LowLevelILRegisterOrConstant<R>
where
A: Architecture<Register = R>,
R: ArchReg<InfoType = A::RegisterInfo>,
{
if operand.constant {
- RegisterOrConstant::Constant(size, operand.value)
+ LowLevelILRegisterOrConstant::Constant(size, operand.value)
} else {
let il_reg = if 0x8000_0000 & operand.reg == 0 {
- Register::ArchReg(arch.register_from_id(operand.reg).unwrap())
+ LowLevelILRegister::ArchReg(
+ arch.register_from_id(RegisterId(operand.reg)).unwrap(),
+ )
} else {
- Register::Temp(operand.reg)
+ LowLevelILRegister::Temp(operand.reg)
};
- RegisterOrConstant::Register(size, il_reg)
+ LowLevelILRegisterOrConstant::Register(size, il_reg)
}
}
@@ -238,7 +340,7 @@ impl<R: ArchReg> FlagWriteOp<R> {
}
pub(crate) fn size_and_op(&self) -> (usize, BNLowLevelILOperation) {
- use self::FlagWriteOp::*;
+ use self::LowLevelILFlagWriteOp::*;
use binaryninjacore_sys::BNLowLevelILOperation::*;
match *self {
@@ -293,10 +395,10 @@ impl<R: ArchReg> FlagWriteOp<R> {
}
}
- pub(crate) fn api_operands(&self) -> (usize, [BNRegisterOrConstant; 5]) {
- use self::FlagWriteOp::*;
+ pub(crate) fn raw_operands(&self) -> (usize, [BNRegisterOrConstant; 5]) {
+ use self::LowLevelILFlagWriteOp::*;
- let mut operands: [BNRegisterOrConstant; 5] = unsafe { mem::zeroed() };
+ let mut operands: [BNRegisterOrConstant; 5] = [BNRegisterOrConstant::default(); 5];
let count = match *self {
Pop(_) => 0,
@@ -311,7 +413,7 @@ impl<R: ArchReg> FlagWriteOp<R> {
| LowPart(_, op0)
| BoolToInt(_, op0)
| FloatToInt(_, op0) => {
- operands[0] = op0.into_api();
+ operands[0] = op0.into();
1
}
@@ -340,8 +442,8 @@ impl<R: ArchReg> FlagWriteOp<R> {
| ModsDp(_, op0, op1)
| TestBit(_, op0, op1)
| AddOverflow(_, op0, op1) => {
- operands[0] = op0.into_api();
- operands[1] = op1.into_api();
+ operands[0] = op0.into();
+ operands[1] = op1.into();
2
}
@@ -349,9 +451,9 @@ impl<R: ArchReg> FlagWriteOp<R> {
| Sbb(_, op0, op1, op2)
| Rlc(_, op0, op1, op2)
| Rrc(_, op0, op1, op2) => {
- operands[0] = op0.into_api();
- operands[1] = op1.into_api();
- operands[2] = op2.into_api();
+ operands[0] = op0.into();
+ operands[1] = op1.into();
+ operands[2] = op2.into();
3
}
};
@@ -363,19 +465,19 @@ impl<R: ArchReg> FlagWriteOp<R> {
pub fn get_default_flag_write_llil<'func, A>(
arch: &A,
role: FlagRole,
- op: FlagWriteOp<A::Register>,
- il: &'func Lifter<A>,
-) -> LiftedExpr<'func, A>
+ op: LowLevelILFlagWriteOp<A::Register>,
+ il: &'func MutableLiftedILFunction<A>,
+) -> MutableLiftedILExpr<'func, A, ValueExpr>
where
A: 'func + Architecture,
{
let (size, operation) = op.size_and_op();
- let (count, operands) = op.api_operands();
+ let (count, operands) = op.raw_operands();
let expr_idx = unsafe {
use binaryninjacore_sys::BNGetDefaultArchitectureFlagWriteLowLevelIL;
BNGetDefaultArchitectureFlagWriteLowLevelIL(
- arch.as_ref().0,
+ arch.as_ref().handle,
operation,
size,
role,
@@ -385,46 +487,48 @@ where
)
};
- Expression::new(il, expr_idx)
+ LowLevelILExpression::new(il, LowLevelExpressionIndex(expr_idx))
}
pub fn get_default_flag_cond_llil<'func, A>(
arch: &A,
cond: FlagCondition,
class: Option<A::FlagClass>,
- il: &'func Lifter<A>,
-) -> LiftedExpr<'func, A>
+ il: &'func MutableLiftedILFunction<A>,
+) -> MutableLiftedILExpr<'func, A, ValueExpr>
where
A: 'func + Architecture,
{
use binaryninjacore_sys::BNGetDefaultArchitectureFlagConditionLowLevelIL;
-
- let handle = arch.as_ref();
- let class_id = class.map(|c| c.id()).unwrap_or(0);
+ let class_id = class.map(|c| c.id().0).unwrap_or(0);
unsafe {
- let expr_idx =
- BNGetDefaultArchitectureFlagConditionLowLevelIL(handle.0, cond, class_id, il.handle);
+ let expr_idx = BNGetDefaultArchitectureFlagConditionLowLevelIL(
+ arch.as_ref().handle,
+ cond,
+ class_id,
+ il.handle,
+ );
- Expression::new(il, expr_idx)
+ LowLevelILExpression::new(il, LowLevelExpressionIndex(expr_idx))
}
}
macro_rules! prim_int_lifter {
($x:ty) => {
- impl<'a, A: 'a + Architecture> Liftable<'a, A> for $x {
+ impl<'a, A: 'a + Architecture> LiftableLowLevelIL<'a, A> for $x {
type Result = ValueExpr;
- fn lift(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, val: Self)
- -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, Self::Result>
+ fn lift(il: &'a MutableLiftedILFunction<A>, val: Self)
+ -> MutableLiftedILExpr<'a, A, Self::Result>
{
- il.const_int(mem::size_of::<Self>(), val as i64 as u64)
+ il.const_int(std::mem::size_of::<Self>(), val as i64 as u64)
}
}
- impl<'a, A: 'a + Architecture> LiftableWithSize<'a, A> for $x {
- fn lift_with_size(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, val: Self, size: usize)
- -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr>
+ impl<'a, A: 'a + Architecture> LiftableLowLevelILWithSize<'a, A> for $x {
+ fn lift_with_size(il: &'a MutableLiftedILFunction<A>, val: Self, size: usize)
+ -> MutableLiftedILExpr<'a, A, ValueExpr>
{
let raw = val as i64;
@@ -436,7 +540,7 @@ macro_rules! prim_int_lifter {
};
if !is_safe {
- error!("il @ {:x} attempted to lift constant 0x{:x} as {} byte expr (won't fit!)",
+ log::error!("il @ {:x} attempted to lift constant 0x{:x} as {} byte expr (won't fit!)",
il.current_address(), val, size);
}
}
@@ -457,74 +561,89 @@ prim_int_lifter!(u16);
prim_int_lifter!(u32);
prim_int_lifter!(u64);
-impl<'a, R: ArchReg, A: 'a + Architecture> Liftable<'a, A> for Register<R>
+impl<'a, R: ArchReg, A: 'a + Architecture> LiftableLowLevelIL<'a, A> for LowLevelILRegister<R>
where
- R: Liftable<'a, A, Result = ValueExpr> + Into<Register<R>>,
+ R: LiftableLowLevelIL<'a, A, Result = ValueExpr> + Into<LowLevelILRegister<R>>,
{
type Result = ValueExpr;
fn lift(
- il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>,
+ il: &'a MutableLiftedILFunction<A>,
reg: Self,
- ) -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, Self::Result> {
+ ) -> MutableLiftedILExpr<'a, A, Self::Result> {
match reg {
- Register::ArchReg(r) => R::lift(il, r),
- Register::Temp(t) => il.reg(il.arch().default_integer_size(), Register::Temp(t)),
+ LowLevelILRegister::ArchReg(r) => R::lift(il, r),
+ LowLevelILRegister::Temp(t) => il.reg(
+ il.arch().default_integer_size(),
+ LowLevelILRegister::Temp(t),
+ ),
}
}
}
-impl<'a, R: ArchReg, A: 'a + Architecture> LiftableWithSize<'a, A> for Register<R>
+impl<'a, R: ArchReg, A: 'a + Architecture> LiftableLowLevelILWithSize<'a, A>
+ for LowLevelILRegister<R>
where
- R: LiftableWithSize<'a, A> + Into<Register<R>>,
+ R: LiftableLowLevelILWithSize<'a, A> + Into<LowLevelILRegister<R>>,
{
fn lift_with_size(
- il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>,
+ il: &'a MutableLiftedILFunction<A>,
reg: Self,
size: usize,
- ) -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> {
+ ) -> MutableLiftedILExpr<'a, A, ValueExpr> {
match reg {
- Register::ArchReg(r) => R::lift_with_size(il, r, size),
- Register::Temp(t) => il.reg(size, Register::Temp(t)),
+ LowLevelILRegister::ArchReg(r) => R::lift_with_size(il, r, size),
+ LowLevelILRegister::Temp(t) => il.reg(size, LowLevelILRegister::Temp(t)),
}
}
}
-impl<'a, R: ArchReg, A: 'a + Architecture> Liftable<'a, A> for RegisterOrConstant<R>
+impl<'a, R: ArchReg, A: 'a + Architecture> LiftableLowLevelIL<'a, A>
+ for LowLevelILRegisterOrConstant<R>
where
- R: LiftableWithSize<'a, A, Result = ValueExpr> + Into<Register<R>>,
+ R: LiftableLowLevelILWithSize<'a, A, Result = ValueExpr> + Into<LowLevelILRegister<R>>,
{
type Result = ValueExpr;
fn lift(
- il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>,
+ il: &'a MutableLiftedILFunction<A>,
reg: Self,
- ) -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, Self::Result> {
+ ) -> MutableLiftedILExpr<'a, A, Self::Result> {
match reg {
- RegisterOrConstant::Register(size, r) => Register::<R>::lift_with_size(il, r, size),
- RegisterOrConstant::Constant(size, value) => u64::lift_with_size(il, value, size),
+ LowLevelILRegisterOrConstant::Register(size, r) => {
+ LowLevelILRegister::<R>::lift_with_size(il, r, size)
+ }
+ LowLevelILRegisterOrConstant::Constant(size, value) => {
+ u64::lift_with_size(il, value, size)
+ }
}
}
}
-impl<'a, R: ArchReg, A: 'a + Architecture> LiftableWithSize<'a, A> for RegisterOrConstant<R>
+impl<'a, R: ArchReg, A: 'a + Architecture> LiftableLowLevelILWithSize<'a, A>
+ for LowLevelILRegisterOrConstant<R>
where
- R: LiftableWithSize<'a, A> + Into<Register<R>>,
+ R: LiftableLowLevelILWithSize<'a, A> + Into<LowLevelILRegister<R>>,
{
fn lift_with_size(
- il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>,
+ il: &'a MutableLiftedILFunction<A>,
reg: Self,
size: usize,
- ) -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> {
+ ) -> MutableLiftedILExpr<'a, A, ValueExpr> {
// TODO ensure requested size is compatible with size of this constant
match reg {
- RegisterOrConstant::Register(_, r) => Register::<R>::lift_with_size(il, r, size),
- RegisterOrConstant::Constant(_, value) => u64::lift_with_size(il, value, size),
+ LowLevelILRegisterOrConstant::Register(_, r) => {
+ LowLevelILRegister::<R>::lift_with_size(il, r, size)
+ }
+ LowLevelILRegisterOrConstant::Constant(_, value) => {
+ u64::lift_with_size(il, value, size)
+ }
}
}
}
-impl<'a, A, R> Liftable<'a, A> for Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R>
+impl<'a, A, R> LiftableLowLevelIL<'a, A>
+ for LowLevelILExpression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R>
where
A: 'a + Architecture,
R: ExpressionResultType,
@@ -532,27 +651,28 @@ where
type Result = R;
fn lift(
- il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>,
+ il: &'a MutableLiftedILFunction<A>,
expr: Self,
- ) -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, Self::Result> {
+ ) -> MutableLiftedILExpr<'a, A, Self::Result> {
debug_assert!(expr.function.handle == il.handle);
expr
}
}
-impl<'a, A: 'a + Architecture> LiftableWithSize<'a, A>
- for Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr>
+impl<'a, A: 'a + Architecture> LiftableLowLevelILWithSize<'a, A>
+ for LowLevelILExpression<'a, A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr>
{
fn lift_with_size(
- il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>,
+ il: &'a MutableLiftedILFunction<A>,
expr: Self,
_size: usize,
- ) -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, Self::Result> {
+ ) -> MutableLiftedILExpr<'a, A, Self::Result> {
#[cfg(debug_assertions)]
{
- if let Some(expr_size) = expr.info().size() {
+ use crate::low_level_il::ExpressionHandler;
+ if let Some(expr_size) = expr.kind().size() {
if expr_size != _size {
- warn!(
+ log::warn!(
"il @ {:x} attempted to lift {} byte expression as {} bytes",
il.current_address(),
expr_size,
@@ -562,39 +682,35 @@ impl<'a, A: 'a + Architecture> LiftableWithSize<'a, A>
}
}
- Liftable::lift(il, expr)
+ LiftableLowLevelIL::lift(il, expr)
}
}
-impl<'func, A, R> Expression<'func, A, Mutable, NonSSA<LiftedNonSSA>, R>
+impl<'func, A, R> LowLevelILExpression<'func, A, Mutable, NonSSA<LiftedNonSSA>, R>
where
A: 'func + Architecture,
R: ExpressionResultType,
{
pub fn with_source_operand(self, op: u32) -> Self {
use binaryninjacore_sys::BNLowLevelILSetExprSourceOperand;
-
- unsafe { BNLowLevelILSetExprSourceOperand(self.function.handle, self.expr_idx, op) }
-
+ unsafe { BNLowLevelILSetExprSourceOperand(self.function.handle, self.index.0, op) }
self
}
pub fn append(self) {
- let il = self.function;
- il.instruction(self);
+ self.function.add_instruction(self);
}
}
-use binaryninjacore_sys::BNLowLevelILOperation;
pub struct ExpressionBuilder<'func, A, R>
where
A: 'func + Architecture,
R: ExpressionResultType,
{
- function: &'func Function<A, Mutable, NonSSA<LiftedNonSSA>>,
+ function: &'func LowLevelILFunction<A, Mutable, NonSSA<LiftedNonSSA>>,
op: BNLowLevelILOperation,
size: usize,
- flags: u32,
+ flag_write: FlagWriteId,
op1: u64,
op2: u64,
op3: u64,
@@ -607,33 +723,31 @@ where
A: 'a + Architecture,
R: ExpressionResultType,
{
- pub fn from_expr(expr: Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R>) -> Self {
+ pub fn from_expr(expr: LowLevelILExpression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R>) -> Self {
use binaryninjacore_sys::BNGetLowLevelILByIndex;
- let instr = unsafe {
- BNGetLowLevelILByIndex(expr.function.handle, expr.expr_idx)
- };
+ let instr = unsafe { BNGetLowLevelILByIndex(expr.function.handle, expr.index.0) };
ExpressionBuilder {
function: expr.function,
op: instr.operation,
size: instr.size,
- flags: instr.flags,
+ flag_write: FlagWriteId(instr.flags),
op1: instr.operands[0],
op2: instr.operands[1],
op3: instr.operands[2],
op4: instr.operands[3],
- _ty: PhantomData
+ _ty: PhantomData,
}
}
pub fn with_flag_write(mut self, flag_write: A::FlagWrite) -> Self {
// TODO verify valid id
- self.flags = flag_write.id();
+ self.flag_write = flag_write.id();
self
}
- pub fn build(self) -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R> {
+ pub fn build(self) -> LowLevelILExpression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R> {
use binaryninjacore_sys::BNLowLevelILAddExpr;
let expr_idx = unsafe {
@@ -641,7 +755,7 @@ where
self.function.handle,
self.op,
self.size,
- self.flags,
+ self.flag_write.0,
self.op1,
self.op2,
self.op3,
@@ -649,25 +763,23 @@ where
)
};
- Expression::new(self.function, expr_idx)
+ LowLevelILExpression::new(self.function, LowLevelExpressionIndex(expr_idx))
}
pub fn with_source_operand(
self,
op: u32,
- ) -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R> {
+ ) -> LowLevelILExpression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R> {
self.build().with_source_operand(op)
}
pub fn append(self) {
let expr = self.build();
- let il = expr.function;
-
- il.instruction(expr);
+ expr.function.add_instruction(expr);
}
}
-impl<'a, A, R> Liftable<'a, A> for ExpressionBuilder<'a, A, R>
+impl<'a, A, R> LiftableLowLevelIL<'a, A> for ExpressionBuilder<'a, A, R>
where
A: 'a + Architecture,
R: ExpressionResultType,
@@ -675,30 +787,30 @@ where
type Result = R;
fn lift(
- il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>,
+ il: &'a MutableLiftedILFunction<A>,
expr: Self,
- ) -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, Self::Result> {
+ ) -> MutableLiftedILExpr<'a, A, Self::Result> {
debug_assert!(expr.function.handle == il.handle);
expr.build()
}
}
-impl<'a, A> LiftableWithSize<'a, A> for ExpressionBuilder<'a, A, ValueExpr>
+impl<'a, A> LiftableLowLevelILWithSize<'a, A> for ExpressionBuilder<'a, A, ValueExpr>
where
A: 'a + Architecture,
{
fn lift_with_size(
- il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>,
+ il: &'a MutableLiftedILFunction<A>,
expr: Self,
_size: usize,
- ) -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> {
+ ) -> MutableLiftedILExpr<'a, A, ValueExpr> {
#[cfg(debug_assertions)]
{
use binaryninjacore_sys::BNLowLevelILOperation::{LLIL_UNIMPL, LLIL_UNIMPL_MEM};
if expr.size != _size && ![LLIL_UNIMPL, LLIL_UNIMPL_MEM].contains(&expr.op) {
- warn!(
+ log::warn!(
"il @ {:x} attempted to lift {} byte expression builder as {} bytes",
il.current_address(),
expr.size,
@@ -707,19 +819,19 @@ where
}
}
- Liftable::lift(il, expr)
+ LiftableLowLevelIL::lift(il, expr)
}
}
macro_rules! no_arg_lifter {
($name:ident, $op:ident, $result:ty) => {
- pub fn $name(&self) -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, $result> {
+ pub fn $name(&self) -> LowLevelILExpression<A, Mutable, NonSSA<LiftedNonSSA>, $result> {
use binaryninjacore_sys::BNLowLevelILAddExpr;
use binaryninjacore_sys::BNLowLevelILOperation::$op;
let expr_idx = unsafe { BNLowLevelILAddExpr(self.handle, $op, 0, 0, 0, 0, 0, 0) };
- Expression::new(self, expr_idx)
+ LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx))
}
};
}
@@ -733,7 +845,7 @@ macro_rules! sized_no_arg_lifter {
function: self,
op: $op,
size,
- flags: 0,
+ flag_write: FlagWriteId(0),
op1: 0,
op2: 0,
op3: 0,
@@ -749,9 +861,9 @@ macro_rules! unsized_unary_op_lifter {
pub fn $name<'a, E>(
&'a self,
expr: E,
- ) -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, $result>
+ ) -> LowLevelILExpression<'a, A, Mutable, NonSSA<LiftedNonSSA>, $result>
where
- E: Liftable<'a, A, Result = ValueExpr>,
+ E: LiftableLowLevelIL<'a, A, Result = ValueExpr>,
{
use binaryninjacore_sys::BNLowLevelILAddExpr;
use binaryninjacore_sys::BNLowLevelILOperation::$op;
@@ -759,10 +871,10 @@ macro_rules! unsized_unary_op_lifter {
let expr = E::lift(self, expr);
let expr_idx = unsafe {
- BNLowLevelILAddExpr(self.handle, $op, 0, 0, expr.expr_idx as u64, 0, 0, 0)
+ BNLowLevelILAddExpr(self.handle, $op, 0, 0, expr.index.0 as u64, 0, 0, 0)
};
- Expression::new(self, expr_idx)
+ LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx))
}
};
}
@@ -771,7 +883,7 @@ macro_rules! sized_unary_op_lifter {
($name:ident, $op:ident, $result:ty) => {
pub fn $name<'a, E>(&'a self, size: usize, expr: E) -> ExpressionBuilder<'a, A, $result>
where
- E: LiftableWithSize<'a, A>,
+ E: LiftableLowLevelILWithSize<'a, A>,
{
use binaryninjacore_sys::BNLowLevelILOperation::$op;
@@ -781,8 +893,8 @@ macro_rules! sized_unary_op_lifter {
function: self,
op: $op,
size,
- flags: 0,
- op1: expr.expr_idx as u64,
+ flag_write: FlagWriteId(0),
+ op1: expr.index.0 as u64,
op2: 0,
op3: 0,
op4: 0,
@@ -796,7 +908,7 @@ macro_rules! size_changing_unary_op_lifter {
($name:ident, $op:ident, $result:ty) => {
pub fn $name<'a, E>(&'a self, size: usize, expr: E) -> ExpressionBuilder<'a, A, $result>
where
- E: LiftableWithSize<'a, A>,
+ E: LiftableLowLevelILWithSize<'a, A>,
{
use binaryninjacore_sys::BNLowLevelILOperation::$op;
@@ -806,8 +918,8 @@ macro_rules! size_changing_unary_op_lifter {
function: self,
op: $op,
size,
- flags: 0,
- op1: expr.expr_idx as u64,
+ flag_write: FlagWriteId(0),
+ op1: expr.index.0 as u64,
op2: 0,
op3: 0,
op4: 0,
@@ -826,8 +938,8 @@ macro_rules! binary_op_lifter {
right: R,
) -> ExpressionBuilder<'a, A, ValueExpr>
where
- L: LiftableWithSize<'a, A>,
- R: LiftableWithSize<'a, A>,
+ L: LiftableLowLevelILWithSize<'a, A>,
+ R: LiftableLowLevelILWithSize<'a, A>,
{
use binaryninjacore_sys::BNLowLevelILOperation::$op;
@@ -838,9 +950,9 @@ macro_rules! binary_op_lifter {
function: self,
op: $op,
size,
- flags: 0,
- op1: left.expr_idx as u64,
- op2: right.expr_idx as u64,
+ flag_write: FlagWriteId(0),
+ op1: left.index.0 as u64,
+ op2: right.index.0 as u64,
op3: 0,
op4: 0,
_ty: PhantomData,
@@ -859,9 +971,9 @@ macro_rules! binary_op_carry_lifter {
carry: C,
) -> ExpressionBuilder<'a, A, ValueExpr>
where
- L: LiftableWithSize<'a, A>,
- R: LiftableWithSize<'a, A>,
- C: LiftableWithSize<'a, A>,
+ L: LiftableLowLevelILWithSize<'a, A>,
+ R: LiftableLowLevelILWithSize<'a, A>,
+ C: LiftableLowLevelILWithSize<'a, A>,
{
use binaryninjacore_sys::BNLowLevelILOperation::$op;
@@ -873,10 +985,10 @@ macro_rules! binary_op_carry_lifter {
function: self,
op: $op,
size,
- flags: 0,
- op1: left.expr_idx as u64,
- op2: right.expr_idx as u64,
- op3: carry.expr_idx as u64,
+ flag_write: FlagWriteId(0),
+ op1: left.index.0 as u64,
+ op2: right.index.0 as u64,
+ op3: carry.index.0 as u64,
op4: 0,
_ty: PhantomData,
}
@@ -884,87 +996,89 @@ macro_rules! binary_op_carry_lifter {
};
}
-impl<A> Function<A, Mutable, NonSSA<LiftedNonSSA>>
+impl<A> LowLevelILFunction<A, Mutable, NonSSA<LiftedNonSSA>>
where
A: Architecture,
{
pub const NO_INPUTS: [ExpressionBuilder<'static, A, ValueExpr>; 0] = [];
- pub const NO_OUTPUTS: [Register<A::Register>; 0] = [];
+ pub const NO_OUTPUTS: [LowLevelILRegister<A::Register>; 0] = [];
- pub fn expression<'a, E: Liftable<'a, A>>(
+ pub fn expression<'a, E: LiftableLowLevelIL<'a, A>>(
&'a self,
expr: E,
- ) -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, E::Result> {
+ ) -> LowLevelILExpression<'a, A, Mutable, NonSSA<LiftedNonSSA>, E::Result> {
E::lift(self, expr)
}
- pub fn instruction<'a, E: Liftable<'a, A>>(&'a self, expr: E) {
+ pub fn add_instruction<'a, E: LiftableLowLevelIL<'a, A>>(&'a self, expr: E) {
let expr = self.expression(expr);
unsafe {
use binaryninjacore_sys::BNLowLevelILAddInstruction;
- BNLowLevelILAddInstruction(self.handle, expr.expr_idx);
+ BNLowLevelILAddInstruction(self.handle, expr.index.0);
}
}
- pub unsafe fn replace_expression<'a, E: Liftable<'a, A>>(
+ pub unsafe fn replace_expression<'a, E: LiftableLowLevelIL<'a, A>>(
&'a self,
- replaced_expr_index: usize,
+ replaced_expr_index: LowLevelExpressionIndex,
replacement: E,
- ) {
- use binaryninjacore_sys::BNGetLowLevelILExprCount;
+ ) -> bool {
use binaryninjacore_sys::BNReplaceLowLevelILExpr;
-
- if replaced_expr_index >= BNGetLowLevelILExprCount(self.handle) {
- panic!(
- "bad expr idx used: {} exceeds function bounds",
- replaced_expr_index
- );
+ if replaced_expr_index.0 >= self.expression_count() {
+ // Invalid expression index, cant replace expression.
+ return false;
}
-
let expr = self.expression(replacement);
- BNReplaceLowLevelILExpr(self.handle, replaced_expr_index, expr.expr_idx);
+ BNReplaceLowLevelILExpr(self.handle, replaced_expr_index.0, expr.index.0);
+ true
}
pub fn const_int(
&self,
size: usize,
val: u64,
- ) -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> {
+ ) -> LowLevelILExpression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> {
use binaryninjacore_sys::BNLowLevelILAddExpr;
use binaryninjacore_sys::BNLowLevelILOperation::LLIL_CONST;
let expr_idx =
unsafe { BNLowLevelILAddExpr(self.handle, LLIL_CONST, size, 0, val, 0, 0, 0) };
- Expression::new(self, expr_idx)
+ LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx))
}
pub fn const_ptr_sized(
&self,
size: usize,
val: u64,
- ) -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> {
+ ) -> LowLevelILExpression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> {
use binaryninjacore_sys::BNLowLevelILAddExpr;
use binaryninjacore_sys::BNLowLevelILOperation::LLIL_CONST_PTR;
let expr_idx =
unsafe { BNLowLevelILAddExpr(self.handle, LLIL_CONST_PTR, size, 0, val, 0, 0, 0) };
- Expression::new(self, expr_idx)
+ LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx))
}
- pub fn const_ptr(&self, val: u64) -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> {
+ pub fn const_ptr(
+ &self,
+ val: u64,
+ ) -> LowLevelILExpression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> {
self.const_ptr_sized(self.arch().address_size(), val)
}
- pub fn trap(&self, val: u64) -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, VoidExpr> {
+ pub fn trap(
+ &self,
+ val: u64,
+ ) -> LowLevelILExpression<A, Mutable, NonSSA<LiftedNonSSA>, VoidExpr> {
use binaryninjacore_sys::BNLowLevelILAddExpr;
use binaryninjacore_sys::BNLowLevelILOperation::LLIL_TRAP;
let expr_idx = unsafe { BNLowLevelILAddExpr(self.handle, LLIL_TRAP, 0, 0, val, 0, 0, 0) };
- Expression::new(self, expr_idx)
+ LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx))
}
no_arg_lifter!(unimplemented, LLIL_UNIMPL, ValueExpr);
@@ -978,84 +1092,102 @@ where
unsized_unary_op_lifter!(call, LLIL_CALL, VoidExpr);
unsized_unary_op_lifter!(ret, LLIL_RET, VoidExpr);
unsized_unary_op_lifter!(jump, LLIL_JUMP, VoidExpr);
- // JumpTo TODO
+ // TODO: LLIL_JUMP_TO
pub fn if_expr<'a: 'b, 'b, C>(
&'a self,
cond: C,
- t: &'b Label,
- f: &'b Label,
- ) -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, VoidExpr>
+ true_label: &'b mut LowLevelILLabel,
+ false_label: &'b mut LowLevelILLabel,
+ ) -> LowLevelILExpression<'a, A, Mutable, NonSSA<LiftedNonSSA>, VoidExpr>
where
- C: Liftable<'b, A, Result = ValueExpr>,
+ C: LiftableLowLevelIL<'b, A, Result = ValueExpr>,
{
use binaryninjacore_sys::BNLowLevelILIf;
let cond = C::lift(self, cond);
+ let mut raw_true_label = BNLowLevelILLabel::from(*true_label);
+ let mut raw_false_label = BNLowLevelILLabel::from(*false_label);
let expr_idx = unsafe {
BNLowLevelILIf(
self.handle,
- cond.expr_idx as u64,
- &t.0 as *const _ as *mut _,
- &f.0 as *const _ as *mut _,
+ cond.index.0 as u64,
+ &mut raw_true_label,
+ &mut raw_false_label,
)
};
- Expression::new(self, expr_idx)
+ // Update the labels after they have been resolved.
+ let mut new_true_label = LowLevelILLabel::from(raw_true_label);
+ let mut new_false_label = LowLevelILLabel::from(raw_false_label);
+ if let Some(location) = true_label.location {
+ new_true_label.location = Some(location);
+ self.update_label_map_for_label(&new_true_label);
+ }
+ if let Some(location) = false_label.location {
+ new_false_label.location = Some(location);
+ self.update_label_map_for_label(&new_false_label);
+ }
+ *true_label = new_true_label;
+ *false_label = new_false_label;
+
+ LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx))
}
+ // TODO: Wtf are these lifetimes??
pub fn goto<'a: 'b, 'b>(
&'a self,
- l: &'b Label,
- ) -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, VoidExpr> {
+ label: &'b mut LowLevelILLabel,
+ ) -> LowLevelILExpression<'a, A, Mutable, NonSSA<LiftedNonSSA>, VoidExpr> {
use binaryninjacore_sys::BNLowLevelILGoto;
- let expr_idx = unsafe { BNLowLevelILGoto(self.handle, &l.0 as *const _ as *mut _) };
+ let mut raw_label = BNLowLevelILLabel::from(*label);
+ let expr_idx = unsafe { BNLowLevelILGoto(self.handle, &mut raw_label) };
+
+ // Update the labels after they have been resolved.
+ let mut new_label = LowLevelILLabel::from(raw_label);
+ if let Some(location) = label.location {
+ new_label.location = Some(location);
+ self.update_label_map_for_label(&new_label);
+ }
+ *label = new_label;
- Expression::new(self, expr_idx)
+ LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx))
}
- pub fn reg<R: Into<Register<A::Register>>>(
+ pub fn reg<R: Into<LowLevelILRegister<A::Register>>>(
&self,
size: usize,
reg: R,
- ) -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> {
+ ) -> LowLevelILExpression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> {
use binaryninjacore_sys::BNLowLevelILAddExpr;
use binaryninjacore_sys::BNLowLevelILOperation::LLIL_REG;
// TODO verify valid id
- let reg = match reg.into() {
- Register::ArchReg(r) => r.id(),
- Register::Temp(r) => 0x8000_0000 | r,
- };
+ let reg = reg.into().id();
let expr_idx =
- unsafe { BNLowLevelILAddExpr(self.handle, LLIL_REG, size, 0, reg as u64, 0, 0, 0) };
+ unsafe { BNLowLevelILAddExpr(self.handle, LLIL_REG, size, 0, reg.0 as u64, 0, 0, 0) };
- Expression::new(self, expr_idx)
+ LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx))
}
- pub fn reg_split<H: Into<Register<A::Register>>, L: Into<Register<A::Register>>>(
+ pub fn reg_split<
+ H: Into<LowLevelILRegister<A::Register>>,
+ L: Into<LowLevelILRegister<A::Register>>,
+ >(
&self,
size: usize,
hi_reg: H,
lo_reg: L,
- ) -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> {
+ ) -> LowLevelILExpression<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 hi_reg = hi_reg.into().id();
+ let lo_reg = lo_reg.into().id();
let expr_idx = unsafe {
BNLowLevelILAddExpr(
@@ -1063,14 +1195,14 @@ where
LLIL_REG_SPLIT,
size,
0,
- hi_reg as u64,
- lo_reg as u64,
+ hi_reg.0 as u64,
+ lo_reg.0 as u64,
0,
0,
)
};
- Expression::new(self, expr_idx)
+ LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx))
}
pub fn set_reg<'a, R, E>(
@@ -1080,16 +1212,13 @@ where
expr: E,
) -> ExpressionBuilder<'a, A, VoidExpr>
where
- R: Into<Register<A::Register>>,
- E: LiftableWithSize<'a, A>,
+ R: Into<LowLevelILRegister<A::Register>>,
+ E: LiftableLowLevelILWithSize<'a, A>,
{
use binaryninjacore_sys::BNLowLevelILOperation::LLIL_SET_REG;
// TODO verify valid id
- let dest_reg = match dest_reg.into() {
- Register::ArchReg(r) => r.id(),
- Register::Temp(r) => 0x8000_0000 | r,
- };
+ let dest_reg = dest_reg.into().id();
let expr = E::lift_with_size(self, expr, size);
@@ -1097,9 +1226,10 @@ where
function: self,
op: LLIL_SET_REG,
size,
- flags: 0,
- op1: dest_reg as u64,
- op2: expr.expr_idx as u64,
+ // TODO: Make these optional?
+ flag_write: FlagWriteId(0),
+ op1: dest_reg.0 as u64,
+ op2: expr.index.0 as u64,
op3: 0,
op4: 0,
_ty: PhantomData,
@@ -1114,23 +1244,15 @@ where
expr: E,
) -> ExpressionBuilder<'a, A, VoidExpr>
where
- H: Into<Register<A::Register>>,
- L: Into<Register<A::Register>>,
- E: LiftableWithSize<'a, A>,
+ H: Into<LowLevelILRegister<A::Register>>,
+ L: Into<LowLevelILRegister<A::Register>>,
+ E: LiftableLowLevelILWithSize<'a, A>,
{
use binaryninjacore_sys::BNLowLevelILOperation::LLIL_SET_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 hi_reg = hi_reg.into().id();
+ let lo_reg = lo_reg.into().id();
let expr = E::lift_with_size(self, expr, size);
@@ -1138,30 +1260,35 @@ where
function: self,
op: LLIL_SET_REG_SPLIT,
size,
- flags: 0,
- op1: hi_reg as u64,
- op2: lo_reg as u64,
- op3: expr.expr_idx as u64,
+ // TODO: Make these optional?
+ flag_write: FlagWriteId(0),
+ op1: hi_reg.0 as u64,
+ op2: lo_reg.0 as u64,
+ op3: expr.index.0 as u64,
op4: 0,
_ty: PhantomData,
}
}
- pub fn flag(&self, flag: A::Flag) -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> {
+ pub fn flag(
+ &self,
+ flag: A::Flag,
+ ) -> LowLevelILExpression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> {
use binaryninjacore_sys::BNLowLevelILAddExpr;
use binaryninjacore_sys::BNLowLevelILOperation::LLIL_FLAG;
// TODO verify valid id
- let expr_idx =
- unsafe { BNLowLevelILAddExpr(self.handle, LLIL_FLAG, 0, 0, flag.id() as u64, 0, 0, 0) };
+ let expr_idx = unsafe {
+ BNLowLevelILAddExpr(self.handle, LLIL_FLAG, 0, 0, flag.id().0 as u64, 0, 0, 0)
+ };
- Expression::new(self, expr_idx)
+ LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx))
}
pub fn flag_cond(
&self,
cond: FlagCondition,
- ) -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> {
+ ) -> LowLevelILExpression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> {
use binaryninjacore_sys::BNLowLevelILAddExpr;
use binaryninjacore_sys::BNLowLevelILOperation::LLIL_FLAG_COND;
@@ -1169,13 +1296,13 @@ where
let expr_idx =
unsafe { BNLowLevelILAddExpr(self.handle, LLIL_FLAG_COND, 0, 0, cond as u64, 0, 0, 0) };
- Expression::new(self, expr_idx)
+ LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx))
}
pub fn flag_group(
&self,
group: A::FlagGroup,
- ) -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> {
+ ) -> LowLevelILExpression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr> {
use binaryninjacore_sys::BNLowLevelILAddExpr;
use binaryninjacore_sys::BNLowLevelILOperation::LLIL_FLAG_GROUP;
@@ -1186,14 +1313,14 @@ where
LLIL_FLAG_GROUP,
0,
0,
- group.id() as u64,
+ group.id().0 as u64,
0,
0,
0,
)
};
- Expression::new(self, expr_idx)
+ LowLevelILExpression::new(self, LowLevelExpressionIndex(expr_idx))
}
pub fn set_flag<'a, E>(
@@ -1202,7 +1329,7 @@ where
expr: E,
) -> ExpressionBuilder<'a, A, VoidExpr>
where
- E: LiftableWithSize<'a, A>,
+ E: LiftableLowLevelILWithSize<'a, A>,
{
use binaryninjacore_sys::BNLowLevelILOperation::LLIL_SET_FLAG;
@@ -1214,9 +1341,9 @@ where
function: self,
op: LLIL_SET_FLAG,
size: 0,
- flags: 0,
- op1: dest_flag.id() as u64,
- op2: expr.expr_idx as u64,
+ flag_write: FlagWriteId(0),
+ op1: dest_flag.id().0 as u64,
+ op2: expr.index.0 as u64,
op3: 0,
op4: 0,
_ty: PhantomData,
@@ -1230,7 +1357,7 @@ where
pub fn load<'a, E>(&'a self, size: usize, source_mem: E) -> ExpressionBuilder<'a, A, ValueExpr>
where
- E: Liftable<'a, A, Result = ValueExpr>,
+ E: LiftableLowLevelIL<'a, A, Result = ValueExpr>,
{
use binaryninjacore_sys::BNLowLevelILOperation::LLIL_LOAD;
@@ -1240,8 +1367,8 @@ where
function: self,
op: LLIL_LOAD,
size,
- flags: 0,
- op1: expr.expr_idx as u64,
+ flag_write: FlagWriteId(0),
+ op1: expr.index.0 as u64,
op2: 0,
op3: 0,
op4: 0,
@@ -1256,8 +1383,8 @@ where
value: V,
) -> ExpressionBuilder<'a, A, VoidExpr>
where
- D: Liftable<'a, A, Result = ValueExpr>,
- V: LiftableWithSize<'a, A>,
+ D: LiftableLowLevelIL<'a, A, Result = ValueExpr>,
+ V: LiftableLowLevelILWithSize<'a, A>,
{
use binaryninjacore_sys::BNLowLevelILOperation::LLIL_STORE;
@@ -1268,9 +1395,9 @@ where
function: self,
op: LLIL_STORE,
size,
- flags: 0,
- op1: dest_mem.expr_idx as u64,
- op2: value.expr_idx as u64,
+ flag_write: FlagWriteId(0),
+ op1: dest_mem.index.0 as u64,
+ op2: value.index.0 as u64,
op3: 0,
op4: 0,
_ty: PhantomData,
@@ -1284,10 +1411,10 @@ where
inputs: PL,
) -> ExpressionBuilder<'a, A, VoidExpr>
where
- O: Into<Register<A::Register>>,
+ O: Into<LowLevelILRegister<A::Register>>,
OL: IntoIterator<Item = O>,
I: Into<A::Intrinsic>,
- P: Liftable<'a, A, Result = ValueExpr>,
+ P: LiftableLowLevelIL<'a, A, Result = ValueExpr>,
PL: IntoIterator<Item = P>,
{
use binaryninjacore_sys::BNLowLevelILOperation::{LLIL_CALL_PARAM, LLIL_INTRINSIC};
@@ -1295,14 +1422,7 @@ where
let mut outputs: Vec<u64> = outputs
.into_iter()
- .map(|output| {
- // TODO verify valid id
- let output = match output.into() {
- Register::ArchReg(r) => r.id(),
- Register::Temp(r) => 0x8000_0000 | r,
- };
- output as u64
- })
+ .map(|output| output.into().id().0 as u64)
.collect();
let output_expr_idx =
unsafe { BNLowLevelILAddOperandList(self.handle, outputs.as_mut_ptr(), outputs.len()) };
@@ -1313,7 +1433,7 @@ where
.into_iter()
.map(|input| {
let input = P::lift(self, input);
- input.expr_idx as u64
+ input.index.0 as u64
})
.collect();
let input_list_expr_idx =
@@ -1335,10 +1455,10 @@ where
function: self,
op: LLIL_INTRINSIC,
size: 0,
- flags: 0,
+ flag_write: FlagWriteId(0),
op1: outputs.len() as u64,
op2: output_expr_idx as u64,
- op3: intrinsic.id() as u64,
+ op3: intrinsic.id().0 as u64,
op4: input_expr_idx as u64,
_ty: PhantomData,
}
@@ -1440,52 +1560,111 @@ where
let arch = loc.arch.unwrap_or_else(|| *self.arch().as_ref());
unsafe {
- BNLowLevelILSetCurrentAddress(self.handle, arch.0, loc.addr);
+ BNLowLevelILSetCurrentAddress(self.handle, arch.handle, loc.addr);
}
}
- pub fn label_for_address<L: Into<Location>>(&self, loc: L) -> Option<&Label> {
+ pub fn label_for_address<L: Into<Location>>(&self, loc: L) -> Option<LowLevelILLabel> {
use binaryninjacore_sys::BNGetLowLevelILLabelForAddress;
let loc: Location = loc.into();
let arch = loc.arch.unwrap_or_else(|| *self.arch().as_ref());
+ let raw_label =
+ unsafe { BNGetLowLevelILLabelForAddress(self.handle, arch.handle, loc.addr) };
+ match raw_label.is_null() {
+ false => {
+ let mut label = unsafe { LowLevelILLabel::from(*raw_label) };
+ // Set the location so that calls to [Self::update_label_map_for_label] will update the label map.
+ label.location = Some(loc);
+ Some(label)
+ }
+ true => None,
+ }
+ }
- let res = unsafe { BNGetLowLevelILLabelForAddress(self.handle, arch.0, loc.addr) };
+ /// Call this after updating the label through an il operation or via [`Self::mark_label`].
+ fn update_label_map_for_label(&self, label: &LowLevelILLabel) {
+ use binaryninjacore_sys::BNGetLowLevelILLabelForAddress;
- if res.is_null() {
- None
- } else {
- Some(unsafe { &*(res as *mut Label) })
+ // Only need to update the label if there is an associated address.
+ if let Some(loc) = label.location {
+ let arch = loc.arch.unwrap_or_else(|| *self.arch().as_ref());
+ // Add the label into the label map
+ unsafe { BNAddLowLevelILLabelForAddress(self.handle, arch.handle, loc.addr) };
+ // Retrieve a pointer to the label in the map
+ let raw_label =
+ unsafe { BNGetLowLevelILLabelForAddress(self.handle, arch.handle, loc.addr) };
+ // We should always have a valid label here
+ assert!(!raw_label.is_null(), "Failed to add label for address!");
+ // Update the label in the map with `label`
+ unsafe { *raw_label = label.into() };
}
}
- pub fn mark_label(&self, label: &mut Label) {
+ pub fn mark_label(&self, label: &mut LowLevelILLabel) {
use binaryninjacore_sys::BNLowLevelILMarkLabel;
- unsafe {
- BNLowLevelILMarkLabel(self.handle, &mut label.0 as *mut _);
+ let mut raw_label = BNLowLevelILLabel::from(*label);
+ unsafe { BNLowLevelILMarkLabel(self.handle, &mut raw_label) };
+ let mut new_label = LowLevelILLabel::from(raw_label);
+ if let Some(location) = label.location {
+ new_label.location = Some(location);
+ self.update_label_map_for_label(&new_label);
}
+ *label = new_label;
}
}
-use binaryninjacore_sys::BNLowLevelILLabel;
+#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
+pub struct LowLevelILLabel {
+ /// Used to update the label map if the label is associated with a location.
+ pub location: Option<Location>,
+ pub resolved: bool,
+ // TODO: This expr_ref is not actually a valid one sometimes...
+ // TODO: We should make these non public and only accessible if resolved is true.
+ pub expr_ref: LowLevelExpressionIndex,
+ // TODO: If this is 7 this label is not valid.
+ pub operand: usize,
+}
-#[repr(C)]
-pub struct Label(BNLowLevelILLabel);
-impl Label {
+impl LowLevelILLabel {
pub fn new() -> Self {
use binaryninjacore_sys::BNLowLevelILInitLabel;
- unsafe {
- // This is one instance where it'd be easy to use mem::MaybeUninit, but *shrug* this is easier
- let mut res = Label(mem::zeroed());
- BNLowLevelILInitLabel(&mut res.0 as *mut _);
- res
+ let mut raw_label = BNLowLevelILLabel::default();
+ unsafe { BNLowLevelILInitLabel(&mut raw_label) };
+ raw_label.into()
+ }
+}
+
+impl From<BNLowLevelILLabel> for LowLevelILLabel {
+ fn from(value: BNLowLevelILLabel) -> Self {
+ Self {
+ location: None,
+ resolved: value.resolved,
+ expr_ref: LowLevelExpressionIndex(value.ref_),
+ operand: value.operand,
}
}
}
-impl Default for Label {
+impl From<LowLevelILLabel> for BNLowLevelILLabel {
+ fn from(value: LowLevelILLabel) -> Self {
+ Self {
+ resolved: value.resolved,
+ ref_: value.expr_ref.0,
+ operand: value.operand,
+ }
+ }
+}
+
+impl From<&LowLevelILLabel> for BNLowLevelILLabel {
+ fn from(value: &LowLevelILLabel) -> Self {
+ Self::from(*value)
+ }
+}
+
+impl Default for LowLevelILLabel {
fn default() -> Self {
Self::new()
}
diff --git a/rust/src/low_level_il/operation.rs b/rust/src/low_level_il/operation.rs
new file mode 100644
index 00000000..dcc761dd
--- /dev/null
+++ b/rust/src/low_level_il/operation.rs
@@ -0,0 +1,1367 @@
+// Copyright 2021-2024 Vector 35 Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use binaryninjacore_sys::{BNGetLowLevelILByIndex, BNLowLevelILInstruction};
+
+use super::*;
+use crate::architecture::{FlagGroupId, FlagId, FlagWriteId, IntrinsicId};
+use std::collections::BTreeMap;
+use std::fmt::{Debug, Formatter};
+use std::marker::PhantomData;
+use std::mem;
+
+pub struct Operation<'func, A, M, F, O>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+ O: OperationArguments,
+{
+ pub(crate) function: &'func LowLevelILFunction<A, M, F>,
+ pub(crate) op: BNLowLevelILInstruction,
+ _args: PhantomData<O>,
+}
+
+impl<'func, A, M, F, O> Operation<'func, A, M, F, O>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+ O: OperationArguments,
+{
+ pub(crate) fn new(
+ function: &'func LowLevelILFunction<A, M, F>,
+ op: BNLowLevelILInstruction,
+ ) -> Self {
+ Self {
+ function,
+ op,
+ _args: PhantomData,
+ }
+ }
+
+ pub fn address(&self) -> u64 {
+ self.op.address
+ }
+}
+
+impl<A, M, O> Operation<'_, A, M, NonSSA<LiftedNonSSA>, O>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ O: OperationArguments,
+{
+ pub fn flag_write(&self) -> Option<A::FlagWrite> {
+ match self.op.flags {
+ 0 => None,
+ id => self.function.arch().flag_write_from_id(FlagWriteId(id)),
+ }
+ }
+}
+
+// LLIL_NOP, LLIL_NORET, LLIL_BP, LLIL_UNDEF, LLIL_UNIMPL
+pub struct NoArgs;
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, NoArgs>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("NoArgs").finish()
+ }
+}
+
+// LLIL_POP
+pub struct Pop;
+
+impl<A, M, F> Operation<'_, A, M, F, Pop>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, Pop>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Pop")
+ .field("address", &self.address())
+ .field("size", &self.size())
+ .finish()
+ }
+}
+
+// LLIL_SYSCALL, LLIL_SYSCALL_SSA
+pub struct Syscall;
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, Syscall>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Syscall").finish()
+ }
+}
+
+// LLIL_INTRINSIC, LLIL_INTRINSIC_SSA
+pub struct Intrinsic;
+
+impl<A, M, F> Operation<'_, A, M, F, Intrinsic>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ // TODO: Support register and expression lists
+ pub fn intrinsic(&self) -> Option<A::Intrinsic> {
+ let raw_id = self.op.operands[2] as u32;
+ self.function.arch().intrinsic_from_id(IntrinsicId(raw_id))
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, Intrinsic>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Intrinsic")
+ .field("address", &self.address())
+ .field("size", &self.intrinsic())
+ .finish()
+ }
+}
+
+// LLIL_SET_REG, LLIL_SET_REG_SSA, LLIL_SET_REG_PARTIAL_SSA
+pub struct SetReg;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, SetReg>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn dest_reg(&self) -> LowLevelILRegister<A::Register> {
+ let raw_id = self.op.operands[0] as u32;
+
+ if raw_id >= 0x8000_0000 {
+ LowLevelILRegister::Temp(raw_id & 0x7fff_ffff)
+ } else {
+ self.function
+ .arch()
+ .register_from_id(RegisterId(raw_id))
+ .map(LowLevelILRegister::ArchReg)
+ .unwrap_or_else(|| {
+ log::error!(
+ "got garbage register from LLIL_SET_REG @ 0x{:x}",
+ self.op.address
+ );
+
+ LowLevelILRegister::Temp(0)
+ })
+ }
+ }
+
+ pub fn source_expr(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[1] as usize),
+ )
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, SetReg>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("SetReg")
+ .field("address", &self.address())
+ .field("size", &self.size())
+ .field("dest_reg", &self.dest_reg())
+ .field("source_expr", &self.source_expr())
+ .finish()
+ }
+}
+
+// LLIL_SET_REG_SPLIT, LLIL_SET_REG_SPLIT_SSA
+pub struct SetRegSplit;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, SetRegSplit>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn dest_reg_high(&self) -> LowLevelILRegister<A::Register> {
+ let raw_id = self.op.operands[0] as u32;
+
+ if raw_id >= 0x8000_0000 {
+ LowLevelILRegister::Temp(raw_id & 0x7fff_ffff)
+ } else {
+ self.function
+ .arch()
+ .register_from_id(RegisterId(raw_id))
+ .map(LowLevelILRegister::ArchReg)
+ .unwrap_or_else(|| {
+ log::error!(
+ "got garbage register from LLIL_SET_REG_SPLIT @ 0x{:x}",
+ self.op.address
+ );
+
+ LowLevelILRegister::Temp(0)
+ })
+ }
+ }
+
+ pub fn dest_reg_low(&self) -> LowLevelILRegister<A::Register> {
+ let raw_id = self.op.operands[1] as u32;
+
+ if raw_id >= 0x8000_0000 {
+ LowLevelILRegister::Temp(raw_id & 0x7fff_ffff)
+ } else {
+ self.function
+ .arch()
+ .register_from_id(RegisterId(raw_id))
+ .map(LowLevelILRegister::ArchReg)
+ .unwrap_or_else(|| {
+ log::error!(
+ "got garbage register from LLIL_SET_REG_SPLIT @ 0x{:x}",
+ self.op.address
+ );
+
+ LowLevelILRegister::Temp(0)
+ })
+ }
+ }
+
+ pub fn source_expr(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[2] as usize),
+ )
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, SetRegSplit>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("SetRegSplit")
+ .field("address", &self.address())
+ .field("size", &self.size())
+ .field("dest_reg_high", &self.dest_reg_high())
+ .field("dest_reg_low", &self.dest_reg_low())
+ .field("source_expr", &self.source_expr())
+ .finish()
+ }
+}
+
+// LLIL_SET_FLAG, LLIL_SET_FLAG_SSA
+pub struct SetFlag;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, SetFlag>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn dest_flag(&self) -> A::Flag {
+ // TODO: Error handling?
+ // TODO: Test this.
+ self.function
+ .arch()
+ .flag_from_id(FlagId(self.op.operands[0] as u32))
+ .unwrap()
+ }
+
+ pub fn source_expr(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[1] as usize),
+ )
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, SetFlag>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("SetFlag")
+ .field("address", &self.address())
+ .field("dest_flag", &self.dest_flag())
+ .field("source_expr", &self.source_expr())
+ .finish()
+ }
+}
+
+// LLIL_LOAD, LLIL_LOAD_SSA
+pub struct Load;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, Load>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn source_mem_expr(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[0] as usize),
+ )
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, Load>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Load")
+ .field("address", &self.address())
+ .field("size", &self.size())
+ .field("source_mem_expr", &self.source_mem_expr())
+ .finish()
+ }
+}
+
+// LLIL_STORE, LLIL_STORE_SSA
+pub struct Store;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, Store>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn dest_mem_expr(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[0] as usize),
+ )
+ }
+
+ pub fn source_expr(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[1] as usize),
+ )
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, Store>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Store")
+ .field("address", &self.address())
+ .field("size", &self.size())
+ .field("dest_mem_expr", &self.dest_mem_expr())
+ .field("source_expr", &self.source_expr())
+ .finish()
+ }
+}
+
+// LLIL_REG, LLIL_REG_SSA, LLIL_REG_SSA_PARTIAL
+pub struct Reg;
+
+impl<A, M, F> Operation<'_, A, M, F, Reg>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn source_reg(&self) -> LowLevelILRegister<A::Register> {
+ let raw_id = self.op.operands[0] as u32;
+
+ if raw_id >= 0x8000_0000 {
+ LowLevelILRegister::Temp(raw_id & 0x7fff_ffff)
+ } else {
+ self.function
+ .arch()
+ .register_from_id(RegisterId(raw_id))
+ .map(LowLevelILRegister::ArchReg)
+ .unwrap_or_else(|| {
+ log::error!(
+ "got garbage register from LLIL_REG @ 0x{:x}",
+ self.op.address
+ );
+
+ LowLevelILRegister::Temp(0)
+ })
+ }
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, Reg>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Reg")
+ .field("address", &self.address())
+ .field("size", &self.size())
+ .field("source_reg", &self.source_reg())
+ .finish()
+ }
+}
+
+// LLIL_REG_SPLIT, LLIL_REG_SPLIT_SSA
+pub struct RegSplit;
+
+impl<A, M, F> Operation<'_, A, M, F, RegSplit>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn low_reg(&self) -> LowLevelILRegister<A::Register> {
+ let raw_id = self.op.operands[0] as u32;
+
+ if raw_id >= 0x8000_0000 {
+ LowLevelILRegister::Temp(raw_id & 0x7fff_ffff)
+ } else {
+ self.function
+ .arch()
+ .register_from_id(RegisterId(raw_id))
+ .map(LowLevelILRegister::ArchReg)
+ .unwrap_or_else(|| {
+ log::error!(
+ "got garbage register from LLIL_REG @ 0x{:x}",
+ self.op.address
+ );
+
+ LowLevelILRegister::Temp(0)
+ })
+ }
+ }
+
+ pub fn high_reg(&self) -> LowLevelILRegister<A::Register> {
+ let raw_id = self.op.operands[1] as u32;
+
+ if raw_id >= 0x8000_0000 {
+ LowLevelILRegister::Temp(raw_id & 0x7fff_ffff)
+ } else {
+ self.function
+ .arch()
+ .register_from_id(RegisterId(raw_id))
+ .map(LowLevelILRegister::ArchReg)
+ .unwrap_or_else(|| {
+ log::error!(
+ "got garbage register from LLIL_REG @ 0x{:x}",
+ self.op.address
+ );
+
+ LowLevelILRegister::Temp(0)
+ })
+ }
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, RegSplit>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("RegSplit")
+ .field("address", &self.address())
+ .field("size", &self.size())
+ .field("low_reg", &self.low_reg())
+ .field("high_reg", &self.high_reg())
+ .finish()
+ }
+}
+
+// LLIL_FLAG, LLIL_FLAG_SSA
+pub struct Flag;
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, Flag>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Flag").finish()
+ }
+}
+
+// LLIL_FLAG_BIT, LLIL_FLAG_BIT_SSA
+pub struct FlagBit;
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, FlagBit>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("FlagBit").finish()
+ }
+}
+
+// LLIL_JUMP
+pub struct Jump;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, Jump>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn target(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[0] as usize),
+ )
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, Jump>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Jump")
+ .field("target", &self.target())
+ .finish()
+ }
+}
+
+// LLIL_JUMP_TO
+pub struct JumpTo;
+
+struct TargetListIter<'func, A, M, F>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ function: &'func LowLevelILFunction<A, M, F>,
+ cursor: BNLowLevelILInstruction,
+ cursor_operand: usize,
+}
+
+impl<A, M, F> TargetListIter<'_, A, M, F>
+where
+ A: 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: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn target(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[0] as usize),
+ )
+ }
+
+ pub fn target_list(&self) -> BTreeMap<u64, LowLevelInstructionIndex> {
+ 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 = LowLevelInstructionIndex(list.next() as usize);
+ result.insert(value, target);
+ }
+
+ result
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, JumpTo>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("JumpTo")
+ .field("target", &self.target())
+ .field("target_list", &self.target_list())
+ .finish()
+ }
+}
+
+// LLIL_CALL, LLIL_CALL_SSA
+pub struct Call;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, Call>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn target(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[0] as usize),
+ )
+ }
+
+ pub fn stack_adjust(&self) -> Option<u64> {
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_CALL_STACK_ADJUST;
+
+ if self.op.operation == LLIL_CALL_STACK_ADJUST {
+ Some(self.op.operands[1])
+ } else {
+ None
+ }
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, Call>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Call")
+ .field("target", &self.target())
+ .field("stack_adjust", &self.stack_adjust())
+ .finish()
+ }
+}
+
+// LLIL_RET
+pub struct Ret;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, Ret>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn target(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[0] as usize),
+ )
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, Ret>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Ret")
+ .field("target", &self.target())
+ .finish()
+ }
+}
+
+// LLIL_IF
+pub struct If;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, If>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn condition(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[0] as usize),
+ )
+ }
+
+ pub fn true_target(&self) -> LowLevelILInstruction<'func, A, M, F> {
+ LowLevelILInstruction::new(
+ self.function,
+ LowLevelInstructionIndex(self.op.operands[1] as usize),
+ )
+ }
+
+ pub fn false_target(&self) -> LowLevelILInstruction<'func, A, M, F> {
+ LowLevelILInstruction::new(
+ self.function,
+ LowLevelInstructionIndex(self.op.operands[2] as usize),
+ )
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, If>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("If")
+ .field("condition", &self.condition())
+ .field("true_target", &self.true_target())
+ .field("false_target", &self.false_target())
+ .finish()
+ }
+}
+
+// LLIL_GOTO
+pub struct Goto;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, Goto>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn target(&self) -> LowLevelILInstruction<'func, A, M, F> {
+ LowLevelILInstruction::new(
+ self.function,
+ LowLevelInstructionIndex(self.op.operands[0] as usize),
+ )
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, Goto>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Goto")
+ .field("target", &self.target())
+ .finish()
+ }
+}
+
+// LLIL_FLAG_COND
+// Valid only in Lifted IL
+pub struct FlagCond;
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, FlagCond>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("FlagCond").finish()
+ }
+}
+
+// LLIL_FLAG_GROUP
+// Valid only in Lifted IL
+pub struct FlagGroup;
+
+impl<A, M> Operation<'_, A, M, NonSSA<LiftedNonSSA>, FlagGroup>
+where
+ A: Architecture,
+ M: FunctionMutability,
+{
+ pub fn flag_group(&self) -> A::FlagGroup {
+ let id = self.op.operands[0] as u32;
+ self.function
+ .arch()
+ .flag_group_from_id(FlagGroupId(id))
+ .unwrap()
+ }
+}
+
+impl<A, M> Debug for Operation<'_, A, M, NonSSA<LiftedNonSSA>, FlagGroup>
+where
+ A: Architecture,
+ M: FunctionMutability,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("FlagGroup")
+ .field("flag_group", &self.flag_group())
+ .finish()
+ }
+}
+
+impl<A, M> Debug for Operation<'_, A, M, SSA, FlagGroup>
+where
+ A: Architecture,
+ M: FunctionMutability,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("FlagGroup").finish()
+ }
+}
+
+// LLIL_TRAP
+pub struct Trap;
+
+impl<A, M, F> Operation<'_, A, M, F, Trap>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn vector(&self) -> u64 {
+ self.op.operands[0]
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, Trap>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Trap")
+ .field("vector", &self.vector())
+ .finish()
+ }
+}
+
+// LLIL_REG_PHI
+pub struct RegPhi;
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, RegPhi>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("RegPhi").finish()
+ }
+}
+
+// LLIL_FLAG_PHI
+pub struct FlagPhi;
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, FlagPhi>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("FlagPhi").finish()
+ }
+}
+
+// LLIL_MEM_PHI
+pub struct MemPhi;
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, MemPhi>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("MemPhi").finish()
+ }
+}
+
+// LLIL_CONST, LLIL_CONST_PTR
+pub struct Const;
+
+impl<A, M, F> Operation<'_, A, M, F, Const>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn value(&self) -> u64 {
+ #[cfg(debug_assertions)]
+ {
+ let raw = self.op.operands[0] as i64;
+
+ let is_safe = match raw.overflowing_shr(self.op.size as u32 * 8) {
+ (_, true) => true,
+ (res, false) => [-1, 0].contains(&res),
+ };
+
+ if !is_safe {
+ log::error!(
+ "il expr @ {:x} contains constant 0x{:x} as {} byte value (doesn't fit!)",
+ self.op.address,
+ self.op.operands[0],
+ self.op.size
+ );
+ }
+ }
+
+ let mut mask = -1i64 as u64;
+
+ if self.op.size < mem::size_of::<u64>() {
+ mask <<= self.op.size * 8;
+ mask = !mask;
+ }
+
+ self.op.operands[0] & mask
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, Const>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Const")
+ .field("size", &self.size())
+ .field("value", &self.value())
+ .finish()
+ }
+}
+
+// LLIL_EXTERN_PTR
+pub struct Extern;
+
+impl<A, M, F> Operation<'_, A, M, F, Extern>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn value(&self) -> u64 {
+ #[cfg(debug_assertions)]
+ {
+ let raw = self.op.operands[0] as i64;
+
+ let is_safe = match raw.overflowing_shr(self.op.size as u32 * 8) {
+ (_, true) => true,
+ (res, false) => [-1, 0].contains(&res),
+ };
+
+ if !is_safe {
+ log::error!(
+ "il expr @ {:x} contains extern 0x{:x} as {} byte value (doesn't fit!)",
+ self.op.address,
+ self.op.operands[0],
+ self.op.size
+ );
+ }
+ }
+
+ let mut mask = -1i64 as u64;
+
+ if self.op.size < mem::size_of::<u64>() {
+ mask <<= self.op.size * 8;
+ mask = !mask;
+ }
+
+ self.op.operands[0] & mask
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, Extern>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Extern")
+ .field("size", &self.size())
+ .field("value", &self.value())
+ .finish()
+ }
+}
+
+// LLIL_ADD, LLIL_SUB, LLIL_AND, LLIL_OR
+// LLIL_XOR, LLIL_LSL, LLIL_LSR, LLIL_ASR
+// LLIL_ROL, LLIL_ROR, LLIL_MUL, LLIL_MULU_DP,
+// LLIL_MULS_DP, LLIL_DIVU, LLIL_DIVS, LLIL_MODU,
+// LLIL_MODS
+pub struct BinaryOp;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, BinaryOp>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn left(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[0] as usize),
+ )
+ }
+
+ pub fn right(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[1] as usize),
+ )
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, BinaryOp>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("BinaryOp")
+ .field("size", &self.size())
+ .field("left", &self.left())
+ .field("right", &self.right())
+ .finish()
+ }
+}
+
+// LLIL_ADC, LLIL_SBB, LLIL_RLC, LLIL_RRC
+pub struct BinaryOpCarry;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, BinaryOpCarry>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn left(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[0] as usize),
+ )
+ }
+
+ pub fn right(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[1] as usize),
+ )
+ }
+
+ pub fn carry(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[2] as usize),
+ )
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, BinaryOpCarry>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("BinaryOpCarry")
+ .field("size", &self.size())
+ .field("left", &self.left())
+ .field("right", &self.right())
+ .field("carry", &self.carry())
+ .finish()
+ }
+}
+
+// LLIL_DIVS_DP, LLIL_DIVU_DP, LLIL_MODU_DP, LLIL_MODS_DP
+pub struct DoublePrecDivOp;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, DoublePrecDivOp>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn high(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[0] as usize),
+ )
+ }
+
+ pub fn low(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[1] as usize),
+ )
+ }
+
+ // TODO: I don't think this actually exists?
+ pub fn right(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[2] as usize),
+ )
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, DoublePrecDivOp>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("DoublePrecDivOp")
+ .field("size", &self.size())
+ .field("high", &self.high())
+ .field("low", &self.low())
+ // TODO: I don't think this actually is used...
+ .field("right", &self.right())
+ .finish()
+ }
+}
+
+// LLIL_PUSH, LLIL_NEG, LLIL_NOT, LLIL_SX,
+// LLIL_ZX, LLIL_LOW_PART, LLIL_BOOL_TO_INT, LLIL_UNIMPL_MEM
+pub struct UnaryOp;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, UnaryOp>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn operand(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[0] as usize),
+ )
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, UnaryOp>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("UnaryOp")
+ .field("size", &self.size())
+ .field("operand", &self.operand())
+ .finish()
+ }
+}
+
+// LLIL_CMP_X
+pub struct Condition;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, Condition>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn left(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[0] as usize),
+ )
+ }
+
+ pub fn right(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[1] as usize),
+ )
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, Condition>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("Condition")
+ .field("size", &self.size())
+ .field("left", &self.left())
+ .field("right", &self.right())
+ .finish()
+ }
+}
+
+// LLIL_UNIMPL_MEM
+pub struct UnimplMem;
+
+impl<'func, A, M, F> Operation<'func, A, M, F, UnimplMem>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ pub fn size(&self) -> usize {
+ self.op.size
+ }
+
+ pub fn mem_expr(&self) -> LowLevelILExpression<'func, A, M, F, ValueExpr> {
+ LowLevelILExpression::new(
+ self.function,
+ LowLevelExpressionIndex(self.op.operands[0] as usize),
+ )
+ }
+}
+
+impl<A, M, F> Debug for Operation<'_, A, M, F, UnimplMem>
+where
+ A: Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("UnimplMem")
+ .field("size", &self.size())
+ .field("mem_expr", &self.mem_expr())
+ .finish()
+ }
+}
+
+// TODO TEST_BIT
+
+pub trait OperationArguments: 'static {}
+
+impl OperationArguments for NoArgs {}
+impl OperationArguments for Pop {}
+impl OperationArguments for Syscall {}
+impl OperationArguments for Intrinsic {}
+impl OperationArguments for SetReg {}
+impl OperationArguments for SetRegSplit {}
+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 {}
+impl OperationArguments for JumpTo {}
+impl OperationArguments for Call {}
+impl OperationArguments for Ret {}
+impl OperationArguments for If {}
+impl OperationArguments for Goto {}
+impl OperationArguments for FlagCond {}
+impl OperationArguments for FlagGroup {}
+impl OperationArguments for Trap {}
+impl OperationArguments for RegPhi {}
+impl OperationArguments for FlagPhi {}
+impl OperationArguments for MemPhi {}
+impl OperationArguments for Const {}
+impl OperationArguments for Extern {}
+impl OperationArguments for BinaryOp {}
+impl OperationArguments for BinaryOpCarry {}
+impl OperationArguments for DoublePrecDivOp {}
+impl OperationArguments for UnaryOp {}
+impl OperationArguments for Condition {}
+impl OperationArguments for UnimplMem {}
diff --git a/rust/src/main_thread.rs b/rust/src/main_thread.rs
new file mode 100644
index 00000000..f6fdc7cf
--- /dev/null
+++ b/rust/src/main_thread.rs
@@ -0,0 +1,129 @@
+use crate::rc::{Ref, RefCountable};
+use binaryninjacore_sys::{
+ BNExecuteMainThreadAction, BNExecuteOnMainThread, BNExecuteOnMainThreadAndWait,
+ BNFreeMainThreadAction, BNIsMainThreadActionDone, BNMainThreadAction, BNMainThreadCallbacks,
+ BNNewMainThreadActionReference, BNRegisterMainThread, BNWaitForMainThreadAction,
+};
+use std::ffi::c_void;
+
+pub struct MainThreadActionExecutor {
+ func: Box<dyn Fn()>,
+}
+
+impl MainThreadActionExecutor {
+ unsafe extern "C" fn cb_execute(ctx: *mut c_void) {
+ let f: Box<Self> = Box::from_raw(ctx as *mut Self);
+ f.execute();
+ }
+
+ pub fn execute(&self) {
+ (self.func)();
+ }
+}
+
+/// Execute passed function on the main thread. Returns `None` if already running on the main thread.
+///
+/// When not running in headless this will block the UI.
+pub fn execute_on_main_thread<F: Fn() + 'static>(f: F) -> Option<Ref<MainThreadAction>> {
+ let boxed_executor = Box::new(MainThreadActionExecutor { func: Box::new(f) });
+ let raw_executor = Box::into_raw(boxed_executor);
+ let raw_action = unsafe {
+ BNExecuteOnMainThread(
+ raw_executor as *mut c_void,
+ Some(MainThreadActionExecutor::cb_execute),
+ )
+ };
+ match raw_action.is_null() {
+ false => Some(MainThreadAction::ref_from_raw(raw_action)),
+ true => None,
+ }
+}
+
+/// Execute passed function on the main thread and wait until the function is finished.
+///
+/// When not running in headless this will block the UI.
+pub fn execute_on_main_thread_and_wait<F: Fn() + 'static>(f: F) {
+ let boxed_executor = Box::new(MainThreadActionExecutor { func: Box::new(f) });
+ let raw_executor = Box::into_raw(boxed_executor);
+ unsafe {
+ BNExecuteOnMainThreadAndWait(
+ raw_executor as *mut c_void,
+ Some(MainThreadActionExecutor::cb_execute),
+ )
+ };
+}
+
+/// The trait required for receiving main thread actions
+pub trait MainThreadHandler: Sized {
+ fn add_action(&self, _view: Ref<MainThreadAction>);
+
+ unsafe extern "C" fn cb_add_action(ctxt: *mut c_void, action: *mut BNMainThreadAction) {
+ ffi_wrap!("MainThread::add_action", {
+ let main_thread = &*(ctxt as *mut Self);
+ let action = MainThreadAction::ref_from_raw(action);
+ main_thread.add_action(action);
+ })
+ }
+
+ /// Register the main thread handler. Leaking [`Self`] in the process.
+ ///
+ /// NOTE: This MUST be called from **within** the main thread.
+ fn register(self) {
+ // NOTE: We leak self here.
+ let raw = Box::into_raw(Box::new(self));
+ let mut callbacks = BNMainThreadCallbacks {
+ context: raw as *mut c_void,
+ addAction: Some(Self::cb_add_action),
+ };
+ unsafe { BNRegisterMainThread(&mut callbacks) };
+ }
+}
+
+pub struct MainThreadAction {
+ pub handle: *mut BNMainThreadAction,
+}
+
+impl MainThreadAction {
+ pub fn from_raw(handle: *mut BNMainThreadAction) -> Self {
+ assert!(!handle.is_null());
+ Self { handle }
+ }
+
+ pub fn ref_from_raw(handle: *mut BNMainThreadAction) -> Ref<Self> {
+ unsafe { Ref::new(Self::from_raw(handle)) }
+ }
+
+ pub fn execute(&self) {
+ unsafe { BNExecuteMainThreadAction(self.handle) }
+ }
+
+ pub fn is_done(&self) -> bool {
+ unsafe { BNIsMainThreadActionDone(self.handle) }
+ }
+
+ pub fn wait(&self) {
+ unsafe { BNWaitForMainThreadAction(self.handle) }
+ }
+}
+
+impl ToOwned for MainThreadAction {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for MainThreadAction {
+ unsafe fn inc_ref(action: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: BNNewMainThreadActionReference(action.handle),
+ })
+ }
+
+ unsafe fn dec_ref(action: &Self) {
+ BNFreeMainThreadAction(action.handle);
+ }
+}
+
+unsafe impl Send for MainThreadAction {}
diff --git a/rust/src/mlil/mod.rs b/rust/src/medium_level_il.rs
index 8a9103ac..6bda8df0 100644
--- a/rust/src/mlil/mod.rs
+++ b/rust/src/medium_level_il.rs
@@ -1,6 +1,8 @@
+//! **WARNING** This API is incomplete and subject to change in the near future!
+
mod block;
mod function;
-mod instruction;
+pub mod instruction;
mod lift;
pub mod operation;
diff --git a/rust/src/mlil/block.rs b/rust/src/medium_level_il/block.rs
index 015c7ba4..1fcd51ca 100644
--- a/rust/src/mlil/block.rs
+++ b/rust/src/medium_level_il/block.rs
@@ -1,59 +1,42 @@
-use std::ops::Range;
-
-use binaryninjacore_sys::BNGetMediumLevelILIndexForInstruction;
-
-use crate::basicblock::{BasicBlock, BlockContext};
+use crate::basic_block::{BasicBlock, BlockContext};
use crate::rc::Ref;
+use std::ops::Range;
-use super::{MediumLevelILFunction, MediumLevelILInstruction};
-
-pub struct MediumLevelILBlockIter {
- function: Ref<MediumLevelILFunction>,
- range: Range<u64>,
-}
-
-impl Iterator for MediumLevelILBlockIter {
- type Item = MediumLevelILInstruction;
-
- fn next(&mut self) -> Option<Self::Item> {
- self.range
- .next()
- .map(|i| unsafe {
- BNGetMediumLevelILIndexForInstruction(self.function.handle, i as usize)
- })
- .map(|i| MediumLevelILInstruction::new(self.function.to_owned(), i))
- }
-}
+use super::{MediumLevelILFunction, MediumLevelILInstruction, MediumLevelInstructionIndex};
pub struct MediumLevelILBlock {
pub(crate) function: Ref<MediumLevelILFunction>,
}
-impl core::fmt::Debug for MediumLevelILBlock {
- fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
- write!(f, "mlil_bb {:?}", self.function)
- }
-}
-
impl BlockContext for MediumLevelILBlock {
- type Iter = MediumLevelILBlockIter;
type Instruction = MediumLevelILInstruction;
+ type InstructionIndex = MediumLevelInstructionIndex;
+ type Iter = MediumLevelILBlockIter;
fn start(&self, block: &BasicBlock<Self>) -> MediumLevelILInstruction {
- let expr_idx = unsafe {
- BNGetMediumLevelILIndexForInstruction(self.function.handle, block.raw_start() as usize)
- };
- MediumLevelILInstruction::new(self.function.to_owned(), expr_idx)
+ // TODO: instruction_from_index says that it is not mapped and will do the call
+ // TODO: What if this IS already MAPPED!?!?!?
+ self.function
+ .instruction_from_index(block.start_index())
+ .unwrap()
}
fn iter(&self, block: &BasicBlock<Self>) -> MediumLevelILBlockIter {
MediumLevelILBlockIter {
function: self.function.to_owned(),
- range: block.raw_start()..block.raw_end(),
+ range: block.start_index().0..block.end_index().0,
}
}
}
+impl std::fmt::Debug for MediumLevelILBlock {
+ fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
+ f.debug_struct("MediumLevelILBlock")
+ .field("function", &self.function)
+ .finish()
+ }
+}
+
impl Clone for MediumLevelILBlock {
fn clone(&self) -> Self {
MediumLevelILBlock {
@@ -61,3 +44,19 @@ impl Clone for MediumLevelILBlock {
}
}
}
+
+pub struct MediumLevelILBlockIter {
+ function: Ref<MediumLevelILFunction>,
+ range: Range<usize>,
+}
+
+impl Iterator for MediumLevelILBlockIter {
+ type Item = MediumLevelILInstruction;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.range
+ .next()
+ .map(MediumLevelInstructionIndex)
+ .and_then(|i| self.function.instruction_from_index(i))
+ }
+}
diff --git a/rust/src/medium_level_il/function.rs b/rust/src/medium_level_il/function.rs
new file mode 100644
index 00000000..a9802005
--- /dev/null
+++ b/rust/src/medium_level_il/function.rs
@@ -0,0 +1,718 @@
+use binaryninjacore_sys::*;
+use std::ffi::c_char;
+use std::fmt::{Debug, Formatter};
+use std::hash::{Hash, Hasher};
+
+use super::{MediumLevelILBlock, MediumLevelILInstruction, MediumLevelInstructionIndex};
+use crate::architecture::CoreArchitecture;
+use crate::basic_block::BasicBlock;
+use crate::confidence::Conf;
+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::Type;
+use crate::variable::{PossibleValueSet, RegisterValue, SSAVariable, UserVariableValue, Variable};
+
+// TODO: Does this belong here?
+pub use binaryninjacore_sys::BNFunctionGraphType as FunctionGraphType;
+
+pub struct MediumLevelILFunction {
+ pub(crate) handle: *mut BNMediumLevelILFunction,
+}
+
+impl MediumLevelILFunction {
+ pub(crate) unsafe fn from_raw(handle: *mut BNMediumLevelILFunction) -> Self {
+ debug_assert!(!handle.is_null());
+ Self { handle }
+ }
+
+ pub(crate) unsafe fn ref_from_raw(handle: *mut BNMediumLevelILFunction) -> Ref<Self> {
+ debug_assert!(!handle.is_null());
+ Ref::new(Self::from_raw(handle))
+ }
+
+ pub fn instruction_at<L: Into<Location>>(&self, loc: L) -> Option<MediumLevelILInstruction> {
+ Some(MediumLevelILInstruction::new(
+ self.to_owned(),
+ self.instruction_index_at(loc)?,
+ ))
+ }
+
+ pub fn instruction_index_at<L: Into<Location>>(
+ &self,
+ loc: L,
+ ) -> Option<MediumLevelInstructionIndex> {
+ let loc: Location = loc.into();
+ let arch = loc
+ .arch
+ .map(|a| a.handle)
+ .unwrap_or_else(std::ptr::null_mut);
+ let instr_idx = unsafe { BNMediumLevelILGetInstructionStart(self.handle, arch, loc.addr) };
+ // `instr_idx` will equal self.instruction_count() if the instruction is not valid.
+ if instr_idx >= self.instruction_count() {
+ None
+ } else {
+ Some(MediumLevelInstructionIndex(instr_idx))
+ }
+ }
+
+ pub fn instruction_from_index(
+ &self,
+ index: MediumLevelInstructionIndex,
+ ) -> Option<MediumLevelILInstruction> {
+ if index.0 >= self.instruction_count() {
+ None
+ } else {
+ Some(MediumLevelILInstruction::new(self.to_owned(), index))
+ }
+ }
+
+ pub fn instruction_from_expr_index(
+ &self,
+ expr_index: MediumLevelInstructionIndex,
+ ) -> Option<MediumLevelILInstruction> {
+ if expr_index.0 >= self.expression_count() {
+ None
+ } else {
+ Some(MediumLevelILInstruction::new_expr(
+ self.to_owned(),
+ expr_index,
+ ))
+ }
+ }
+
+ pub fn instruction_count(&self) -> usize {
+ unsafe { BNGetMediumLevelILInstructionCount(self.handle) }
+ }
+
+ pub fn expression_count(&self) -> usize {
+ unsafe { BNGetMediumLevelILExprCount(self.handle) }
+ }
+
+ pub fn ssa_form(&self) -> MediumLevelILFunction {
+ let ssa = unsafe { BNGetMediumLevelILSSAForm(self.handle) };
+ assert!(!ssa.is_null());
+ MediumLevelILFunction { handle: ssa }
+ }
+
+ pub fn function(&self) -> Ref<Function> {
+ unsafe {
+ let func = BNGetMediumLevelILOwnerFunction(self.handle);
+ Function::ref_from_raw(func)
+ }
+ }
+
+ pub fn basic_blocks(&self) -> Array<BasicBlock<MediumLevelILBlock>> {
+ let mut count = 0;
+ let blocks = unsafe { BNGetMediumLevelILBasicBlockList(self.handle, &mut count) };
+ let context = MediumLevelILBlock {
+ function: self.to_owned(),
+ };
+ unsafe { Array::new(blocks, count, context) }
+ }
+
+ pub fn var_definitions(&self, var: &Variable) -> Array<MediumLevelILInstruction> {
+ let mut count = 0;
+ let raw_var = BNVariable::from(var);
+ let raw_instr_idxs =
+ unsafe { BNGetMediumLevelILVariableDefinitions(self.handle, &raw_var, &mut count) };
+ assert!(!raw_instr_idxs.is_null());
+ unsafe { Array::new(raw_instr_idxs, count, self.to_owned()) }
+ }
+
+ pub fn create_user_stack_var<'a, S: BnStrCompatible, C: Into<Conf<&'a Type>>>(
+ self,
+ offset: i64,
+ var_type: C,
+ name: S,
+ ) {
+ let mut owned_raw_var_ty = Conf::<&Type>::into_raw(var_type.into());
+ let name = name.into_bytes_with_nul();
+ unsafe {
+ BNCreateUserStackVariable(
+ self.function().handle,
+ offset,
+ &mut owned_raw_var_ty,
+ name.as_ref().as_ptr() as *const c_char,
+ )
+ }
+ }
+
+ pub fn delete_user_stack_var(self, offset: i64) {
+ unsafe { BNDeleteUserStackVariable(self.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 raw_var = BNVariable::from(var);
+ let mut owned_raw_var_ty = Conf::<&Type>::into_raw(var_type.into());
+ let name = name.into_bytes_with_nul();
+ unsafe {
+ BNCreateUserVariable(
+ self.function().handle,
+ &raw_var,
+ &mut owned_raw_var_ty,
+ name.as_ref().as_ptr() as *const _,
+ ignore_disjoint_uses,
+ )
+ }
+ }
+
+ pub fn delete_user_var(&self, var: &Variable) {
+ let raw_var = BNVariable::from(var);
+ unsafe { BNDeleteUserVariable(self.function().handle, &raw_var) }
+ }
+
+ pub fn is_var_user_defined(&self, var: &Variable) -> bool {
+ let raw_var = BNVariable::from(var);
+ unsafe { BNIsVariableUserDefined(self.function().handle, &raw_var) }
+ }
+
+ /// 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::medium_level_il::MediumLevelILFunction;
+ /// # use binaryninja::variable::PossibleValueSet;
+ /// # let mlil_fun: MediumLevelILFunction = todo!();
+ /// let user_var_val = mlil_fun.user_var_values().iter().next().unwrap();
+ /// let def_address = user_var_val.def_site.addr;
+ /// let var_value = PossibleValueSet::ConstantValue { value: 5 };
+ /// mlil_fun
+ /// .set_user_var_value(&user_var_val.variable, def_address, var_value)
+ /// .unwrap();
+ /// ```
+ pub fn set_user_var_value(
+ &self,
+ var: &Variable,
+ addr: u64,
+ value: PossibleValueSet,
+ ) -> Result<(), ()> {
+ let Some(_def_site) = self
+ .var_definitions(var)
+ .iter()
+ .find(|def| def.address == addr)
+ else {
+ // Error "No definition for Variable found at given address"
+ return Err(());
+ };
+ let function = self.function();
+ let def_site = BNArchitectureAndAddress {
+ arch: function.arch().handle,
+ address: addr,
+ };
+ let raw_var = BNVariable::from(var);
+ let raw_value = PossibleValueSet::into_raw(value);
+ unsafe { BNSetUserVariableValue(function.handle, &raw_var, &def_site, &raw_value) }
+ PossibleValueSet::free_owned_raw(raw_value);
+ 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
+ .var_definitions(var)
+ .iter()
+ .find(|site| site.address == addr)
+ else {
+ //error "Could not get definition for Variable"
+ return Err(());
+ };
+
+ let function = self.function();
+ let raw_var = BNVariable::from(var);
+ let def_site = BNArchitectureAndAddress {
+ arch: function.arch().handle,
+ address: addr,
+ };
+
+ unsafe { BNClearUserVariableValue(function.handle, &raw_var, &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) -> Array<UserVariableValue> {
+ let mut count = 0;
+ let function = self.function();
+ let var_values = unsafe { BNGetAllUserVariableValues(function.handle, &mut count) };
+ assert!(!var_values.is_null());
+ unsafe { Array::new(var_values, count, ()) }
+ }
+
+ /// Clear all user defined variable values.
+ pub fn clear_user_var_values(&self) -> Result<(), ()> {
+ for user_var_val in &self.user_var_values() {
+ self.clear_user_var_value(&user_var_val.variable, user_var_val.def_site.addr)?;
+ }
+ Ok(())
+ }
+
+ pub fn create_auto_stack_var<'a, T: Into<Conf<&'a Type>>, S: BnStrCompatible>(
+ &self,
+ offset: i64,
+ var_type: T,
+ name: S,
+ ) {
+ let mut owned_raw_var_ty = Conf::<&Type>::into_raw(var_type.into());
+ let name = name.into_bytes_with_nul();
+ let name_c_str = name.as_ref();
+ unsafe {
+ BNCreateAutoStackVariable(
+ self.function().handle,
+ offset,
+ &mut owned_raw_var_ty,
+ name_c_str.as_ptr() as *const c_char,
+ )
+ }
+ }
+
+ pub fn delete_auto_stack_var(&self, offset: i64) {
+ unsafe { BNDeleteAutoStackVariable(self.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 raw_var = BNVariable::from(var);
+ let mut owned_raw_var_ty = Conf::<&Type>::into_raw(var_type.into());
+ let name = name.into_bytes_with_nul();
+ let name_c_str = name.as_ref();
+ unsafe {
+ BNCreateAutoVariable(
+ self.function().handle,
+ &raw_var,
+ &mut owned_raw_var_ty,
+ 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::medium_level_il::MediumLevelILFunction;
+ /// # use binaryninja::variable::Variable;
+ /// # let mlil_fun: MediumLevelILFunction = todo!();
+ /// # let mlil_var: Variable = todo!();
+ /// let instr_idx = mlil_fun.var_refs(&mlil_var).get(0).expr_idx;
+ /// ```
+ pub fn var_refs(&self, var: &Variable) -> Array<ILReferenceSource> {
+ let mut count = 0;
+ let mut raw_var = BNVariable::from(var);
+ let refs = unsafe {
+ BNGetMediumLevelILVariableReferences(self.function().handle, &mut raw_var, &mut count)
+ };
+ assert!(!refs.is_null());
+ unsafe { Array::new(refs, count, ()) }
+ }
+
+ /// Retrieves variable references from a specified location or range within a medium-level IL function.
+ ///
+ /// Passing in a `length` will query a range for variable references, instead of just the address
+ /// specified in `location`.
+ pub fn var_refs_from(
+ &self,
+ location: impl Into<Location>,
+ length: Option<u64>,
+ ) -> Array<VariableReferenceSource> {
+ let location = location.into();
+ let raw_arch = location
+ .arch
+ .map(|a| a.handle)
+ .unwrap_or(std::ptr::null_mut());
+ let function = self.function();
+ let mut count = 0;
+
+ let refs = if let Some(length) = length {
+ unsafe {
+ BNGetMediumLevelILVariableReferencesInRange(
+ function.handle,
+ raw_arch,
+ location.addr,
+ length,
+ &mut count,
+ )
+ }
+ } else {
+ unsafe {
+ BNGetMediumLevelILVariableReferencesFrom(
+ function.handle,
+ raw_arch,
+ location.addr,
+ &mut count,
+ )
+ }
+ };
+ assert!(!refs.is_null());
+ unsafe { Array::new(refs, count, ()) }
+ }
+
+ // TODO: Rename to `current_location`?
+ /// Current IL Address
+ pub fn current_address(&self) -> Location {
+ let addr = unsafe { BNMediumLevelILGetCurrentAddress(self.handle) };
+ Location::from(addr)
+ }
+
+ // TODO: Rename to `set_current_location`?
+ /// Set the current IL Address
+ pub fn set_current_address(&self, location: impl Into<Location>) {
+ let location = location.into();
+ let arch = location
+ .arch
+ .map(|a| a.handle)
+ .unwrap_or(std::ptr::null_mut());
+ unsafe { BNMediumLevelILSetCurrentAddress(self.handle, arch, location.addr) }
+ }
+
+ /// Returns the [`BasicBlock`] at the given instruction `index`.
+ ///
+ /// You can also retrieve this using [`MediumLevelILInstruction::basic_block`].
+ pub fn basic_block_containing_index(
+ &self,
+ index: MediumLevelInstructionIndex,
+ ) -> Option<Ref<BasicBlock<MediumLevelILBlock>>> {
+ let context = MediumLevelILBlock {
+ function: self.to_owned(),
+ };
+ // TODO: If we can guarantee self.index is valid we can omit the wrapped Option.
+ let basic_block_ptr =
+ unsafe { BNGetMediumLevelILBasicBlockForInstruction(self.handle, index.0) };
+ match basic_block_ptr.is_null() {
+ false => Some(unsafe { BasicBlock::ref_from_raw(basic_block_ptr, context) }),
+ true => None,
+ }
+ }
+
+ /// Ends the function and computes the list of basic blocks.
+ ///
+ /// NOTE: This should be called after updating MLIL.
+ pub fn finalize(&self) {
+ unsafe { BNFinalizeMediumLevelILFunction(self.handle) }
+ }
+
+ /// Generate SSA form given the current MLIL.
+ ///
+ /// NOTE: This should be called after updating MLIL.
+ ///
+ /// * `analyze_conditionals` - whether to analyze conditionals
+ /// * `handle_aliases` - whether to handle aliases
+ /// * `non_aliased_vars` - optional list of variables known to be not aliased
+ /// * `aliased_vars` - optional list of variables known to be aliased
+ pub fn generate_ssa_form(
+ &self,
+ analyze_conditionals: bool,
+ handle_aliases: bool,
+ non_aliased_vars: impl IntoIterator<Item = Variable>,
+ aliased_vars: impl IntoIterator<Item = Variable>,
+ ) {
+ let raw_non_aliased_vars: Vec<BNVariable> =
+ non_aliased_vars.into_iter().map(Into::into).collect();
+ let raw_aliased_vars: Vec<BNVariable> = aliased_vars.into_iter().map(Into::into).collect();
+ unsafe {
+ BNGenerateMediumLevelILSSAForm(
+ self.handle,
+ analyze_conditionals,
+ handle_aliases,
+ raw_non_aliased_vars.as_ptr() as *mut _,
+ raw_non_aliased_vars.len(),
+ raw_aliased_vars.as_ptr() as *mut _,
+ raw_aliased_vars.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,
+ ssa_variable: &SSAVariable,
+ ) -> Option<MediumLevelILInstruction> {
+ let raw_var = BNVariable::from(ssa_variable.variable);
+ let result = unsafe {
+ BNGetMediumLevelILSSAVarDefinition(self.handle, &raw_var, ssa_variable.version)
+ };
+ // TODO: Does this return the expression or instruction index? Also we dont diff and this prob doesnt work.
+ self.instruction_from_index(MediumLevelInstructionIndex(result))
+ }
+
+ pub fn ssa_memory_definition(&self, version: usize) -> Option<MediumLevelILInstruction> {
+ let result = unsafe { BNGetMediumLevelILSSAMemoryDefinition(self.handle, version) };
+ // TODO: Does this return the expression or instruction index? Also we dont diff and this prob doesnt work.
+ self.instruction_from_index(MediumLevelInstructionIndex(result))
+ }
+
+ /// Gets all the instructions that use the given SSA variable.
+ pub fn ssa_variable_uses(&self, ssa_variable: &SSAVariable) -> Array<MediumLevelILInstruction> {
+ let mut count = 0;
+ let raw_var = BNVariable::from(ssa_variable.variable);
+ let uses = unsafe {
+ BNGetMediumLevelILSSAVarUses(self.handle, &raw_var, ssa_variable.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 `variable` is live at any point in the function
+ pub fn is_ssa_variable_live(&self, ssa_variable: &SSAVariable) -> bool {
+ let raw_var = BNVariable::from(ssa_variable.variable);
+ unsafe { BNIsMediumLevelILSSAVarLive(self.handle, &raw_var, ssa_variable.version) }
+ }
+
+ pub fn variable_definitions(&self, variable: &Variable) -> Array<MediumLevelILInstruction> {
+ let mut count = 0;
+ let raw_var = BNVariable::from(variable);
+ let defs =
+ unsafe { BNGetMediumLevelILVariableDefinitions(self.handle, &raw_var, &mut count) };
+ unsafe { Array::new(defs, count, self.to_owned()) }
+ }
+
+ pub fn variable_uses(&self, variable: &Variable) -> Array<MediumLevelILInstruction> {
+ let mut count = 0;
+ let raw_var = BNVariable::from(variable);
+ let uses = unsafe { BNGetMediumLevelILVariableUses(self.handle, &raw_var, &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 raw_var = BNVariable::from(variable);
+ let uses = unsafe {
+ BNGetMediumLevelILLiveInstructionsForVariable(
+ self.handle,
+ &raw_var,
+ include_last_user,
+ &mut count,
+ )
+ };
+ unsafe { Array::new(uses, count, self.to_owned()) }
+ }
+
+ pub fn ssa_variable_value(&self, ssa_variable: &SSAVariable) -> RegisterValue {
+ let raw_var = BNVariable::from(ssa_variable.variable);
+ unsafe { BNGetMediumLevelILSSAVarValue(self.handle, &raw_var, ssa_variable.version) }.into()
+ }
+
+ pub fn create_graph(&self, settings: Option<DisassemblySettings>) -> FlowGraph {
+ let settings = settings.map(|x| x.handle).unwrap_or(std::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 [`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 [`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 the MLIL SSA variables for a given [`Variable`].
+ pub fn ssa_variables(&self, variable: &Variable) -> Array<SSAVariable> {
+ let mut count = 0;
+ let raw_variable = BNVariable::from(variable);
+ let versions = unsafe {
+ BNGetMediumLevelILVariableSSAVersions(self.handle, &raw_variable, &mut count)
+ };
+ unsafe { Array::new(versions, count, *variable) }
+ }
+}
+
+impl ToOwned for MediumLevelILFunction {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for MediumLevelILFunction {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: BNNewMediumLevelILFunctionReference(handle.handle),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeMediumLevelILFunction(handle.handle);
+ }
+}
+
+impl Debug for MediumLevelILFunction {
+ fn fmt(&self, f: &mut Formatter) -> std::fmt::Result {
+ f.debug_struct("MediumLevelILFunction")
+ .field("arch", &self.function().arch())
+ .field("instruction_count", &self.instruction_count())
+ .finish()
+ }
+}
+
+unsafe impl Send for MediumLevelILFunction {}
+unsafe impl Sync for MediumLevelILFunction {}
+
+impl Eq for MediumLevelILFunction {}
+impl PartialEq for MediumLevelILFunction {
+ fn eq(&self, rhs: &Self) -> bool {
+ self.function().eq(&rhs.function())
+ }
+}
+
+impl Hash for MediumLevelILFunction {
+ fn hash<H: Hasher>(&self, state: &mut H) {
+ self.function().hash(state)
+ }
+}
+
+pub struct ILReferenceSource {
+ pub function: Ref<Function>,
+ pub arch: CoreArchitecture,
+ pub addr: u64,
+ pub graph_type: FunctionGraphType,
+ pub expr_idx: usize,
+}
+
+impl From<BNILReferenceSource> for ILReferenceSource {
+ fn from(value: BNILReferenceSource) -> Self {
+ Self {
+ function: unsafe { Function::ref_from_raw(value.func) },
+ arch: unsafe { CoreArchitecture::from_raw(value.arch) },
+ addr: value.addr,
+ graph_type: value.type_,
+ expr_idx: value.exprId,
+ }
+ }
+}
+
+impl From<&BNILReferenceSource> for ILReferenceSource {
+ fn from(value: &BNILReferenceSource) -> Self {
+ Self {
+ function: unsafe { Function::from_raw(value.func).to_owned() },
+ arch: unsafe { CoreArchitecture::from_raw(value.arch) },
+ addr: value.addr,
+ graph_type: value.type_,
+ expr_idx: value.exprId,
+ }
+ }
+}
+
+impl CoreArrayProvider for ILReferenceSource {
+ type Raw = BNILReferenceSource;
+ type Context = ();
+ 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> {
+ raw.into()
+ }
+}
+
+pub struct VariableReferenceSource {
+ pub variable: Variable,
+ pub source: ILReferenceSource,
+}
+
+impl From<BNVariableReferenceSource> for VariableReferenceSource {
+ fn from(value: BNVariableReferenceSource) -> Self {
+ Self {
+ variable: Variable::from(value.var),
+ source: value.source.into(),
+ }
+ }
+}
+
+impl From<&BNVariableReferenceSource> for VariableReferenceSource {
+ fn from(value: &BNVariableReferenceSource) -> Self {
+ Self {
+ variable: Variable::from(value.var),
+ // TODO: We really need to document this better, or have some other facility for this.
+ // NOTE: We take this as a ref to increment the function ref.
+ source: ILReferenceSource::from(&value.source),
+ }
+ }
+}
+
+impl CoreArrayProvider for VariableReferenceSource {
+ type Raw = BNVariableReferenceSource;
+ type Context = ();
+ 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> {
+ raw.into()
+ }
+}
diff --git a/rust/src/mlil/instruction.rs b/rust/src/medium_level_il/instruction.rs
index 88564cc6..bd07da94 100644
--- a/rust/src/mlil/instruction.rs
+++ b/rust/src/medium_level_il/instruction.rs
@@ -1,175 +1,73 @@
-use binaryninjacore_sys::*;
-
-use crate::architecture::CoreIntrinsic;
+use super::lift::*;
+use super::operation::*;
+use super::{MediumLevelILBlock, MediumLevelILFunction};
+use crate::architecture::{CoreIntrinsic, FlagId, IntrinsicId, RegisterId};
+use crate::basic_block::BasicBlock;
+use crate::confidence::Conf;
use crate::disassembly::InstructionTextToken;
use crate::operand_iter::OperandIter;
use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref};
-use crate::types::{
- Conf, ConstantData, DataFlowQueryOption, ILBranchDependence, PossibleValueSet,
- RegisterValue, RegisterValueType, SSAVariable, Type, Variable,
-};
+use crate::types::Type;
+use crate::variable::{ConstantData, PossibleValueSet, RegisterValue, SSAVariable, Variable};
+use crate::{DataFlowQueryOption, ILBranchDependence};
+use binaryninjacore_sys::*;
+use std::fmt;
+use std::fmt::{Debug, Display, Formatter};
-use super::lift::*;
-use super::operation::*;
-use super::MediumLevelILFunction;
+#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct MediumLevelInstructionIndex(pub usize);
+
+impl MediumLevelInstructionIndex {
+ pub fn next(&self) -> Self {
+ Self(self.0 + 1)
+ }
+}
+
+impl From<usize> for MediumLevelInstructionIndex {
+ fn from(index: usize) -> Self {
+ Self(index)
+ }
+}
+
+impl From<u64> for MediumLevelInstructionIndex {
+ fn from(index: u64) -> Self {
+ Self(index as usize)
+ }
+}
+
+impl Display for MediumLevelInstructionIndex {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.write_fmt(format_args!("{}", self.0))
+ }
+}
#[derive(Clone)]
pub struct MediumLevelILInstruction {
pub function: Ref<MediumLevelILFunction>,
pub address: u64,
- pub index: usize,
+ // TODO; Because this structure is incorrectly named instruction, we want to make it clear that we actually have the expression index.
+ pub expr_index: MediumLevelInstructionIndex,
pub size: usize,
pub kind: MediumLevelILInstructionKind,
}
-#[derive(Copy, Clone)]
-pub enum MediumLevelILInstructionKind {
- Nop,
- Noret,
- Bp,
- Undef,
- Unimpl,
- If(MediumLevelILOperationIf),
- FloatConst(FloatConst),
- Const(Constant),
- ConstPtr(Constant),
- Import(Constant),
- ExternPtr(ExternPtr),
- ConstData(ConstData),
- Jump(Jump),
- RetHint(Jump),
- StoreSsa(StoreSsa),
- StoreStructSsa(StoreStructSsa),
- StoreStruct(StoreStruct),
- Store(Store),
- JumpTo(JumpTo),
- Goto(Goto),
- FreeVarSlot(FreeVarSlot),
- SetVarField(SetVarField),
- SetVar(SetVar),
- FreeVarSlotSsa(FreeVarSlotSsa),
- SetVarSsaField(SetVarSsaField),
- SetVarAliasedField(SetVarSsaField),
- SetVarAliased(SetVarAliased),
- SetVarSsa(SetVarSsa),
- VarPhi(VarPhi),
- MemPhi(MemPhi),
- VarSplit(VarSplit),
- SetVarSplit(SetVarSplit),
- VarSplitSsa(VarSplitSsa),
- SetVarSplitSsa(SetVarSplitSsa),
- Add(BinaryOp),
- Sub(BinaryOp),
- And(BinaryOp),
- Or(BinaryOp),
- Xor(BinaryOp),
- Lsl(BinaryOp),
- Lsr(BinaryOp),
- Asr(BinaryOp),
- Rol(BinaryOp),
- Ror(BinaryOp),
- Mul(BinaryOp),
- MuluDp(BinaryOp),
- MulsDp(BinaryOp),
- Divu(BinaryOp),
- DivuDp(BinaryOp),
- Divs(BinaryOp),
- DivsDp(BinaryOp),
- Modu(BinaryOp),
- ModuDp(BinaryOp),
- Mods(BinaryOp),
- ModsDp(BinaryOp),
- CmpE(BinaryOp),
- CmpNe(BinaryOp),
- CmpSlt(BinaryOp),
- CmpUlt(BinaryOp),
- CmpSle(BinaryOp),
- CmpUle(BinaryOp),
- CmpSge(BinaryOp),
- CmpUge(BinaryOp),
- CmpSgt(BinaryOp),
- CmpUgt(BinaryOp),
- TestBit(BinaryOp),
- AddOverflow(BinaryOp),
- FcmpE(BinaryOp),
- FcmpNe(BinaryOp),
- FcmpLt(BinaryOp),
- FcmpLe(BinaryOp),
- FcmpGe(BinaryOp),
- FcmpGt(BinaryOp),
- FcmpO(BinaryOp),
- FcmpUo(BinaryOp),
- Fadd(BinaryOp),
- Fsub(BinaryOp),
- Fmul(BinaryOp),
- Fdiv(BinaryOp),
- Adc(BinaryOpCarry),
- Sbb(BinaryOpCarry),
- Rlc(BinaryOpCarry),
- Rrc(BinaryOpCarry),
- Call(Call),
- Tailcall(Call),
- Syscall(Syscall),
- Intrinsic(Intrinsic),
- IntrinsicSsa(IntrinsicSsa),
- CallSsa(CallSsa),
- TailcallSsa(CallSsa),
- CallUntypedSsa(CallUntypedSsa),
- TailcallUntypedSsa(CallUntypedSsa),
- SyscallSsa(SyscallSsa),
- SyscallUntypedSsa(SyscallUntypedSsa),
- CallUntyped(CallUntyped),
- TailcallUntyped(CallUntyped),
- SyscallUntyped(SyscallUntyped),
- SeparateParamList(SeparateParamList),
- SharedParamSlot(SharedParamSlot),
- Neg(UnaryOp),
- Not(UnaryOp),
- Sx(UnaryOp),
- Zx(UnaryOp),
- LowPart(UnaryOp),
- BoolToInt(UnaryOp),
- UnimplMem(UnaryOp),
- Fsqrt(UnaryOp),
- Fneg(UnaryOp),
- Fabs(UnaryOp),
- FloatToInt(UnaryOp),
- IntToFloat(UnaryOp),
- FloatConv(UnaryOp),
- RoundToInt(UnaryOp),
- Floor(UnaryOp),
- Ceil(UnaryOp),
- Ftrunc(UnaryOp),
- Load(UnaryOp),
- LoadStruct(LoadStruct),
- LoadStructSsa(LoadStructSsa),
- LoadSsa(LoadSsa),
- Ret(Ret),
- Var(Var),
- AddressOf(Var),
- VarField(Field),
- AddressOfField(Field),
- VarSsa(VarSsa),
- VarAliased(VarSsa),
- VarSsaField(VarSsaField),
- VarAliasedField(VarSsaField),
- Trap(Trap),
-}
-
-impl core::fmt::Debug for MediumLevelILInstruction {
- fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
- write!(
- f,
- "<{} at 0x{:08}>",
- core::any::type_name::<Self>(),
- self.address,
- )
+impl MediumLevelILInstruction {
+ pub(crate) fn new(
+ function: Ref<MediumLevelILFunction>,
+ index: MediumLevelInstructionIndex,
+ ) -> Self {
+ // TODO: If op.sourceOperation == BN_INVALID_OPERAND && op.operation == MLIL_NOP return None
+ let expr_index = unsafe { BNGetMediumLevelILIndexForInstruction(function.handle, index.0) };
+ Self::new_expr(function, MediumLevelInstructionIndex(expr_index))
}
-}
-impl MediumLevelILInstruction {
- pub(crate) fn new(function: Ref<MediumLevelILFunction>, index: usize) -> Self {
- let op = unsafe { BNGetMediumLevelILByIndex(function.handle, index) };
+ // TODO: I need MediumLevelILExpression YESTERDAY!!!!
+ pub(crate) fn new_expr(
+ function: Ref<MediumLevelILFunction>,
+ expr_index: MediumLevelInstructionIndex,
+ ) -> Self {
+ // TODO: If op.sourceOperation == BN_INVALID_OPERAND && op.operation == MLIL_NOP return None
+ let op = unsafe { BNGetMediumLevelILByIndex(function.handle, expr_index.0) };
use BNMediumLevelILOperation::*;
use MediumLevelILInstructionKind as Op;
let kind = match op.operation {
@@ -180,8 +78,8 @@ impl MediumLevelILInstruction {
MLIL_UNIMPL => Op::Unimpl,
MLIL_IF => Op::If(MediumLevelILOperationIf {
condition: op.operands[0] as usize,
- dest_true: op.operands[1],
- dest_false: op.operands[2],
+ dest_true: MediumLevelInstructionIndex(op.operands[1] as usize),
+ dest_false: MediumLevelInstructionIndex(op.operands[2] as usize),
}),
MLIL_FLOAT_CONST => Op::FloatConst(FloatConst {
constant: get_float(op.operands[0], op.size),
@@ -238,7 +136,7 @@ impl MediumLevelILInstruction {
first_operand: op.operands[2] as usize,
}),
MLIL_GOTO => Op::Goto(Goto {
- dest: op.operands[0],
+ dest: MediumLevelInstructionIndex(op.operands[0] as usize),
}),
MLIL_FREE_VAR_SLOT => Op::FreeVarSlot(FreeVarSlot {
dest: get_var(op.operands[0]),
@@ -711,14 +609,14 @@ impl MediumLevelILInstruction {
| MLIL_CALL_OUTPUT_SSA
| MLIL_MEMORY_INTRINSIC_OUTPUT_SSA
| MLIL_MEMORY_INTRINSIC_SSA => {
- unreachable!()
+ unimplemented!()
}
};
Self {
function,
address: op.address,
- index,
+ expr_index,
size: op.size,
kind,
}
@@ -748,9 +646,14 @@ impl MediumLevelILInstruction {
ConstData(op) => Lifted::ConstData(LiftedConstData {
constant_data: ConstantData::new(
- self.function.get_function(),
+ self.function.function(),
RegisterValue {
- state: RegisterValueType::from_raw_value(op.constant_data_kind).unwrap(),
+ // TODO: Replace with a From<u32> for RegisterValueType.
+ // TODO: We might also want to change the type of `op.constant_data_kind`
+ // TODO: To RegisterValueType and do the conversion when creating instruction.
+ state: unsafe {
+ std::mem::transmute::<u32, BNRegisterValueType>(op.constant_data_kind)
+ },
value: op.constant_data_value,
offset: 0,
size: op.size,
@@ -789,6 +692,9 @@ impl MediumLevelILInstruction {
dest: self.lift_operand(op.dest),
targets: OperandIter::new(&*self.function, op.first_operand, op.num_operands)
.pairs()
+ .map(|(addr, instr_idx)| {
+ (addr, MediumLevelInstructionIndex(instr_idx as usize))
+ })
.collect(),
}),
Goto(op) => Lifted::Goto(op),
@@ -906,7 +812,11 @@ impl MediumLevelILInstruction {
output: OperandIter::new(&*self.function, op.first_output, op.num_outputs)
.vars()
.collect(),
- intrinsic: CoreIntrinsic(self.function.get_function().arch().0, op.intrinsic),
+ intrinsic: CoreIntrinsic::new(
+ self.function.function().arch(),
+ IntrinsicId(op.intrinsic),
+ )
+ .expect("Valid intrinsic"),
params: OperandIter::new(&*self.function, op.first_param, op.num_params)
.exprs()
.map(|expr| expr.lift())
@@ -925,7 +835,11 @@ impl MediumLevelILInstruction {
output: OperandIter::new(&*self.function, op.first_output, op.num_outputs)
.ssa_vars()
.collect(),
- intrinsic: CoreIntrinsic(self.function.get_function().arch().0, op.intrinsic),
+ intrinsic: CoreIntrinsic::new(
+ self.function.function().arch(),
+ IntrinsicId(op.intrinsic),
+ )
+ .expect("Valid intrinsic"),
params: OperandIter::new(&*self.function, op.first_param, op.num_params)
.exprs()
.map(|expr| expr.lift())
@@ -1028,7 +942,7 @@ impl MediumLevelILInstruction {
MediumLevelILLiftedInstruction {
function: self.function.clone(),
address: self.address,
- index: self.index,
+ index: self.expr_index,
size: self.size,
kind,
}
@@ -1040,8 +954,8 @@ impl MediumLevelILInstruction {
assert!(unsafe {
BNGetMediumLevelILExprText(
self.function.handle,
- self.function.get_function().arch().0,
- self.index,
+ self.function.function().arch().handle,
+ self.expr_index.0,
&mut tokens,
&mut count,
core::ptr::null_mut(),
@@ -1052,69 +966,78 @@ impl MediumLevelILInstruction {
/// Value of expression if constant or a known value
pub fn value(&self) -> RegisterValue {
- unsafe { BNGetMediumLevelILExprValue(self.function.handle, self.index) }.into()
+ unsafe { BNGetMediumLevelILExprValue(self.function.handle, self.expr_index.0) }.into()
+ }
+
+ /// Returns the [`BasicBlock`] containing the given [`MediumLevelILInstruction`].
+ pub fn basic_block(&self) -> Option<Ref<BasicBlock<MediumLevelILBlock>>> {
+ // TODO: We might be able to .expect this if we guarantee that self.index is valid.
+ self.function.basic_block_containing_index(self.expr_index)
+ }
+
+ /// Possible values of expression using path-sensitive static data flow analysis
+ pub fn possible_values(&self) -> PossibleValueSet {
+ self.possible_values_with_opts(&[])
}
/// 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 {
+ pub fn possible_values_with_opts(&self, options: &[DataFlowQueryOption]) -> PossibleValueSet {
+ let value = unsafe {
BNGetMediumLevelILPossibleExprValues(
self.function.handle,
- self.index,
- options_ptr,
- options_len,
+ self.expr_index.0,
+ options.as_ptr() as *mut _,
+ options.len(),
)
};
- let result = unsafe { PossibleValueSet::from_raw(value) };
- unsafe { BNFreePossibleValueSet(&mut value) }
- result
+ PossibleValueSet::from_owned_raw(value)
}
- pub fn possible_ssa_variable_values(
+ pub fn possible_ssa_variable_values(&self, ssa_var: &SSAVariable) -> PossibleValueSet {
+ self.possible_ssa_variable_values_with_opts(ssa_var, &[])
+ }
+
+ pub fn possible_ssa_variable_values_with_opts(
&self,
- ssa_var: SSAVariable,
- options: Option<&[DataFlowQueryOption]>,
+ ssa_var: &SSAVariable,
+ options: &[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 {
+ let raw_var = BNVariable::from(ssa_var.variable);
+ let value = unsafe {
BNGetMediumLevelILPossibleSSAVarValues(
self.function.handle,
- &ssa_var.variable.raw(),
+ &raw_var,
ssa_var.version,
- self.index,
- options_ptr,
- options_len,
+ self.expr_index.0,
+ options.as_ptr() as *mut _,
+ options.len(),
)
};
- let result = unsafe { PossibleValueSet::from_raw(value) };
- unsafe { BNFreePossibleValueSet(&mut value) }
- result
+ PossibleValueSet::from_owned_raw(value)
}
- /// return the variable version used at this instruction
+ /// Return the ssa version of a [`Variable`] at the given instruction.
pub fn ssa_variable_version(&self, var: Variable) -> SSAVariable {
+ let raw_var = BNVariable::from(var);
let version = unsafe {
BNGetMediumLevelILSSAVarVersionAtILInstruction(
self.function.handle,
- &var.raw(),
- self.index,
+ &raw_var,
+ self.expr_index.0,
)
};
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> {
+ pub fn branch_dependencies(&self) -> Array<BranchDependence> {
let mut count = 0;
let deps = unsafe {
- BNGetAllMediumLevelILBranchDependence(self.function.handle, self.index, &mut count)
+ BNGetAllMediumLevelILBranchDependence(
+ self.function.handle,
+ self.expr_index.0,
+ &mut count,
+ )
};
assert!(!deps.is_null());
unsafe { Array::new(deps, count, self.function.clone()) }
@@ -1122,7 +1045,11 @@ impl MediumLevelILInstruction {
pub fn branch_dependence_at(&self, instruction: MediumLevelILInstruction) -> BranchDependence {
let deps = unsafe {
- BNGetMediumLevelILBranchDependence(self.function.handle, self.index, instruction.index)
+ BNGetMediumLevelILBranchDependence(
+ self.function.handle,
+ self.expr_index.0,
+ instruction.expr_index.0,
+ )
};
BranchDependence {
instruction,
@@ -1133,19 +1060,17 @@ impl MediumLevelILInstruction {
/// 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)
+ BNGetMediumLevelILSSAMemoryVersionAtILInstruction(
+ self.function.handle,
+ self.expr_index.0,
+ )
}
}
/// 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,
- )
- })
+ let result = unsafe { BNGetMediumLevelILExprType(self.function.handle, self.expr_index.0) };
+ (!result.type_.is_null()).then(|| Conf::<Ref<Type>>::from_owned_raw(result))
}
/// Set type of expression
@@ -1154,35 +1079,31 @@ impl MediumLevelILInstruction {
/// 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 set_expr_type<'a, T: Into<Conf<&'a Type>>>(&self, ty: T) {
+ let mut ty: BNTypeWithConfidence = Conf::<&Type>::into_raw(ty.into());
+ unsafe { BNSetMediumLevelILExprType(self.function.handle, self.expr_index.0, &mut ty) }
}
- pub fn variable_for_register(&self, reg_id: u32) -> Variable {
+ pub fn variable_for_register(&self, reg_id: RegisterId) -> Variable {
let result = unsafe {
BNGetMediumLevelILVariableForRegisterAtInstruction(
self.function.handle,
- reg_id,
- self.index,
+ reg_id.0,
+ self.expr_index.0,
)
};
- unsafe { Variable::from_raw(result) }
+ Variable::from(result)
}
- pub fn variable_for_flag(&self, flag_id: u32) -> Variable {
+ pub fn variable_for_flag(&self, flag_id: FlagId) -> Variable {
let result = unsafe {
BNGetMediumLevelILVariableForFlagAtInstruction(
self.function.handle,
- flag_id,
- self.index,
+ flag_id.0,
+ self.expr_index.0,
)
};
- unsafe { Variable::from_raw(result) }
+ Variable::from(result)
}
pub fn variable_for_stack_location(&self, offset: i64) -> Variable {
@@ -1190,134 +1111,134 @@ impl MediumLevelILInstruction {
BNGetMediumLevelILVariableForStackLocationAtInstruction(
self.function.handle,
offset,
- self.index,
+ self.expr_index.0,
)
};
- unsafe { Variable::from_raw(result) }
+ Variable::from(result)
}
- pub fn register_value(&self, reg_id: u32) -> RegisterValue {
+ pub fn register_value(&self, reg_id: RegisterId) -> RegisterValue {
unsafe {
- BNGetMediumLevelILRegisterValueAtInstruction(self.function.handle, reg_id, self.index)
+ BNGetMediumLevelILRegisterValueAtInstruction(
+ self.function.handle,
+ reg_id.0,
+ self.expr_index.0,
+ )
}
.into()
}
- pub fn register_value_after(&self, reg_id: u32) -> RegisterValue {
+ pub fn register_value_after(&self, reg_id: RegisterId) -> RegisterValue {
unsafe {
BNGetMediumLevelILRegisterValueAfterInstruction(
self.function.handle,
- reg_id,
- self.index,
+ reg_id.0,
+ self.expr_index.0,
)
}
.into()
}
- pub fn possible_register_values(
+ pub fn possible_register_values(&self, reg_id: RegisterId) -> PossibleValueSet {
+ self.possible_register_values_with_opts(reg_id, &[])
+ }
+
+ pub fn possible_register_values_with_opts(
&self,
- reg_id: u32,
- options: Option<&[DataFlowQueryOption]>,
+ reg_id: RegisterId,
+ options: &[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 {
+ let value = unsafe {
BNGetMediumLevelILPossibleRegisterValuesAtInstruction(
self.function.handle,
- reg_id,
- self.index,
- options_ptr,
- options_len,
+ reg_id.0,
+ self.expr_index.0,
+ options.as_ptr() as *mut _,
+ options.len(),
)
};
- let result = unsafe { PossibleValueSet::from_raw(value) };
- unsafe { BNFreePossibleValueSet(&mut value) }
- result
+ PossibleValueSet::from_owned_raw(value)
}
- pub fn possible_register_values_after(
+ pub fn possible_register_values_after(&self, reg_id: RegisterId) -> PossibleValueSet {
+ self.possible_register_values_after_with_opts(reg_id, &[])
+ }
+
+ pub fn possible_register_values_after_with_opts(
&self,
- reg_id: u32,
- options: Option<&[DataFlowQueryOption]>,
+ reg_id: RegisterId,
+ options: &[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 {
+ let value = unsafe {
BNGetMediumLevelILPossibleRegisterValuesAfterInstruction(
self.function.handle,
- reg_id,
- self.index,
- options_ptr,
- options_len,
+ reg_id.0,
+ self.expr_index.0,
+ options.as_ptr() as *mut _,
+ options.len(),
)
};
- let result = unsafe { PossibleValueSet::from_raw(value) };
- unsafe { BNFreePossibleValueSet(&mut value) }
- result
+ PossibleValueSet::from_owned_raw(value)
}
- pub fn flag_value(&self, flag_id: u32) -> RegisterValue {
+ pub fn flag_value(&self, flag_id: FlagId) -> RegisterValue {
unsafe {
- BNGetMediumLevelILFlagValueAtInstruction(self.function.handle, flag_id, self.index)
+ BNGetMediumLevelILFlagValueAtInstruction(
+ self.function.handle,
+ flag_id.0,
+ self.expr_index.0,
+ )
}
.into()
}
- pub fn flag_value_after(&self, flag_id: u32) -> RegisterValue {
+ pub fn flag_value_after(&self, flag_id: FlagId) -> RegisterValue {
unsafe {
- BNGetMediumLevelILFlagValueAfterInstruction(self.function.handle, flag_id, self.index)
+ BNGetMediumLevelILFlagValueAfterInstruction(
+ self.function.handle,
+ flag_id.0,
+ self.expr_index.0,
+ )
}
.into()
}
- pub fn possible_flag_values(
+ pub fn possible_flag_values(&self, flag_id: FlagId) -> PossibleValueSet {
+ self.possible_flag_values_with_opts(flag_id, &[])
+ }
+
+ pub fn possible_flag_values_with_opts(
&self,
- flag_id: u32,
- options: Option<&[DataFlowQueryOption]>,
+ flag_id: FlagId,
+ options: &[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 {
+ let value = unsafe {
BNGetMediumLevelILPossibleFlagValuesAtInstruction(
self.function.handle,
- flag_id,
- self.index,
- options_ptr,
- options_len,
+ flag_id.0,
+ self.expr_index.0,
+ options.as_ptr() as *mut _,
+ options.len(),
)
};
- let result = unsafe { PossibleValueSet::from_raw(value) };
- unsafe { BNFreePossibleValueSet(&mut value) }
- result
+ PossibleValueSet::from_owned_raw(value)
}
- pub fn possible_flag_values_after(
+ pub fn possible_flag_values_after_with_opts(
&self,
- flag_id: u32,
- options: Option<&[DataFlowQueryOption]>,
+ flag_id: FlagId,
+ options: &[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 {
+ let value = unsafe {
BNGetMediumLevelILPossibleFlagValuesAfterInstruction(
self.function.handle,
- flag_id,
- self.index,
- options_ptr,
- options_len,
+ flag_id.0,
+ self.expr_index.0,
+ options.as_ptr() as *mut _,
+ options.len(),
)
};
- let result = unsafe { PossibleValueSet::from_raw(value) };
- unsafe { BNFreePossibleValueSet(&mut value) }
- result
+ PossibleValueSet::from_owned_raw(value)
}
pub fn stack_contents(&self, offset: i64, size: usize) -> RegisterValue {
@@ -1326,7 +1247,7 @@ impl MediumLevelILInstruction {
self.function.handle,
offset,
size,
- self.index,
+ self.expr_index.0,
)
}
.into()
@@ -1338,60 +1259,48 @@ impl MediumLevelILInstruction {
self.function.handle,
offset,
size,
- self.index,
+ self.expr_index.0,
)
}
.into()
}
- pub fn possible_stack_contents(
+ pub fn possible_stack_contents_with_opts(
&self,
offset: i64,
size: usize,
- options: Option<&[DataFlowQueryOption]>,
+ options: &[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 {
+ let value = unsafe {
BNGetMediumLevelILPossibleStackContentsAtInstruction(
self.function.handle,
offset,
size,
- self.index,
- options_ptr,
- options_len,
+ self.expr_index.0,
+ options.as_ptr() as *mut _,
+ options.len(),
)
};
- let result = unsafe { PossibleValueSet::from_raw(value) };
- unsafe { BNFreePossibleValueSet(&mut value) }
- result
+ PossibleValueSet::from_owned_raw(value)
}
- pub fn possible_stack_contents_after(
+ pub fn possible_stack_contents_after_with_opts(
&self,
offset: i64,
size: usize,
- options: Option<&[DataFlowQueryOption]>,
+ options: &[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 {
+ let value = unsafe {
BNGetMediumLevelILPossibleStackContentsAfterInstruction(
self.function.handle,
offset,
size,
- self.index,
- options_ptr,
- options_len,
+ self.expr_index.0,
+ options.as_ptr() as *mut _,
+ options.len(),
)
};
- let result = unsafe { PossibleValueSet::from_raw(value) };
- unsafe { BNFreePossibleValueSet(&mut value) }
- result
+ PossibleValueSet::from_owned_raw(value)
}
/// Gets the unique variable for a definition instruction. This unique variable can be passed
@@ -1399,25 +1308,36 @@ impl MediumLevelILInstruction {
/// assigned variable to query.
///
/// * `var` - variable to query
- pub fn split_var_for_definition(&self, var: Variable) -> Variable {
+ pub fn split_var_for_definition(&self, var: &Variable) -> Variable {
+ let raw_var = BNVariable::from(var);
let index = unsafe {
BNGetDefaultIndexForMediumLevelILVariableDefinition(
self.function.handle,
- &var.raw(),
- self.index,
+ &raw_var,
+ self.expr_index.0,
)
};
- Variable::new(var.t, index, var.storage)
+ Variable::new(var.ty, 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)
+ 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))
+ // TODO: UGH, if your gonna call it expr_idx, call the instruction and expression!!!!!
+ // TODO: We dont even need to say instruction in the type!
+ // TODO: IF you want to have an instruction type, there needs to be a separate expression type
+ // TODO: See the lowlevelil module.
+ let expr_idx_is_really_instr_idx = MediumLevelInstructionIndex(expr_idx);
+ // TODO: See the comment in the unchecked function, ugh, i hate this..
+ let operand_instr = self
+ .function
+ .instruction_from_expr_index(expr_idx_is_really_instr_idx)
+ .unwrap();
+ Box::new(operand_instr.lift())
}
fn lift_binary_op(&self, op: BinaryOp) -> LiftedBinaryOp {
@@ -1489,6 +1409,17 @@ impl MediumLevelILInstruction {
}
}
+impl Debug for MediumLevelILInstruction {
+ fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ f.debug_struct("MediumLevelILInstruction")
+ .field("address", &self.address)
+ .field("index", &self.expr_index)
+ .field("size", &self.size)
+ .field("kind", &self.kind)
+ .finish()
+ }
+}
+
impl CoreArrayProvider for MediumLevelILInstruction {
type Raw = usize;
type Context = Ref<MediumLevelILFunction>;
@@ -1501,10 +1432,148 @@ unsafe impl CoreArrayProviderInner for MediumLevelILInstruction {
}
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
- context.instruction_from_idx(*raw)
+ // TODO: This needs to be tested!!!!
+ // TODO: What if this does not need to be mapped!!!!
+ context
+ .instruction_from_index(MediumLevelInstructionIndex(*raw))
+ .unwrap()
}
}
+#[derive(Debug, Copy, Clone)]
+pub enum MediumLevelILInstructionKind {
+ Nop,
+ Noret,
+ Bp,
+ Undef,
+ Unimpl,
+ If(MediumLevelILOperationIf),
+ FloatConst(FloatConst),
+ Const(Constant),
+ ConstPtr(Constant),
+ Import(Constant),
+ ExternPtr(ExternPtr),
+ ConstData(ConstData),
+ Jump(Jump),
+ RetHint(Jump),
+ StoreSsa(StoreSsa),
+ StoreStructSsa(StoreStructSsa),
+ StoreStruct(StoreStruct),
+ Store(Store),
+ JumpTo(JumpTo),
+ Goto(Goto),
+ FreeVarSlot(FreeVarSlot),
+ SetVarField(SetVarField),
+ SetVar(SetVar),
+ FreeVarSlotSsa(FreeVarSlotSsa),
+ SetVarSsaField(SetVarSsaField),
+ SetVarAliasedField(SetVarSsaField),
+ SetVarAliased(SetVarAliased),
+ SetVarSsa(SetVarSsa),
+ VarPhi(VarPhi),
+ MemPhi(MemPhi),
+ VarSplit(VarSplit),
+ SetVarSplit(SetVarSplit),
+ VarSplitSsa(VarSplitSsa),
+ SetVarSplitSsa(SetVarSplitSsa),
+ Add(BinaryOp),
+ Sub(BinaryOp),
+ And(BinaryOp),
+ Or(BinaryOp),
+ Xor(BinaryOp),
+ Lsl(BinaryOp),
+ Lsr(BinaryOp),
+ Asr(BinaryOp),
+ Rol(BinaryOp),
+ Ror(BinaryOp),
+ Mul(BinaryOp),
+ MuluDp(BinaryOp),
+ MulsDp(BinaryOp),
+ Divu(BinaryOp),
+ DivuDp(BinaryOp),
+ Divs(BinaryOp),
+ DivsDp(BinaryOp),
+ Modu(BinaryOp),
+ ModuDp(BinaryOp),
+ Mods(BinaryOp),
+ ModsDp(BinaryOp),
+ CmpE(BinaryOp),
+ CmpNe(BinaryOp),
+ CmpSlt(BinaryOp),
+ CmpUlt(BinaryOp),
+ CmpSle(BinaryOp),
+ CmpUle(BinaryOp),
+ CmpSge(BinaryOp),
+ CmpUge(BinaryOp),
+ CmpSgt(BinaryOp),
+ CmpUgt(BinaryOp),
+ TestBit(BinaryOp),
+ AddOverflow(BinaryOp),
+ FcmpE(BinaryOp),
+ FcmpNe(BinaryOp),
+ FcmpLt(BinaryOp),
+ FcmpLe(BinaryOp),
+ FcmpGe(BinaryOp),
+ FcmpGt(BinaryOp),
+ FcmpO(BinaryOp),
+ FcmpUo(BinaryOp),
+ Fadd(BinaryOp),
+ Fsub(BinaryOp),
+ Fmul(BinaryOp),
+ Fdiv(BinaryOp),
+ Adc(BinaryOpCarry),
+ Sbb(BinaryOpCarry),
+ Rlc(BinaryOpCarry),
+ Rrc(BinaryOpCarry),
+ Call(Call),
+ Tailcall(Call),
+ Syscall(Syscall),
+ Intrinsic(Intrinsic),
+ IntrinsicSsa(IntrinsicSsa),
+ CallSsa(CallSsa),
+ TailcallSsa(CallSsa),
+ CallUntypedSsa(CallUntypedSsa),
+ TailcallUntypedSsa(CallUntypedSsa),
+ SyscallSsa(SyscallSsa),
+ SyscallUntypedSsa(SyscallUntypedSsa),
+ CallUntyped(CallUntyped),
+ TailcallUntyped(CallUntyped),
+ SyscallUntyped(SyscallUntyped),
+ SeparateParamList(SeparateParamList),
+ SharedParamSlot(SharedParamSlot),
+ Neg(UnaryOp),
+ Not(UnaryOp),
+ Sx(UnaryOp),
+ Zx(UnaryOp),
+ LowPart(UnaryOp),
+ BoolToInt(UnaryOp),
+ UnimplMem(UnaryOp),
+ Fsqrt(UnaryOp),
+ Fneg(UnaryOp),
+ Fabs(UnaryOp),
+ FloatToInt(UnaryOp),
+ IntToFloat(UnaryOp),
+ FloatConv(UnaryOp),
+ RoundToInt(UnaryOp),
+ Floor(UnaryOp),
+ Ceil(UnaryOp),
+ Ftrunc(UnaryOp),
+ Load(UnaryOp),
+ LoadStruct(LoadStruct),
+ LoadStructSsa(LoadStructSsa),
+ LoadSsa(LoadSsa),
+ Ret(Ret),
+ Var(Var),
+ AddressOf(Var),
+ VarField(Field),
+ AddressOfField(Field),
+ VarSsa(VarSsa),
+ VarAliased(VarSsa),
+ VarSsaField(VarSsaField),
+ VarAliasedField(VarSsaField),
+ Trap(Trap),
+}
+
fn get_float(value: u64, size: usize) -> f64 {
match size {
4 => f32::from_bits(value as u32) as f64,
@@ -1519,7 +1588,7 @@ fn get_raw_operation(function: &MediumLevelILFunction, idx: usize) -> BNMediumLe
}
fn get_var(id: u64) -> Variable {
- unsafe { Variable::from_identifier(id) }
+ Variable::from_identifier(id)
}
fn get_var_ssa(id: u64, version: usize) -> SSAVariable {
@@ -1578,7 +1647,10 @@ unsafe impl CoreArrayProviderInner for BranchDependence {
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
Self {
- instruction: MediumLevelILInstruction::new(context.clone(), raw.branch),
+ instruction: MediumLevelILInstruction::new(
+ context.clone(),
+ MediumLevelInstructionIndex(raw.branch),
+ ),
dependence: raw.dependence,
}
}
diff --git a/rust/src/mlil/lift.rs b/rust/src/medium_level_il/lift.rs
index 7a5e159b..d6cceb18 100644
--- a/rust/src/mlil/lift.rs
+++ b/rust/src/medium_level_il/lift.rs
@@ -1,11 +1,10 @@
use std::collections::BTreeMap;
+use super::operation::*;
+use super::{MediumLevelILFunction, MediumLevelInstructionIndex};
use crate::architecture::CoreIntrinsic;
use crate::rc::Ref;
-use crate::types::{ConstantData, SSAVariable, Variable};
-
-use super::operation::*;
-use super::MediumLevelILFunction;
+use crate::variable::{ConstantData, SSAVariable, Variable};
#[derive(Clone)]
pub enum MediumLevelILLiftedOperand {
@@ -16,18 +15,19 @@ pub enum MediumLevelILLiftedOperand {
Float(f64),
Int(u64),
IntList(Vec<u64>),
- TargetMap(BTreeMap<u64, u64>),
+ TargetMap(BTreeMap<u64, MediumLevelInstructionIndex>),
Var(Variable),
VarList(Vec<Variable>),
VarSsa(SSAVariable),
VarSsaList(Vec<SSAVariable>),
+ InstructionIndex(MediumLevelInstructionIndex),
}
#[derive(Clone, Debug, PartialEq)]
pub struct MediumLevelILLiftedInstruction {
pub function: Ref<MediumLevelILFunction>,
pub address: u64,
- pub index: usize,
+ pub index: MediumLevelInstructionIndex,
pub size: usize,
pub kind: MediumLevelILLiftedInstructionKind,
}
@@ -310,8 +310,8 @@ impl MediumLevelILLiftedInstruction {
Nop | Noret | Bp | Undef | Unimpl => vec![],
If(op) => vec![
("condition", Operand::Expr(*op.condition.clone())),
- ("dest_true", Operand::Int(op.dest_true)),
- ("dest_false", Operand::Int(op.dest_false)),
+ ("dest_true", Operand::InstructionIndex(op.dest_true)),
+ ("dest_false", Operand::InstructionIndex(op.dest_false)),
],
FloatConst(op) => vec![("constant", Operand::Float(op.constant))],
Const(op) | ConstPtr(op) | Import(op) => vec![("constant", Operand::Int(op.constant))],
@@ -350,7 +350,7 @@ impl MediumLevelILLiftedInstruction {
("dest", Operand::Expr(*op.dest.clone())),
("targets", Operand::TargetMap(op.targets.clone())),
],
- Goto(op) => vec![("dest", Operand::Int(op.dest))],
+ Goto(op) => vec![("dest", Operand::InstructionIndex(op.dest))],
FreeVarSlot(op) => vec![("dest", Operand::Var(op.dest))],
SetVarField(op) => vec![
("dest", Operand::Var(op.dest)),
diff --git a/rust/src/mlil/operation.rs b/rust/src/medium_level_il/operation.rs
index 278d1edb..e11f59c8 100644
--- a/rust/src/mlil/operation.rs
+++ b/rust/src/medium_level_il/operation.rs
@@ -1,21 +1,20 @@
+use super::{MediumLevelILLiftedInstruction, MediumLevelInstructionIndex};
+use crate::architecture::CoreIntrinsic;
+use crate::variable::{ConstantData, SSAVariable, Variable};
use std::collections::BTreeMap;
-use crate::{architecture::CoreIntrinsic, types::{ConstantData, SSAVariable, Variable}};
-
-use super::MediumLevelILLiftedInstruction;
-
// IF
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct MediumLevelILOperationIf {
pub condition: usize,
- pub dest_true: u64,
- pub dest_false: u64,
+ pub dest_true: MediumLevelInstructionIndex,
+ pub dest_false: MediumLevelInstructionIndex,
}
#[derive(Clone, Debug, PartialEq)]
pub struct LiftedIf {
pub condition: Box<MediumLevelILLiftedInstruction>,
- pub dest_true: u64,
- pub dest_false: u64,
+ pub dest_true: MediumLevelInstructionIndex,
+ pub dest_false: MediumLevelInstructionIndex,
}
// FLOAT_CONST
@@ -38,7 +37,7 @@ pub struct ExternPtr {
}
// CONST_DATA
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct ConstData {
pub constant_data_kind: u32,
pub constant_data_value: i64,
@@ -50,7 +49,7 @@ pub struct LiftedConstData {
}
// JUMP, RET_HINT
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct Jump {
pub dest: usize,
}
@@ -60,7 +59,7 @@ pub struct LiftedJump {
}
// STORE_SSA
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct StoreSsa {
pub dest: usize,
pub dest_memory: u64,
@@ -76,7 +75,7 @@ pub struct LiftedStoreSsa {
}
// STORE_STRUCT_SSA
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct StoreStructSsa {
pub dest: usize,
pub offset: u64,
@@ -94,7 +93,7 @@ pub struct LiftedStoreStructSsa {
}
// STORE_STRUCT
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct StoreStruct {
pub dest: usize,
pub offset: u64,
@@ -108,7 +107,7 @@ pub struct LiftedStoreStruct {
}
// STORE
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct Store {
pub dest: usize,
pub src: usize,
@@ -120,7 +119,7 @@ pub struct LiftedStore {
}
// JUMP_TO
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct JumpTo {
pub dest: usize,
pub first_operand: usize,
@@ -129,13 +128,13 @@ pub struct JumpTo {
#[derive(Clone, Debug, PartialEq)]
pub struct LiftedJumpTo {
pub dest: Box<MediumLevelILLiftedInstruction>,
- pub targets: BTreeMap<u64, u64>,
+ pub targets: BTreeMap<u64, MediumLevelInstructionIndex>,
}
// GOTO
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub struct Goto {
- pub dest: u64,
+ pub dest: MediumLevelInstructionIndex,
}
// FREE_VAR_SLOT
@@ -145,7 +144,7 @@ pub struct FreeVarSlot {
}
// SET_VAR_FIELD
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct SetVarField {
pub dest: Variable,
pub offset: u64,
@@ -159,9 +158,10 @@ pub struct LiftedSetVarField {
}
// SET_VAR
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct SetVar {
pub dest: Variable,
+ // TODO: Expression?
pub src: usize,
}
#[derive(Clone, Debug, PartialEq)]
@@ -178,7 +178,7 @@ pub struct FreeVarSlotSsa {
}
// SET_VAR_SSA_FIELD, SET_VAR_ALIASED_FIELD
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct SetVarSsaField {
pub dest: SSAVariable,
pub prev: SSAVariable,
@@ -194,7 +194,7 @@ pub struct LiftedSetVarSsaField {
}
// SET_VAR_ALIASED
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct SetVarAliased {
pub dest: SSAVariable,
pub prev: SSAVariable,
@@ -208,7 +208,7 @@ pub struct LiftedSetVarAliased {
}
// SET_VAR_SSA
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct SetVarSsa {
pub dest: SSAVariable,
pub src: usize,
@@ -220,7 +220,7 @@ pub struct LiftedSetVarSsa {
}
// VAR_PHI
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct VarPhi {
pub dest: SSAVariable,
pub first_operand: usize,
@@ -233,7 +233,7 @@ pub struct LiftedVarPhi {
}
// MEM_PHI
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct MemPhi {
pub dest_memory: u64,
pub first_operand: usize,
@@ -253,7 +253,7 @@ pub struct VarSplit {
}
// SET_VAR_SPLIT
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct SetVarSplit {
pub high: Variable,
pub low: Variable,
@@ -274,7 +274,7 @@ pub struct VarSplitSsa {
}
// SET_VAR_SPLIT_SSA
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct SetVarSplitSsa {
pub high: SSAVariable,
pub low: SSAVariable,
@@ -288,7 +288,7 @@ pub struct LiftedSetVarSplitSsa {
}
// ADD, SUB, AND, OR, XOR, LSL, LSR, ASR, ROL, ROR, MUL, MULU_DP, MULS_DP, DIVU, DIVU_DP, DIVS, DIVS_DP, MODU, MODU_DP, MODS, MODS_DP, CMP_E, CMP_NE, CMP_SLT, CMP_ULT, CMP_SLE, CMP_ULE, CMP_SGE, CMP_UGE, CMP_SGT, CMP_UGT, TEST_BIT, ADD_OVERFLOW, FCMP_E, FCMP_NE, FCMP_LT, FCMP_LE, FCMP_GE, FCMP_GT, FCMP_O, FCMP_UO, FADD, FSUB, FMUL, FDIV
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct BinaryOp {
pub left: usize,
pub right: usize,
@@ -300,7 +300,7 @@ pub struct LiftedBinaryOp {
}
// ADC, SBB, RLC, RRC
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct BinaryOpCarry {
pub left: usize,
pub right: usize,
@@ -314,7 +314,7 @@ pub struct LiftedBinaryOpCarry {
}
// CALL, TAILCALL
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct Call {
pub first_output: usize,
pub num_outputs: usize,
@@ -330,7 +330,7 @@ pub struct LiftedCall {
}
// SYSCALL
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct Syscall {
pub first_output: usize,
pub num_outputs: usize,
@@ -344,7 +344,7 @@ pub struct LiftedSyscallCall {
}
// INTRINSIC
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct Intrinsic {
pub first_output: usize,
pub num_outputs: usize,
@@ -360,7 +360,7 @@ pub struct LiftedIntrinsic {
}
// INTRINSIC_SSA
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct IntrinsicSsa {
pub first_output: usize,
pub num_outputs: usize,
@@ -376,7 +376,7 @@ pub struct LiftedIntrinsicSsa {
}
// CALL_SSA, TAILCALL_SSA
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct CallSsa {
pub output: usize,
pub dest: usize,
@@ -393,7 +393,7 @@ pub struct LiftedCallSsa {
}
// CALL_UNTYPED_SSA, TAILCALL_UNTYPED_SSA
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct CallUntypedSsa {
pub output: usize,
pub dest: usize,
@@ -409,7 +409,7 @@ pub struct LiftedCallUntypedSsa {
}
// SYSCALL_SSA
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct SyscallSsa {
pub output: usize,
pub first_param: usize,
@@ -424,7 +424,7 @@ pub struct LiftedSyscallSsa {
}
// SYSCALL_UNTYPED_SSA
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct SyscallUntypedSsa {
pub output: usize,
pub params: usize,
@@ -438,7 +438,7 @@ pub struct LiftedSyscallUntypedSsa {
}
// CALL_UNTYPED, TAILCALL_UNTYPED
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct CallUntyped {
pub output: usize,
pub dest: usize,
@@ -454,7 +454,7 @@ pub struct LiftedCallUntyped {
}
// SYSCALL_UNTYPED
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct SyscallUntyped {
pub output: usize,
pub params: usize,
@@ -468,7 +468,7 @@ pub struct LiftedSyscallUntyped {
}
// NEG, NOT, SX, ZX, LOW_PART, BOOL_TO_INT, UNIMPL_MEM, FSQRT, FNEG, FABS, FLOAT_TO_INT, INT_TO_FLOAT, FLOAT_CONV, ROUND_TO_INT, FLOOR, CEIL, FTRUNC, LOAD
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct UnaryOp {
pub src: usize,
}
@@ -478,7 +478,7 @@ pub struct LiftedUnaryOp {
}
// LOAD_STRUCT
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct LoadStruct {
pub src: usize,
pub offset: u64,
@@ -490,7 +490,7 @@ pub struct LiftedLoadStruct {
}
// LOAD_STRUCT_SSA
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct LoadStructSsa {
pub src: usize,
pub offset: u64,
@@ -504,7 +504,7 @@ pub struct LiftedLoadStructSsa {
}
// LOAD_SSA
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct LoadSsa {
pub src: usize,
pub src_memory: u64,
@@ -516,7 +516,7 @@ pub struct LiftedLoadSsa {
}
// RET
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct Ret {
pub first_operand: usize,
pub num_operands: usize,
@@ -527,7 +527,7 @@ pub struct LiftedRet {
}
// SEPARATE_PARAM_LIST
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct SeparateParamList {
pub first_param: usize,
pub num_params: usize,
@@ -538,7 +538,7 @@ pub struct LiftedSeparateParamList {
}
// SHARED_PARAM_SLOT
-#[derive(Copy, Clone)]
+#[derive(Debug, Copy, Clone)]
pub struct SharedParamSlot {
pub first_param: usize,
pub num_params: usize,
diff --git a/rust/src/metadata.rs b/rust/src/metadata.rs
index 9eeff8fa..d18b6339 100644
--- a/rust/src/metadata.rs
+++ b/rust/src/metadata.rs
@@ -350,11 +350,13 @@ impl CoreArrayProvider for Metadata {
}
unsafe impl CoreArrayProviderInner for Metadata {
- unsafe fn free(raw: *mut *mut BNMetadata, _count: usize, _context: &()) {
+ unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
+ // TODO: `count` is not passed into the core here...
BNFreeMetadataArray(raw);
}
- unsafe fn wrap_raw<'a>(raw: &'a *mut BNMetadata, context: &'a ()) -> Self::Wrapped<'a> {
- Guard::new(Metadata::from_raw(*raw), context)
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
+ Guard::new(Self::from_raw(*raw), context)
}
}
diff --git a/rust/src/mlil/function.rs b/rust/src/mlil/function.rs
deleted file mode 100644
index 928f1b6a..00000000
--- a/rust/src/mlil/function.rs
+++ /dev/null
@@ -1,727 +0,0 @@
-use core::hash::{Hash, Hasher};
-use std::ffi::c_char;
-
-use binaryninjacore_sys::*;
-
-use crate::architecture::CoreArchitecture;
-use crate::basicblock::BasicBlock;
-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};
-
-pub struct MediumLevelILFunction {
- pub(crate) handle: *mut BNMediumLevelILFunction,
-}
-
-unsafe impl Send for MediumLevelILFunction {}
-unsafe impl Sync for MediumLevelILFunction {}
-
-impl Eq for MediumLevelILFunction {}
-impl PartialEq for MediumLevelILFunction {
- fn eq(&self, rhs: &Self) -> bool {
- self.get_function().eq(&rhs.get_function())
- }
-}
-
-impl Hash for MediumLevelILFunction {
- fn hash<H: Hasher>(&self, state: &mut H) {
- self.get_function().hash(state)
- }
-}
-
-impl MediumLevelILFunction {
- pub(crate) unsafe fn ref_from_raw(handle: *mut BNMediumLevelILFunction) -> Ref<Self> {
- debug_assert!(!handle.is_null());
-
- Self { handle }.to_owned()
- }
-
- pub fn instruction_at<L: Into<Location>>(&self, loc: L) -> Option<MediumLevelILInstruction> {
- let loc: Location = loc.into();
- let arch_handle = loc.arch.unwrap();
-
- let expr_idx =
- unsafe { BNMediumLevelILGetInstructionStart(self.handle, arch_handle.0, loc.addr) };
-
- if expr_idx >= self.instruction_count() {
- None
- } else {
- Some(MediumLevelILInstruction::new(self.to_owned(), expr_idx))
- }
- }
-
- pub fn instruction_from_idx(&self, expr_idx: usize) -> MediumLevelILInstruction {
- MediumLevelILInstruction::new(self.to_owned(), expr_idx)
- }
-
- pub fn lifted_instruction_from_idx(&self, expr_idx: usize) -> MediumLevelILLiftedInstruction {
- 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) }
- }
-
- pub fn ssa_form(&self) -> MediumLevelILFunction {
- let ssa = unsafe { BNGetMediumLevelILSSAForm(self.handle) };
- assert!(!ssa.is_null());
- MediumLevelILFunction { handle: ssa }
- }
-
- pub fn get_function(&self) -> Ref<Function> {
- unsafe {
- let func = BNGetMediumLevelILOwnerFunction(self.handle);
- Function::from_raw(func)
- }
- }
-
- pub fn basic_blocks(&self) -> Array<BasicBlock<MediumLevelILBlock>> {
- let mut count = 0;
- let blocks = unsafe { BNGetMediumLevelILBasicBlockList(self.handle, &mut count) };
- let context = MediumLevelILBlock {
- function: self.to_owned(),
- };
-
- 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 {
- type Owned = Ref<Self>;
-
- fn to_owned(&self) -> Self::Owned {
- unsafe { RefCountable::inc_ref(self) }
- }
-}
-
-unsafe impl RefCountable for MediumLevelILFunction {
- unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
- Ref::new(Self {
- handle: BNNewMediumLevelILFunctionReference(handle.handle),
- })
- }
-
- unsafe fn dec_ref(handle: &Self) {
- BNFreeMediumLevelILFunction(handle.handle);
- }
-}
-
-impl core::fmt::Debug for MediumLevelILFunction {
- fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
- 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()),
- }
- }
-}
diff --git a/rust/src/operand_iter.rs b/rust/src/operand_iter.rs
index 0d24fd49..fcbf5fad 100644
--- a/rust/src/operand_iter.rs
+++ b/rust/src/operand_iter.rs
@@ -3,27 +3,37 @@ use binaryninjacore_sys::BNGetMediumLevelILByIndex;
use binaryninjacore_sys::BNHighLevelILOperation;
use binaryninjacore_sys::BNMediumLevelILOperation;
-use crate::hlil::{HighLevelILFunction, HighLevelILInstruction};
-use crate::mlil::{MediumLevelILFunction, MediumLevelILInstruction};
+use crate::high_level_il::{
+ HighLevelILFunction, HighLevelILInstruction, HighLevelInstructionIndex,
+};
+use crate::medium_level_il::{
+ MediumLevelILFunction, MediumLevelILInstruction, MediumLevelInstructionIndex,
+};
use crate::rc::{Ref, RefCountable};
-use crate::types::{SSAVariable, Variable};
+use crate::variable::{SSAVariable, Variable};
+
+// TODO: This code needs to go away IMO, we have the facilities to do this for each IL already!
pub trait ILFunction {
type Instruction;
+ type InstructionIndex: From<u64>;
- fn il_instruction_from_idx(&self, expr_idx: usize) -> Self::Instruction;
- fn operands_from_idx(&self, expr_idx: usize) -> [u64; 5];
+ // TODO Actually this is il expression from index!
+ fn il_instruction_from_index(&self, instr_index: Self::InstructionIndex) -> Self::Instruction;
+ fn operands_from_index(&self, instr_index: Self::InstructionIndex) -> [u64; 5];
}
impl ILFunction for MediumLevelILFunction {
type Instruction = MediumLevelILInstruction;
+ type InstructionIndex = MediumLevelInstructionIndex;
- fn il_instruction_from_idx(&self, expr_idx: usize) -> Self::Instruction {
- self.instruction_from_idx(expr_idx)
+ fn il_instruction_from_index(&self, instr_index: Self::InstructionIndex) -> Self::Instruction {
+ self.instruction_from_expr_index(instr_index).unwrap()
}
- fn operands_from_idx(&self, expr_idx: usize) -> [u64; 5] {
- let node = unsafe { BNGetMediumLevelILByIndex(self.handle, expr_idx) };
+ fn operands_from_index(&self, instr_index: Self::InstructionIndex) -> [u64; 5] {
+ // TODO: WTF?!?!?!
+ let node = unsafe { BNGetMediumLevelILByIndex(self.handle, instr_index.0) };
assert_eq!(node.operation, BNMediumLevelILOperation::MLIL_UNDEF);
node.operands
}
@@ -31,13 +41,14 @@ impl ILFunction for MediumLevelILFunction {
impl ILFunction for HighLevelILFunction {
type Instruction = HighLevelILInstruction;
+ type InstructionIndex = HighLevelInstructionIndex;
- fn il_instruction_from_idx(&self, expr_idx: usize) -> Self::Instruction {
- self.instruction_from_idx(expr_idx)
+ fn il_instruction_from_index(&self, instr_index: Self::InstructionIndex) -> Self::Instruction {
+ self.instruction_from_expr_index(instr_index).unwrap()
}
- fn operands_from_idx(&self, expr_idx: usize) -> [u64; 5] {
- let node = unsafe { BNGetHighLevelILByIndex(self.handle, expr_idx, self.full_ast) };
+ fn operands_from_index(&self, instr_index: Self::InstructionIndex) -> [u64; 5] {
+ let node = unsafe { BNGetHighLevelILByIndex(self.handle, instr_index.0, self.full_ast) };
assert_eq!(node.operation, BNHighLevelILOperation::HLIL_UNDEF);
node.operands
}
@@ -82,6 +93,7 @@ impl<F: ILFunction + RefCountable> OperandIter<F> {
impl<F: ILFunction + RefCountable> Iterator for OperandIter<F> {
type Item = u64;
+
fn next(&mut self) -> Option<Self::Item> {
if let Some(item) = self.current_iter.next() {
self.remaining -= 1;
@@ -89,7 +101,8 @@ impl<F: ILFunction + RefCountable> Iterator for OperandIter<F> {
} else {
// Will short-circuit and return `None` once iter is exhausted
let iter_idx = self.next_iter_idx?;
- let operands = self.function.operands_from_idx(iter_idx);
+ let iter_idx = F::InstructionIndex::from(iter_idx as u64);
+ let operands = self.function.operands_from_index(iter_idx);
let next = if self.remaining > 4 {
self.next_iter_idx = Some(operands[4] as usize);
@@ -104,6 +117,7 @@ impl<F: ILFunction + RefCountable> Iterator for OperandIter<F> {
}
}
}
+
impl<F: ILFunction + RefCountable> ExactSizeIterator for OperandIter<F> {
fn len(&self) -> usize {
self.remaining + self.current_iter.len()
@@ -145,6 +159,7 @@ impl Iterator for OperandIterInner {
}
}
}
+
impl ExactSizeIterator for OperandIterInner {
fn len(&self) -> usize {
4 - self.idx
@@ -152,6 +167,7 @@ impl ExactSizeIterator for OperandIterInner {
}
pub struct OperandPairIter<F: ILFunction + RefCountable>(OperandIter<F>);
+
impl<F: ILFunction + RefCountable> Iterator for OperandPairIter<F> {
type Item = (u64, u64);
@@ -168,15 +184,18 @@ impl<F: ILFunction + RefCountable> ExactSizeIterator for OperandPairIter<F> {
}
pub struct OperandExprIter<F: ILFunction + RefCountable>(OperandIter<F>);
+
impl<F: ILFunction + RefCountable> Iterator for OperandExprIter<F> {
type Item = F::Instruction;
fn next(&mut self) -> Option<Self::Item> {
self.0
.next()
- .map(|idx| self.0.function.il_instruction_from_idx(idx as usize))
+ .map(F::InstructionIndex::from)
+ .map(|idx| self.0.function.il_instruction_from_index(idx))
}
}
+
impl<F: ILFunction + RefCountable> ExactSizeIterator for OperandExprIter<F> {
fn len(&self) -> usize {
self.0.len()
@@ -184,11 +203,12 @@ impl<F: ILFunction + RefCountable> ExactSizeIterator for OperandExprIter<F> {
}
pub struct OperandVarIter<F: ILFunction + RefCountable>(OperandIter<F>);
+
impl<F: ILFunction + RefCountable> Iterator for OperandVarIter<F> {
type Item = Variable;
fn next(&mut self) -> Option<Self::Item> {
- self.0.next().map(get_var)
+ self.0.next().map(Variable::from_identifier)
}
}
impl<F: ILFunction + RefCountable> ExactSizeIterator for OperandVarIter<F> {
@@ -198,25 +218,20 @@ impl<F: ILFunction + RefCountable> ExactSizeIterator for OperandVarIter<F> {
}
pub struct OperandSSAVarIter<F: ILFunction + RefCountable>(OperandPairIter<F>);
+
impl<F: ILFunction + RefCountable> Iterator for OperandSSAVarIter<F> {
type Item = SSAVariable;
fn next(&mut self) -> Option<Self::Item> {
- self.0
- .next()
- .map(|(id, version)| get_var_ssa(id, version as usize))
+ self.0.next().map(|(id, version)| {
+ let var = Variable::from_identifier(id);
+ SSAVariable::new(var, version as _)
+ })
}
}
+
impl<F: ILFunction + RefCountable> ExactSizeIterator for OperandSSAVarIter<F> {
fn len(&self) -> usize {
self.0.len()
}
}
-
-pub fn get_var(id: u64) -> Variable {
- unsafe { Variable::from_identifier(id) }
-}
-
-pub fn get_var_ssa(id: u64, version: usize) -> SSAVariable {
- SSAVariable::new(get_var(id), version)
-}
diff --git a/rust/src/platform.rs b/rust/src/platform.rs
index 4e31a0a2..62c61297 100644
--- a/rust/src/platform.rs
+++ b/rust/src/platform.rs
@@ -14,18 +14,20 @@
//! Contains all information related to the execution environment of the binary, mainly the calling conventions used
-use std::{borrow::Borrow, collections::HashMap, os::raw, path::Path, ptr, slice};
-
-use binaryninjacore_sys::*;
-
+use crate::type_container::TypeContainer;
+use crate::type_parser::{TypeParserError, TypeParserErrorSeverity, TypeParserResult};
use crate::{
architecture::{Architecture, CoreArchitecture},
- callingconvention::CallingConvention,
+ calling_convention::CoreCallingConvention,
rc::*,
string::*,
- typelibrary::TypeLibrary,
- types::{QualifiedName, QualifiedNameAndType, Type},
+ type_library::TypeLibrary,
+ types::QualifiedNameAndType,
};
+use binaryninjacore_sys::*;
+use std::fmt::Debug;
+use std::ptr::NonNull;
+use std::{borrow::Borrow, ffi, ptr};
#[derive(PartialEq, Eq, Hash)]
pub struct Platform {
@@ -37,7 +39,7 @@ unsafe impl Sync for Platform {}
macro_rules! cc_func {
($get_name:ident, $get_api:ident, $set_name:ident, $set_api:ident) => {
- pub fn $get_name(&self) -> Option<Ref<CallingConvention<CoreArchitecture>>> {
+ pub fn $get_name(&self) -> Option<Ref<CoreCallingConvention>> {
let arch = self.arch();
unsafe {
@@ -46,16 +48,19 @@ macro_rules! cc_func {
if cc.is_null() {
None
} else {
- Some(CallingConvention::ref_from_raw(cc, arch))
+ Some(CoreCallingConvention::ref_from_raw(
+ cc,
+ arch.as_ref().handle(),
+ ))
}
}
}
- pub fn $set_name<A: Architecture>(&self, cc: &CallingConvention<A>) {
+ pub fn $set_name(&self, cc: &CoreCallingConvention) {
let arch = self.arch();
assert!(
- cc.arch_handle.borrow().as_ref().0 == arch.0,
+ cc.arch_handle.borrow().as_ref().handle == arch.handle,
"use of calling convention with non-matching Platform architecture!"
);
@@ -67,9 +72,13 @@ macro_rules! cc_func {
}
impl Platform {
- pub(crate) unsafe fn ref_from_raw(handle: *mut BNPlatform) -> Ref<Self> {
+ pub(crate) unsafe fn from_raw(handle: *mut BNPlatform) -> Self {
debug_assert!(!handle.is_null());
+ Self { handle }
+ }
+ pub(crate) unsafe fn ref_from_raw(handle: *mut BNPlatform) -> Ref<Self> {
+ debug_assert!(!handle.is_null());
Ref::new(Self { handle })
}
@@ -98,7 +107,7 @@ impl Platform {
pub fn list_by_arch(arch: &CoreArchitecture) -> Array<Platform> {
unsafe {
let mut count = 0;
- let handles = BNGetPlatformListByArchitecture(arch.0, &mut count);
+ let handles = BNGetPlatformListByArchitecture(arch.handle, &mut count);
Array::new(handles, count, ())
}
@@ -125,7 +134,7 @@ impl Platform {
let mut count = 0;
let handles = BNGetPlatformListByOSAndArchitecture(
raw_name.as_ref().as_ptr() as *mut _,
- arch.0,
+ arch.handle,
&mut count,
);
@@ -145,10 +154,8 @@ impl Platform {
pub fn new<A: Architecture, S: BnStrCompatible>(arch: &A, name: S) -> Ref<Self> {
let name = name.into_bytes_with_nul();
unsafe {
- let handle = BNCreatePlatform(arch.as_ref().0, name.as_ref().as_ptr() as *mut _);
-
+ let handle = BNCreatePlatform(arch.as_ref().handle, name.as_ref().as_ptr() as *mut _);
assert!(!handle.is_null());
-
Ref::new(Self { handle })
}
}
@@ -164,10 +171,23 @@ impl Platform {
unsafe { CoreArchitecture::from_raw(BNGetPlatformArchitecture(self.handle)) }
}
- pub fn get_type_libraries_by_name(&self, name: &QualifiedName) -> Array<TypeLibrary> {
+ pub fn type_container(&self) -> TypeContainer {
+ let type_container_ptr = NonNull::new(unsafe { BNGetPlatformTypeContainer(self.handle) });
+ // NOTE: I have no idea how this isn't a UAF, see the note in `TypeContainer::from_raw`
+ // TODO: We are cloning here for platforms but we dont need to do this for [BinaryViewExt::type_container]
+ // TODO: Why does this require that we, construct a TypeContainer, duplicate the type container, then drop the original.
+ unsafe { TypeContainer::from_raw(type_container_ptr.unwrap()).clone() }
+ }
+
+ pub fn get_type_libraries_by_name<T: BnStrCompatible>(&self, name: T) -> Array<TypeLibrary> {
let mut count = 0;
+ let name = name.into_bytes_with_nul();
let result = unsafe {
- BNGetPlatformTypeLibrariesByName(self.handle, &name.0 as *const _ as *mut _, &mut count)
+ BNGetPlatformTypeLibrariesByName(
+ self.handle,
+ name.as_ref().as_ptr() as *mut _,
+ &mut count,
+ )
};
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
@@ -216,11 +236,10 @@ impl Platform {
BNSetPlatformSystemCallConvention
);
- pub fn calling_conventions(&self) -> Array<CallingConvention<CoreArchitecture>> {
+ pub fn calling_conventions(&self) -> Array<CoreCallingConvention> {
unsafe {
let mut count = 0;
let handles = BNGetPlatformCallingConventions(self.handle, &mut count);
-
Array::new(handles, count, self.arch())
}
}
@@ -229,7 +248,6 @@ impl Platform {
unsafe {
let mut count = 0;
let handles = BNGetPlatformTypes(self.handle, &mut count);
-
Array::new(handles, count, ())
}
}
@@ -238,7 +256,6 @@ impl Platform {
unsafe {
let mut count = 0;
let handles = BNGetPlatformVariables(self.handle, &mut count);
-
Array::new(handles, count, ())
}
}
@@ -247,108 +264,143 @@ impl Platform {
unsafe {
let mut count = 0;
let handles = BNGetPlatformFunctions(self.handle, &mut count);
-
Array::new(handles, count, ())
}
}
-}
-
-pub trait TypeParser {
- fn parse_types_from_source<S: BnStrCompatible, P: AsRef<Path>>(
- &self,
- _source: S,
- _filename: S,
- _include_directories: &[P],
- _auto_type_source: S,
- ) -> Result<TypeParserResult, String> {
- Err(String::new())
- }
-}
-#[derive(Clone, Default)]
-pub struct TypeParserResult {
- pub types: HashMap<String, Ref<Type>>,
- pub variables: HashMap<String, Ref<Type>>,
- pub functions: HashMap<String, Ref<Type>>,
-}
+ // TODO: system_calls
+ // TODO: add a helper function to define a system call (platform function with a specific type)
-impl TypeParser for Platform {
- fn parse_types_from_source<S: BnStrCompatible, P: AsRef<Path>>(
+ // TODO: Documentation, specifically how this differs from the TypeParser impl
+ pub fn preprocess_source(
&self,
- source: S,
- filename: S,
- include_directories: &[P],
- auto_type_source: S,
- ) -> Result<TypeParserResult, String> {
- let mut result = BNTypeParserResult {
- functionCount: 0,
- typeCount: 0,
- variableCount: 0,
- functions: ptr::null_mut(),
- types: ptr::null_mut(),
- variables: ptr::null_mut(),
- };
-
- let mut type_parser_result = TypeParserResult::default();
-
- let mut error_string: *mut raw::c_char = ptr::null_mut();
+ source: &str,
+ file_name: &str,
+ include_dirs: &[BnString],
+ ) -> Result<BnString, TypeParserError> {
+ let source_cstr = BnString::new(source);
+ let file_name_cstr = BnString::new(file_name);
- let src = source.into_bytes_with_nul();
- let filename = filename.into_bytes_with_nul();
- let auto_type_source = auto_type_source.into_bytes_with_nul();
-
- let mut include_dirs = vec![];
+ let mut result = ptr::null_mut();
+ let mut error_string = ptr::null_mut();
+ let success = unsafe {
+ BNPreprocessSource(
+ source_cstr.as_ptr(),
+ file_name_cstr.as_ptr(),
+ &mut result,
+ &mut error_string,
+ include_dirs.as_ptr() as *mut *const ffi::c_char,
+ include_dirs.len(),
+ )
+ };
- for dir in include_directories.iter() {
- let d = dir
- .as_ref()
- .to_string_lossy()
- .to_string()
- .into_bytes_with_nul();
- include_dirs.push(d.as_ptr() as _);
+ if success {
+ assert!(!result.is_null());
+ Ok(unsafe { BnString::from_raw(result) })
+ } else {
+ assert!(!error_string.is_null());
+ Err(TypeParserError::new(
+ TypeParserErrorSeverity::FatalSeverity,
+ unsafe { BnString::from_raw(error_string) }.to_string(),
+ file_name.to_string(),
+ 0,
+ 0,
+ ))
}
+ }
- unsafe {
- let success = BNParseTypesFromSource(
+ // TODO: Documentation, specifically how this differs from the TypeParser impl
+ pub fn parse_types_from_source(
+ &self,
+ src: &str,
+ filename: &str,
+ include_dirs: &[BnString],
+ auto_type_source: &str,
+ ) -> Result<TypeParserResult, TypeParserError> {
+ let source_cstr = BnString::new(src);
+ let file_name_cstr = BnString::new(filename);
+ let auto_type_source = BnString::new(auto_type_source);
+
+ let mut raw_result = BNTypeParserResult::default();
+ let mut error_string = ptr::null_mut();
+ let success = unsafe {
+ BNParseTypesFromSource(
self.handle,
- src.as_ref().as_ptr() as _,
- filename.as_ref().as_ptr() as _,
- &mut result,
+ source_cstr.as_ptr(),
+ file_name_cstr.as_ptr(),
+ &mut raw_result,
&mut error_string,
- include_dirs.as_mut_ptr(),
+ include_dirs.as_ptr() as *mut *const ffi::c_char,
include_dirs.len(),
- auto_type_source.as_ref().as_ptr() as _,
- );
-
- let error_msg = BnString::from_raw(error_string);
+ auto_type_source.as_ptr(),
+ )
+ };
- if !success {
- return Err(error_msg.to_string());
- }
+ if success {
+ let result = TypeParserResult::from_raw(&raw_result);
+ // NOTE: This is safe because the core allocated the TypeParserResult
+ TypeParserResult::free_raw(raw_result);
+ Ok(result)
+ } else {
+ assert!(!error_string.is_null());
+ Err(TypeParserError::new(
+ TypeParserErrorSeverity::FatalSeverity,
+ unsafe { BnString::from_raw(error_string) }.to_string(),
+ filename.to_string(),
+ 0,
+ 0,
+ ))
+ }
+ }
- for i in slice::from_raw_parts(result.types, result.typeCount) {
- let name = QualifiedName(i.name);
- type_parser_result
- .types
- .insert(name.string(), Type::ref_from_raw(i.type_));
- }
+ // TODO: Documentation, specifically how this differs from the TypeParser impl
+ pub fn parse_types_from_source_file(
+ &self,
+ filename: &str,
+ include_dirs: &[BnString],
+ auto_type_source: &str,
+ ) -> Result<TypeParserResult, TypeParserError> {
+ let file_name_cstr = BnString::new(filename);
+ let auto_type_source = BnString::new(auto_type_source);
- for i in slice::from_raw_parts(result.functions, result.functionCount) {
- let name = QualifiedName(i.name);
- type_parser_result
- .functions
- .insert(name.string(), Type::ref_from_raw(i.type_));
- }
+ let mut raw_result = BNTypeParserResult::default();
+ let mut error_string = ptr::null_mut();
+ let success = unsafe {
+ BNParseTypesFromSourceFile(
+ self.handle,
+ file_name_cstr.as_ptr(),
+ &mut raw_result,
+ &mut error_string,
+ include_dirs.as_ptr() as *mut *const ffi::c_char,
+ include_dirs.len(),
+ auto_type_source.as_ptr(),
+ )
+ };
- for i in slice::from_raw_parts(result.variables, result.variableCount) {
- let name = QualifiedName(i.name);
- type_parser_result
- .variables
- .insert(name.string(), Type::ref_from_raw(i.type_));
- }
+ if success {
+ let result = TypeParserResult::from_raw(&raw_result);
+ // NOTE: This is safe because the core allocated the TypeParserResult
+ TypeParserResult::free_raw(raw_result);
+ Ok(result)
+ } else {
+ assert!(!error_string.is_null());
+ Err(TypeParserError::new(
+ TypeParserErrorSeverity::FatalSeverity,
+ unsafe { BnString::from_raw(error_string) }.to_string(),
+ filename.to_string(),
+ 0,
+ 0,
+ ))
}
+ }
+}
- Ok(type_parser_result)
+impl Debug for Platform {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("Platform")
+ .field("name", &self.name())
+ .field("arch", &self.arch())
+ .finish()
}
}
@@ -379,11 +431,12 @@ impl CoreArrayProvider for Platform {
}
unsafe impl CoreArrayProviderInner for Platform {
- unsafe fn free(raw: *mut *mut BNPlatform, count: usize, _context: &()) {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreePlatformList(raw, count);
}
- unsafe fn wrap_raw<'a>(raw: &'a *mut BNPlatform, context: &'a ()) -> Self::Wrapped<'a> {
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
debug_assert!(!raw.is_null());
- Guard::new(Platform { handle: *raw }, context)
+ Guard::new(Self::from_raw(*raw), context)
}
}
diff --git a/rust/src/progress.rs b/rust/src/progress.rs
new file mode 100644
index 00000000..5ebf8273
--- /dev/null
+++ b/rust/src/progress.rs
@@ -0,0 +1,271 @@
+use std::ffi::c_void;
+
+pub trait ProgressCallback: Sized {
+ type SplitProgressType: SplitProgressBuilder;
+ fn progress(&mut self, progress: usize, total: usize) -> bool;
+
+ unsafe extern "C" fn cb_progress_callback(
+ ctxt: *mut c_void,
+ progress: usize,
+ total: usize,
+ ) -> bool {
+ let ctxt: &mut Self = &mut *(ctxt as *mut Self);
+ ctxt.progress(progress, total)
+ }
+
+ #[allow(clippy::wrong_self_convention)]
+ unsafe fn into_raw(&mut self) -> *mut c_void {
+ self as *mut Self as *mut c_void
+ }
+
+ /// Split a single progress function into proportionally sized subparts.
+ /// This function takes the original progress function and returns a new function whose signature
+ /// is the same but whose output is shortened to correspond to the specified subparts.
+ ///
+ /// The length of a subpart is proportional to the sum of all the weights.
+ /// E.g. with `subpart_weights = &[ 25, 50, 25 ]`, this will return a function that calls
+ /// progress_func and maps its progress to the ranges `[0..=25, 25..=75, 75..=100]`
+ ///
+ /// Weights of subparts, described above
+ ///
+ /// * `progress_func` - Original progress function (usually updates a UI)
+ /// * `subpart_weights` - Weights of subparts, described above
+ fn split(self, subpart_weights: &'static [usize]) -> Self::SplitProgressType;
+}
+
+pub trait SplitProgressBuilder {
+ type Progress<'a>: ProgressCallback
+ where
+ Self: 'a;
+ fn next_subpart(&mut self) -> Option<Self::Progress<'_>>;
+}
+
+impl<F> ProgressCallback for F
+where
+ F: FnMut(usize, usize) -> bool,
+{
+ type SplitProgressType = SplitProgress<F>;
+
+ fn progress(&mut self, progress: usize, total: usize) -> bool {
+ self(progress, total)
+ }
+
+ fn split(self, subpart_weights: &'static [usize]) -> Self::SplitProgressType {
+ SplitProgress::new(self, subpart_weights)
+ }
+}
+
+pub struct NoProgressCallback;
+
+impl ProgressCallback for NoProgressCallback {
+ type SplitProgressType = SplitProgressNop;
+
+ fn progress(&mut self, _progress: usize, _total: usize) -> bool {
+ unreachable!()
+ }
+
+ unsafe extern "C" fn cb_progress_callback(
+ _ctxt: *mut c_void,
+ _progress: usize,
+ _total: usize,
+ ) -> bool {
+ true
+ }
+
+ fn split(self, subpart_weights: &'static [usize]) -> Self::SplitProgressType {
+ SplitProgressNop(subpart_weights.len())
+ }
+}
+
+pub struct SplitProgressNop(usize);
+
+impl SplitProgressBuilder for SplitProgressNop {
+ type Progress<'a> = NoProgressCallback;
+
+ fn next_subpart(&mut self) -> Option<Self::Progress<'_>> {
+ if self.0 == 0 {
+ return None;
+ }
+ self.0 -= 1;
+ Some(NoProgressCallback)
+ }
+}
+
+pub struct SplitProgress<P> {
+ callback: P,
+ subpart_weights: &'static [usize],
+ total: usize,
+ progress: usize,
+}
+
+impl<P: ProgressCallback> SplitProgress<P> {
+ pub fn new(callback: P, subpart_weights: &'static [usize]) -> Self {
+ let total = subpart_weights.iter().sum();
+ Self {
+ callback,
+ subpart_weights,
+ total,
+ progress: 0,
+ }
+ }
+
+ pub fn next_subpart(&mut self) -> Option<SplitProgressInstance<'_, P>> {
+ if self.subpart_weights.is_empty() {
+ return None;
+ }
+ Some(SplitProgressInstance { progress: self })
+ }
+}
+
+impl<P: ProgressCallback> SplitProgressBuilder for SplitProgress<P> {
+ type Progress<'a>
+ = SplitProgressInstance<'a, P>
+ where
+ Self: 'a;
+ fn next_subpart(&mut self) -> Option<Self::Progress<'_>> {
+ self.next_subpart()
+ }
+}
+
+pub struct SplitProgressInstance<'a, P: ProgressCallback> {
+ progress: &'a mut SplitProgress<P>,
+}
+
+impl<P: ProgressCallback> Drop for SplitProgressInstance<'_, P> {
+ fn drop(&mut self) {
+ self.progress.progress += self.progress.subpart_weights[0];
+ self.progress.subpart_weights = &self.progress.subpart_weights[1..];
+ }
+}
+
+impl<P: ProgressCallback> ProgressCallback for SplitProgressInstance<'_, P> {
+ type SplitProgressType = SplitProgress<Self>;
+
+ fn progress(&mut self, progress: usize, total: usize) -> bool {
+ let subpart_progress = (self.progress.subpart_weights[0] * progress) / total;
+ let progress = self.progress.progress + subpart_progress;
+ self.progress
+ .callback
+ .progress(progress, self.progress.total)
+ }
+
+ fn split(self, subpart_weights: &'static [usize]) -> Self::SplitProgressType {
+ SplitProgress::new(self, subpart_weights)
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use std::cell::Cell;
+
+ use super::*;
+
+ #[test]
+ fn progress_simple() {
+ let progress = Cell::new(0);
+ let mut callback = |p, _| {
+ progress.set(p);
+ true
+ };
+ callback.progress(0, 100);
+ assert_eq!(progress.get(), 0);
+ callback.progress(1, 100);
+ assert_eq!(progress.get(), 1);
+ callback.progress(50, 100);
+ assert_eq!(progress.get(), 50);
+ callback.progress(99, 100);
+ assert_eq!(progress.get(), 99);
+ callback.progress(100, 100);
+ assert_eq!(progress.get(), 100);
+ }
+
+ #[test]
+ fn progress_simple_split() {
+ let progress = Cell::new(0);
+ let callback = |p, _| {
+ progress.set(p);
+ true
+ };
+ let mut split = callback.split(&[25, 50, 25]);
+ // 0..=25
+ let mut split_instance = split.next_subpart().unwrap();
+ split_instance.progress(0, 100);
+ assert_eq!(progress.get(), 0);
+ split_instance.progress(100, 100);
+ assert_eq!(progress.get(), 25);
+ drop(split_instance);
+
+ // 25..=75
+ let mut split_instance = split.next_subpart().unwrap();
+ split_instance.progress(0, 100);
+ assert_eq!(progress.get(), 25);
+ split_instance.progress(25, 100);
+ // there is no way to check for exact values, it depends on how the calculation is done,
+ // at the time or writing of this test is always round down, but we just check a range because this
+ // could change
+ assert!((36..=37).contains(&progress.get()));
+ split_instance.progress(50, 100);
+ assert_eq!(progress.get(), 50);
+ split_instance.progress(100, 100);
+ assert_eq!(progress.get(), 75);
+ drop(split_instance);
+
+ // 75..=100
+ let mut split_instance = split.next_subpart().unwrap();
+ split_instance.progress(0, 100);
+ assert_eq!(progress.get(), 75);
+ split_instance.progress(100, 100);
+ assert_eq!(progress.get(), 100);
+ drop(split_instance);
+
+ assert!(split.next_subpart().is_none());
+ }
+
+ #[test]
+ fn progress_recursive_split() {
+ let progress = Cell::new(0);
+ let callback = |p, _| {
+ progress.set(p);
+ true
+ };
+ let mut split = callback.split(&[25, 50, 25]);
+ // 0..=25
+ let mut split_instance = split.next_subpart().unwrap();
+ split_instance.progress(0, 100);
+ assert_eq!(progress.get(), 0);
+ split_instance.progress(100, 100);
+ assert_eq!(progress.get(), 25);
+ drop(split_instance);
+
+ // 25..=75, will get split into two parts: 25..=50 and 50..=75
+ {
+ let split_instance = split.next_subpart().unwrap();
+ let mut sub_split = split_instance.split(&[50, 50]);
+ // 25..=50
+ let mut sub_split_instance = sub_split.next_subpart().unwrap();
+ sub_split_instance.progress(0, 100);
+ assert_eq!(progress.get(), 25);
+ sub_split_instance.progress(100, 100);
+ assert_eq!(progress.get(), 50);
+ drop(sub_split_instance);
+
+ // 50..=75
+ let mut sub_split_instance = sub_split.next_subpart().unwrap();
+ sub_split_instance.progress(0, 100);
+ assert_eq!(progress.get(), 50);
+ sub_split_instance.progress(100, 100);
+ assert_eq!(progress.get(), 75);
+ drop(sub_split_instance);
+ }
+
+ // 75..=100
+ let mut split_instance = split.next_subpart().unwrap();
+ split_instance.progress(0, 100);
+ assert_eq!(progress.get(), 75);
+ split_instance.progress(100, 100);
+ assert_eq!(progress.get(), 100);
+ drop(split_instance);
+
+ assert!(split.next_subpart().is_none());
+ }
+}
diff --git a/rust/src/project.rs b/rust/src/project.rs
index f08ad63e..fad1e5ef 100644
--- a/rust/src/project.rs
+++ b/rust/src/project.rs
@@ -1,16 +1,22 @@
+pub mod file;
+pub mod folder;
+
+use std::ffi::{c_char, c_void};
+use std::fmt::Debug;
use std::ptr::{null_mut, NonNull};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
-use std::{ffi, mem};
use binaryninjacore_sys::*;
use crate::metadata::Metadata;
-use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref};
+use crate::progress::{NoProgressCallback, ProgressCallback};
+use crate::project::file::ProjectFile;
+use crate::project::folder::ProjectFolder;
+use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
use crate::string::{BnStrCompatible, BnString};
-#[repr(C)]
pub struct Project {
- handle: NonNull<BNProject>,
+ pub(crate) handle: NonNull<BNProject>,
}
impl Project {
@@ -18,14 +24,8 @@ impl Project {
Project { handle }
}
- pub(crate) unsafe fn ref_from_raw(handle: &*mut BNProject) -> &Self {
- debug_assert!(!handle.is_null());
- mem::transmute(handle)
- }
-
- #[allow(clippy::mut_from_ref)]
- pub(crate) unsafe fn as_raw(&self) -> &mut BNProject {
- &mut *self.handle.as_ptr()
+ pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNProject>) -> Ref<Self> {
+ Ref::new(Self { handle })
}
pub fn all_open() -> Array<Project> {
@@ -39,35 +39,35 @@ impl Project {
///
/// * `path` - Path to the project directory (.bnpr)
/// * `name` - Name of the new project
- pub fn create<P: BnStrCompatible, S: BnStrCompatible>(path: P, name: S) -> Self {
+ pub fn create<P: BnStrCompatible, S: BnStrCompatible>(path: P, name: S) -> Option<Ref<Self>> {
let path_raw = path.into_bytes_with_nul();
let name_raw = name.into_bytes_with_nul();
let handle = unsafe {
BNCreateProject(
- path_raw.as_ref().as_ptr() as *const ffi::c_char,
- name_raw.as_ref().as_ptr() as *const ffi::c_char,
+ path_raw.as_ref().as_ptr() as *const c_char,
+ name_raw.as_ref().as_ptr() as *const c_char,
)
};
- unsafe { Self::from_raw(NonNull::new(handle).unwrap()) }
+ NonNull::new(handle).map(|h| unsafe { Self::ref_from_raw(h) })
}
/// Open an existing project
///
/// * `path` - Path to the project directory (.bnpr) or project metadata file (.bnpm)
- pub fn open_project<P: BnStrCompatible>(path: P) -> Self {
+ pub fn open_project<P: BnStrCompatible>(path: P) -> Option<Ref<Self>> {
let path_raw = path.into_bytes_with_nul();
- let handle = unsafe { BNOpenProject(path_raw.as_ref().as_ptr() as *const ffi::c_char) };
- unsafe { Self::from_raw(NonNull::new(handle).unwrap()) }
+ let handle = unsafe { BNOpenProject(path_raw.as_ref().as_ptr() as *const c_char) };
+ NonNull::new(handle).map(|h| unsafe { Self::ref_from_raw(h) })
}
/// Check if the project is currently open
pub fn is_open(&self) -> bool {
- unsafe { BNProjectIsOpen(self.as_raw()) }
+ unsafe { BNProjectIsOpen(self.handle.as_ptr()) }
}
/// Open a closed project
pub fn open(&self) -> Result<(), ()> {
- if unsafe { BNProjectOpen(self.as_raw()) } {
+ if unsafe { BNProjectOpen(self.handle.as_ptr()) } {
Ok(())
} else {
Err(())
@@ -76,7 +76,7 @@ impl Project {
/// Close a open project
pub fn close(&self) -> Result<(), ()> {
- if unsafe { BNProjectClose(self.as_raw()) } {
+ if unsafe { BNProjectClose(self.handle.as_ptr()) } {
Ok(())
} else {
Err(())
@@ -85,35 +85,43 @@ impl Project {
/// Get the unique id of this project
pub fn id(&self) -> BnString {
- unsafe { BnString::from_raw(BNProjectGetId(self.as_raw())) }
+ unsafe { BnString::from_raw(BNProjectGetId(self.handle.as_ptr())) }
}
/// Get the path of the project
pub fn path(&self) -> BnString {
- unsafe { BnString::from_raw(BNProjectGetPath(self.as_raw())) }
+ unsafe { BnString::from_raw(BNProjectGetPath(self.handle.as_ptr())) }
}
/// Get the name of the project
pub fn name(&self) -> BnString {
- unsafe { BnString::from_raw(BNProjectGetName(self.as_raw())) }
+ unsafe { BnString::from_raw(BNProjectGetName(self.handle.as_ptr())) }
}
/// Set the name of the project
pub fn set_name<S: BnStrCompatible>(&self, value: S) {
let value = value.into_bytes_with_nul();
- unsafe { BNProjectSetName(self.as_raw(), value.as_ref().as_ptr() as *const ffi::c_char) }
+ unsafe {
+ BNProjectSetName(
+ self.handle.as_ptr(),
+ value.as_ref().as_ptr() as *const c_char,
+ )
+ }
}
/// Get the description of the project
pub fn description(&self) -> BnString {
- unsafe { BnString::from_raw(BNProjectGetDescription(self.as_raw())) }
+ unsafe { BnString::from_raw(BNProjectGetDescription(self.handle.as_ptr())) }
}
/// Set the description of the project
pub fn set_description<S: BnStrCompatible>(&self, value: S) {
let value = value.into_bytes_with_nul();
unsafe {
- BNProjectSetDescription(self.as_raw(), value.as_ref().as_ptr() as *const ffi::c_char)
+ BNProjectSetDescription(
+ self.handle.as_ptr(),
+ value.as_ref().as_ptr() as *const c_char,
+ )
}
}
@@ -121,7 +129,7 @@ impl Project {
pub fn query_metadata<S: BnStrCompatible>(&self, key: S) -> Ref<Metadata> {
let key = key.into_bytes_with_nul();
let result = unsafe {
- BNProjectQueryMetadata(self.as_raw(), key.as_ref().as_ptr() as *const ffi::c_char)
+ BNProjectQueryMetadata(self.handle.as_ptr(), key.as_ref().as_ptr() as *const c_char)
};
unsafe { Metadata::ref_from_raw(result) }
}
@@ -134,8 +142,8 @@ impl Project {
let key_raw = key.into_bytes_with_nul();
unsafe {
BNProjectStoreMetadata(
- self.as_raw(),
- key_raw.as_ref().as_ptr() as *const ffi::c_char,
+ self.handle.as_ptr(),
+ key_raw.as_ref().as_ptr() as *const c_char,
value.handle,
)
}
@@ -146,14 +154,14 @@ impl Project {
let key_raw = key.into_bytes_with_nul();
unsafe {
BNProjectRemoveMetadata(
- self.as_raw(),
- key_raw.as_ref().as_ptr() as *const ffi::c_char,
+ self.handle.as_ptr(),
+ key_raw.as_ref().as_ptr() as *const c_char,
)
}
}
pub fn push_folder(&self, file: &ProjectFolder) {
- unsafe { BNProjectPushFolder(self.as_raw(), file.as_raw()) }
+ unsafe { BNProjectPushFolder(self.handle.as_ptr(), file.handle.as_ptr()) }
}
/// Recursively create files and folders in the project from a path on disk
@@ -166,28 +174,12 @@ impl Project {
path: P,
parent: Option<&ProjectFolder>,
description: D,
- ) -> Result<ProjectFolder, ()>
+ ) -> Result<Ref<ProjectFolder>, ()>
where
P: BnStrCompatible,
D: BnStrCompatible,
{
- let path_raw = path.into_bytes_with_nul();
- let description_raw = description.into_bytes_with_nul();
- let parent_ptr = parent
- .map(|p| unsafe { p.as_raw() as *mut _ })
- .unwrap_or(null_mut());
-
- unsafe {
- let result = BNProjectCreateFolderFromPath(
- self.as_raw(),
- path_raw.as_ref().as_ptr() as *const ffi::c_char,
- parent_ptr,
- description_raw.as_ref().as_ptr() as *const ffi::c_char,
- null_mut(),
- Some(cb_progress_func_nop),
- );
- Ok(ProjectFolder::from_raw(NonNull::new(result).ok_or(())?))
- }
+ self.create_folder_from_path_with_progress(path, parent, description, NoProgressCallback)
}
/// Recursively create files and folders in the project from a path on disk
@@ -195,36 +187,33 @@ impl Project {
/// * `path` - Path to folder on disk
/// * `parent` - Parent folder in the project that will contain the new contents
/// * `description` - Description for created root folder
- /// * `progress_func` - Progress function that will be called
- pub fn create_folder_from_path_with_progress<P, D, F>(
+ /// * `progress` - [`ProgressCallback`] that will be called as the [`ProjectFolder`] is being created
+ pub fn create_folder_from_path_with_progress<P, D, PC>(
&self,
path: P,
parent: Option<&ProjectFolder>,
description: D,
- mut progress_func: F,
- ) -> Result<ProjectFolder, ()>
+ mut progress: PC,
+ ) -> Result<Ref<ProjectFolder>, ()>
where
P: BnStrCompatible,
D: BnStrCompatible,
- F: FnMut(usize, usize) -> bool,
+ PC: ProgressCallback,
{
let path_raw = path.into_bytes_with_nul();
let description_raw = description.into_bytes_with_nul();
- let parent_ptr = parent
- .map(|p| unsafe { p.as_raw() as *mut _ })
- .unwrap_or(null_mut());
+ let parent_ptr = parent.map(|p| p.handle.as_ptr()).unwrap_or(null_mut());
- let progress_ctx = &mut progress_func as *mut F as *mut ffi::c_void;
unsafe {
let result = BNProjectCreateFolderFromPath(
- self.as_raw(),
- path_raw.as_ref().as_ptr() as *const ffi::c_char,
+ self.handle.as_ptr(),
+ path_raw.as_ref().as_ptr() as *const c_char,
parent_ptr,
- description_raw.as_ref().as_ptr() as *const ffi::c_char,
- progress_ctx,
- Some(cb_progress_func::<F>),
+ description_raw.as_ref().as_ptr() as *const c_char,
+ &mut progress as *mut PC as *mut c_void,
+ Some(PC::cb_progress_callback),
);
- Ok(ProjectFolder::from_raw(NonNull::new(result).ok_or(())?))
+ Ok(ProjectFolder::ref_from_raw(NonNull::new(result).ok_or(())?))
}
}
@@ -238,24 +227,22 @@ impl Project {
parent: Option<&ProjectFolder>,
name: N,
description: D,
- ) -> Result<ProjectFolder, ()>
+ ) -> Result<Ref<ProjectFolder>, ()>
where
N: BnStrCompatible,
D: BnStrCompatible,
{
let name_raw = name.into_bytes_with_nul();
let description_raw = description.into_bytes_with_nul();
- let parent_ptr = parent
- .map(|p| unsafe { p.as_raw() as *mut _ })
- .unwrap_or(null_mut());
+ let parent_ptr = parent.map(|p| p.handle.as_ptr()).unwrap_or(null_mut());
unsafe {
let result = BNProjectCreateFolder(
- self.as_raw(),
+ self.handle.as_ptr(),
parent_ptr,
- name_raw.as_ref().as_ptr() as *const ffi::c_char,
- description_raw.as_ref().as_ptr() as *const ffi::c_char,
+ name_raw.as_ref().as_ptr() as *const c_char,
+ description_raw.as_ref().as_ptr() as *const c_char,
);
- Ok(ProjectFolder::from_raw(NonNull::new(result).ok_or(())?))
+ Ok(ProjectFolder::ref_from_raw(NonNull::new(result).ok_or(())?))
}
}
@@ -271,7 +258,7 @@ impl Project {
name: N,
description: D,
id: I,
- ) -> Result<ProjectFolder, ()>
+ ) -> Result<Ref<ProjectFolder>, ()>
where
N: BnStrCompatible,
D: BnStrCompatible,
@@ -279,26 +266,24 @@ impl Project {
{
let name_raw = name.into_bytes_with_nul();
let description_raw = description.into_bytes_with_nul();
- let parent_ptr = parent
- .map(|p| unsafe { p.as_raw() as *mut _ })
- .unwrap_or(null_mut());
+ let parent_ptr = parent.map(|p| p.handle.as_ptr()).unwrap_or(null_mut());
let id_raw = id.into_bytes_with_nul();
unsafe {
let result = BNProjectCreateFolderUnsafe(
- self.as_raw(),
+ self.handle.as_ptr(),
parent_ptr,
- name_raw.as_ref().as_ptr() as *const ffi::c_char,
- description_raw.as_ref().as_ptr() as *const ffi::c_char,
- id_raw.as_ref().as_ptr() as *const ffi::c_char,
+ name_raw.as_ref().as_ptr() as *const c_char,
+ description_raw.as_ref().as_ptr() as *const c_char,
+ id_raw.as_ref().as_ptr() as *const c_char,
);
- Ok(ProjectFolder::from_raw(NonNull::new(result).ok_or(())?))
+ Ok(ProjectFolder::ref_from_raw(NonNull::new(result).ok_or(())?))
}
}
/// Get a list of folders in the project
pub fn folders(&self) -> Result<Array<ProjectFolder>, ()> {
let mut count = 0;
- let result = unsafe { BNProjectGetFolders(self.as_raw(), &mut count) };
+ let result = unsafe { BNProjectGetFolders(self.handle.as_ptr(), &mut count) };
if result.is_null() {
return Err(());
}
@@ -307,55 +292,39 @@ impl Project {
}
/// Retrieve a folder in the project by unique folder `id`
- pub fn folder_by_id<S: BnStrCompatible>(&self, id: S) -> Option<ProjectFolder> {
+ pub fn folder_by_id<S: BnStrCompatible>(&self, id: S) -> Option<Ref<ProjectFolder>> {
let id_raw = id.into_bytes_with_nul();
- let id_ptr = id_raw.as_ref().as_ptr() as *const ffi::c_char;
-
- let result = unsafe { BNProjectGetFolderById(self.as_raw(), id_ptr) };
+ let id_ptr = id_raw.as_ref().as_ptr() as *const c_char;
+ let result = unsafe { BNProjectGetFolderById(self.handle.as_ptr(), id_ptr) };
let handle = NonNull::new(result)?;
- Some(unsafe { ProjectFolder::from_raw(handle) })
+ Some(unsafe { ProjectFolder::ref_from_raw(handle) })
}
- /// Recursively delete a folder from the project
+ /// Recursively delete a [`ProjectFolder`] from the [`Project`].
///
- /// * `folder` - Folder to delete recursively
+ /// * `folder` - [`ProjectFolder`] to delete recursively
pub fn delete_folder(&self, folder: &ProjectFolder) -> Result<(), ()> {
- let result = unsafe {
- BNProjectDeleteFolder(
- self.as_raw(),
- folder.as_raw(),
- null_mut(),
- Some(cb_progress_func_nop),
- )
- };
- if result {
- Ok(())
- } else {
- Err(())
- }
+ self.delete_folder_with_progress(folder, NoProgressCallback)
}
- /// Recursively delete a folder from the project
+ /// Recursively delete a [`ProjectFolder`] from the [`Project`].
///
- /// * `folder` - Folder to delete recursively
- /// * `progress_func` - Progress function that will be called as objects get deleted
- pub fn delete_folder_with_progress<F>(
+ /// * `folder` - [`ProjectFolder`] to delete recursively
+ /// * `progress` - [`ProgressCallback`] that will be called as objects get deleted
+ pub fn delete_folder_with_progress<P: ProgressCallback>(
&self,
folder: &ProjectFolder,
- mut progress_func: F,
- ) -> Result<(), ()>
- where
- F: FnMut(usize, usize) -> bool,
- {
- let progress_ctx = &mut progress_func as *mut F as *mut ffi::c_void;
+ mut progress: P,
+ ) -> Result<(), ()> {
let result = unsafe {
BNProjectDeleteFolder(
- self.as_raw(),
- folder.as_raw(),
- progress_ctx,
- Some(cb_progress_func::<F>),
+ self.handle.as_ptr(),
+ folder.handle.as_ptr(),
+ &mut progress as *mut P as *mut c_void,
+ Some(P::cb_progress_callback),
)
};
+
if result {
Ok(())
} else {
@@ -364,7 +333,7 @@ impl Project {
}
pub fn push_file(&self, file: &ProjectFile) {
- unsafe { BNProjectPushFile(self.as_raw(), file.as_raw()) }
+ unsafe { BNProjectPushFile(self.handle.as_ptr(), file.handle.as_ptr()) }
}
/// Create a file in the project from a path on disk
@@ -379,27 +348,19 @@ impl Project {
folder: Option<&ProjectFolder>,
name: N,
description: D,
- ) -> Result<ProjectFile, ()>
+ ) -> Result<Ref<ProjectFile>, ()>
where
P: BnStrCompatible,
N: BnStrCompatible,
D: BnStrCompatible,
{
- let path_raw = path.into_bytes_with_nul();
- let name_raw = name.into_bytes_with_nul();
- let description_raw = description.into_bytes_with_nul();
- unsafe {
- let result = BNProjectCreateFileFromPath(
- self.as_raw(),
- path_raw.as_ref().as_ptr() as *const ffi::c_char,
- folder.map(|x| x.as_raw() as *mut _).unwrap_or(null_mut()),
- name_raw.as_ref().as_ptr() as *const ffi::c_char,
- description_raw.as_ref().as_ptr() as *const ffi::c_char,
- null_mut(),
- Some(cb_progress_func_nop),
- );
- Ok(ProjectFile::from_raw(NonNull::new(result).ok_or(())?))
- }
+ self.create_file_from_path_with_progress(
+ path,
+ folder,
+ name,
+ description,
+ NoProgressCallback,
+ )
}
/// Create a file in the project from a path on disk
@@ -408,36 +369,37 @@ impl Project {
/// * `folder` - Folder to place the created file in
/// * `name` - Name to assign to the created file
/// * `description` - Description to assign to the created file
- /// * `progress_func` - Progress function that will be called as the file is being added
- pub fn create_file_from_path_with_progress<P, N, D, F>(
+ /// * `progress` - [`ProgressCallback`] that will be called as the [`ProjectFile`] is being added
+ pub fn create_file_from_path_with_progress<P, N, D, PC>(
&self,
path: P,
folder: Option<&ProjectFolder>,
name: N,
description: D,
- mut progress_func: F,
- ) -> Result<ProjectFile, ()>
+ mut progress: PC,
+ ) -> Result<Ref<ProjectFile>, ()>
where
P: BnStrCompatible,
N: BnStrCompatible,
D: BnStrCompatible,
- F: FnMut(usize, usize) -> bool,
+ PC: ProgressCallback,
{
let path_raw = path.into_bytes_with_nul();
let name_raw = name.into_bytes_with_nul();
let description_raw = description.into_bytes_with_nul();
- let progress_ctx = &mut progress_func as *mut F as *mut ffi::c_void;
+ let folder_ptr = folder.map(|p| p.handle.as_ptr()).unwrap_or(null_mut());
+
unsafe {
let result = BNProjectCreateFileFromPath(
- self.as_raw(),
- path_raw.as_ref().as_ptr() as *const ffi::c_char,
- folder.map(|x| x.as_raw() as *mut _).unwrap_or(null_mut()),
- name_raw.as_ref().as_ptr() as *const ffi::c_char,
- description_raw.as_ref().as_ptr() as *const ffi::c_char,
- progress_ctx,
- Some(cb_progress_func::<F>),
+ self.handle.as_ptr(),
+ path_raw.as_ref().as_ptr() as *const c_char,
+ folder_ptr,
+ name_raw.as_ref().as_ptr() as *const c_char,
+ description_raw.as_ref().as_ptr() as *const c_char,
+ &mut progress as *mut PC as *mut c_void,
+ Some(PC::cb_progress_callback),
);
- Ok(ProjectFile::from_raw(NonNull::new(result).ok_or(())?))
+ Ok(ProjectFile::ref_from_raw(NonNull::new(result).ok_or(())?))
}
}
@@ -457,31 +419,22 @@ impl Project {
description: D,
id: I,
creation_time: SystemTime,
- ) -> Result<ProjectFile, ()>
+ ) -> Result<Ref<ProjectFile>, ()>
where
P: BnStrCompatible,
N: BnStrCompatible,
D: BnStrCompatible,
I: BnStrCompatible,
{
- let path_raw = path.into_bytes_with_nul();
- let name_raw = name.into_bytes_with_nul();
- let description_raw = description.into_bytes_with_nul();
- let id_raw = id.into_bytes_with_nul();
- unsafe {
- let result = BNProjectCreateFileFromPathUnsafe(
- self.as_raw(),
- path_raw.as_ref().as_ptr() as *const ffi::c_char,
- folder.map(|x| x.as_raw() as *mut _).unwrap_or(null_mut()),
- name_raw.as_ref().as_ptr() as *const ffi::c_char,
- description_raw.as_ref().as_ptr() as *const ffi::c_char,
- id_raw.as_ref().as_ptr() as *const ffi::c_char,
- systime_to_bntime(creation_time).unwrap(),
- null_mut(),
- Some(cb_progress_func_nop),
- );
- Ok(ProjectFile::from_raw(NonNull::new(result).ok_or(())?))
- }
+ self.create_file_from_path_unsafe_with_progress(
+ path,
+ folder,
+ name,
+ description,
+ id,
+ creation_time,
+ NoProgressCallback,
+ )
}
/// Create a file in the project from a path on disk
@@ -492,9 +445,9 @@ impl Project {
/// * `description` - Description to assign to the created file
/// * `id` - id unique ID
/// * `creation_time` - Creation time of the file
- /// * `progress_func` - Progress function that will be called as the file is being added
+ /// * `progress` - [`ProgressCallback`] that will be called as the [`ProjectFile`] is being created
#[allow(clippy::too_many_arguments)]
- pub unsafe fn create_file_from_path_with_progress_unsafe<P, N, D, I, F>(
+ pub unsafe fn create_file_from_path_unsafe_with_progress<P, N, D, I, PC>(
&self,
path: P,
folder: Option<&ProjectFolder>,
@@ -502,33 +455,34 @@ impl Project {
description: D,
id: I,
creation_time: SystemTime,
- mut progress_func: F,
- ) -> Result<ProjectFile, ()>
+ mut progress: PC,
+ ) -> Result<Ref<ProjectFile>, ()>
where
P: BnStrCompatible,
N: BnStrCompatible,
D: BnStrCompatible,
I: BnStrCompatible,
- F: FnMut(usize, usize) -> bool,
+ PC: ProgressCallback,
{
let path_raw = path.into_bytes_with_nul();
let name_raw = name.into_bytes_with_nul();
let description_raw = description.into_bytes_with_nul();
let id_raw = id.into_bytes_with_nul();
- let progress_ctx = &mut progress_func as *mut F as *mut ffi::c_void;
+ let folder_ptr = folder.map(|p| p.handle.as_ptr()).unwrap_or(null_mut());
+
unsafe {
let result = BNProjectCreateFileFromPathUnsafe(
- self.as_raw(),
- path_raw.as_ref().as_ptr() as *const ffi::c_char,
- folder.map(|x| x.as_raw() as *mut _).unwrap_or(null_mut()),
- name_raw.as_ref().as_ptr() as *const ffi::c_char,
- description_raw.as_ref().as_ptr() as *const ffi::c_char,
- id_raw.as_ref().as_ptr() as *const ffi::c_char,
+ self.handle.as_ptr(),
+ path_raw.as_ref().as_ptr() as *const c_char,
+ folder_ptr,
+ name_raw.as_ref().as_ptr() as *const c_char,
+ description_raw.as_ref().as_ptr() as *const c_char,
+ id_raw.as_ref().as_ptr() as *const c_char,
systime_to_bntime(creation_time).unwrap(),
- progress_ctx,
- Some(cb_progress_func::<F>),
+ &mut progress as *mut PC as *mut c_void,
+ Some(PC::cb_progress_callback),
);
- Ok(ProjectFile::from_raw(NonNull::new(result).ok_or(())?))
+ Ok(ProjectFile::ref_from_raw(NonNull::new(result).ok_or(())?))
}
}
@@ -544,26 +498,12 @@ impl Project {
folder: Option<&ProjectFolder>,
name: N,
description: D,
- ) -> Result<ProjectFile, ()>
+ ) -> Result<Ref<ProjectFile>, ()>
where
N: BnStrCompatible,
D: BnStrCompatible,
{
- let name_raw = name.into_bytes_with_nul();
- let description_raw = description.into_bytes_with_nul();
- unsafe {
- let result = BNProjectCreateFile(
- self.as_raw(),
- contents.as_ptr(),
- contents.len(),
- folder.map(|x| x.as_raw() as *mut _).unwrap_or(null_mut()),
- name_raw.as_ref().as_ptr() as *const ffi::c_char,
- description_raw.as_ref().as_ptr() as *const ffi::c_char,
- null_mut(),
- Some(cb_progress_func_nop),
- );
- Ok(ProjectFile::from_raw(NonNull::new(result).ok_or(())?))
- }
+ self.create_file_with_progress(contents, folder, name, description, NoProgressCallback)
}
/// Create a file in the project
@@ -572,35 +512,36 @@ impl Project {
/// * `folder` - Folder to place the created file in
/// * `name` - Name to assign to the created file
/// * `description` - Description to assign to the created file
- /// * `progress_func` - Progress function that will be called as the file is being added
- pub fn create_file_with_progress<N, D, F>(
+ /// * `progress` - [`ProgressCallback`] that will be called as the [`ProjectFile`] is being created
+ pub fn create_file_with_progress<N, D, P>(
&self,
contents: &[u8],
folder: Option<&ProjectFolder>,
name: N,
description: D,
- mut progress_func: F,
- ) -> Result<ProjectFile, ()>
+ mut progress: P,
+ ) -> Result<Ref<ProjectFile>, ()>
where
N: BnStrCompatible,
D: BnStrCompatible,
- F: FnMut(usize, usize) -> bool,
+ P: ProgressCallback,
{
let name_raw = name.into_bytes_with_nul();
let description_raw = description.into_bytes_with_nul();
- let progress_ctx = &mut progress_func as *mut F as *mut ffi::c_void;
+ let folder_ptr = folder.map(|p| p.handle.as_ptr()).unwrap_or(null_mut());
+
unsafe {
let result = BNProjectCreateFile(
- self.as_raw(),
+ self.handle.as_ptr(),
contents.as_ptr(),
contents.len(),
- folder.map(|x| x.as_raw() as *mut _).unwrap_or(null_mut()),
- name_raw.as_ref().as_ptr() as *const ffi::c_char,
- description_raw.as_ref().as_ptr() as *const ffi::c_char,
- progress_ctx,
- Some(cb_progress_func::<F>),
+ folder_ptr,
+ name_raw.as_ref().as_ptr() as *const c_char,
+ description_raw.as_ref().as_ptr() as *const c_char,
+ &mut progress as *mut P as *mut c_void,
+ Some(P::cb_progress_callback),
);
- Ok(ProjectFile::from_raw(NonNull::new(result).ok_or(())?))
+ Ok(ProjectFile::ref_from_raw(NonNull::new(result).ok_or(())?))
}
}
@@ -620,30 +561,21 @@ impl Project {
description: D,
id: I,
creation_time: SystemTime,
- ) -> Result<ProjectFile, ()>
+ ) -> Result<Ref<ProjectFile>, ()>
where
N: BnStrCompatible,
D: BnStrCompatible,
I: BnStrCompatible,
{
- let name_raw = name.into_bytes_with_nul();
- let description_raw = description.into_bytes_with_nul();
- let id_raw = id.into_bytes_with_nul();
- unsafe {
- let result = BNProjectCreateFileUnsafe(
- self.as_raw(),
- contents.as_ptr(),
- contents.len(),
- folder.map(|x| x.as_raw() as *mut _).unwrap_or(null_mut()),
- name_raw.as_ref().as_ptr() as *const ffi::c_char,
- description_raw.as_ref().as_ptr() as *const ffi::c_char,
- id_raw.as_ref().as_ptr() as *const ffi::c_char,
- systime_to_bntime(creation_time).unwrap(),
- null_mut(),
- Some(cb_progress_func_nop),
- );
- Ok(ProjectFile::from_raw(NonNull::new(result).ok_or(())?))
- }
+ self.create_file_unsafe_with_progress(
+ contents,
+ folder,
+ name,
+ description,
+ id,
+ creation_time,
+ NoProgressCallback,
+ )
}
/// Create a file in the project
@@ -654,9 +586,9 @@ impl Project {
/// * `description` - Description to assign to the created file
/// * `id` - id unique ID
/// * `creation_time` - Creation time of the file
- /// * `progress_func` - Progress function that will be called as the file is being added
+ /// * `progress` - [`ProgressCallback`] that will be called as the [`ProjectFile`] is being created
#[allow(clippy::too_many_arguments)]
- pub unsafe fn create_file_with_progress_unsafe<N, D, I, F>(
+ pub unsafe fn create_file_unsafe_with_progress<N, D, I, P>(
&self,
contents: &[u8],
folder: Option<&ProjectFolder>,
@@ -664,66 +596,67 @@ impl Project {
description: D,
id: I,
creation_time: SystemTime,
- mut progress_func: F,
- ) -> Result<ProjectFile, ()>
+ mut progress: P,
+ ) -> Result<Ref<ProjectFile>, ()>
where
N: BnStrCompatible,
D: BnStrCompatible,
I: BnStrCompatible,
- F: FnMut(usize, usize) -> bool,
+ P: ProgressCallback,
{
let name_raw = name.into_bytes_with_nul();
let description_raw = description.into_bytes_with_nul();
let id_raw = id.into_bytes_with_nul();
- let progress_ctx = &mut progress_func as *mut F as *mut ffi::c_void;
+ let folder_ptr = folder.map(|p| p.handle.as_ptr()).unwrap_or(null_mut());
+
unsafe {
let result = BNProjectCreateFileUnsafe(
- self.as_raw(),
+ self.handle.as_ptr(),
contents.as_ptr(),
contents.len(),
- folder.map(|x| x.as_raw() as *mut _).unwrap_or(null_mut()),
- name_raw.as_ref().as_ptr() as *const ffi::c_char,
- description_raw.as_ref().as_ptr() as *const ffi::c_char,
- id_raw.as_ref().as_ptr() as *const ffi::c_char,
+ folder_ptr,
+ name_raw.as_ref().as_ptr() as *const c_char,
+ description_raw.as_ref().as_ptr() as *const c_char,
+ id_raw.as_ref().as_ptr() as *const c_char,
systime_to_bntime(creation_time).unwrap(),
- progress_ctx,
- Some(cb_progress_func::<F>),
+ &mut progress as *mut P as *mut c_void,
+ Some(P::cb_progress_callback),
);
- Ok(ProjectFile::from_raw(NonNull::new(result).ok_or(())?))
+ Ok(ProjectFile::ref_from_raw(NonNull::new(result).ok_or(())?))
}
}
/// Get a list of files in the project
- pub fn files(&self) -> Result<Array<ProjectFile>, ()> {
+ pub fn files(&self) -> Array<ProjectFile> {
let mut count = 0;
- let result = unsafe { BNProjectGetFiles(self.as_raw(), &mut count) };
+ let result = unsafe { BNProjectGetFiles(self.handle.as_ptr(), &mut count) };
assert!(!result.is_null());
- Ok(unsafe { Array::new(result, count, ()) })
+ unsafe { Array::new(result, count, ()) }
}
/// Retrieve a file in the project by unique `id`
- pub fn file_by_id<S: BnStrCompatible>(&self, id: S) -> Option<ProjectFile> {
+ pub fn file_by_id<S: BnStrCompatible>(&self, id: S) -> Option<Ref<ProjectFile>> {
let id_raw = id.into_bytes_with_nul();
- let id_ptr = id_raw.as_ref().as_ptr() as *const ffi::c_char;
+ let id_ptr = id_raw.as_ref().as_ptr() as *const c_char;
- let result = unsafe { BNProjectGetFileById(self.as_raw(), id_ptr) };
+ let result = unsafe { BNProjectGetFileById(self.handle.as_ptr(), id_ptr) };
let handle = NonNull::new(result)?;
- Some(unsafe { ProjectFile::from_raw(handle) })
+ Some(unsafe { ProjectFile::ref_from_raw(handle) })
}
/// Retrieve a file in the project by the `path` on disk
- pub fn file_by_path<S: BnStrCompatible>(&self, path: S) -> Option<ProjectFile> {
+ pub fn file_by_path<S: BnStrCompatible>(&self, path: S) -> Option<Ref<ProjectFile>> {
let path_raw = path.into_bytes_with_nul();
- let path_ptr = path_raw.as_ref().as_ptr() as *const ffi::c_char;
+ let path_ptr = path_raw.as_ref().as_ptr() as *const c_char;
- let result = unsafe { BNProjectGetFileByPathOnDisk(self.as_raw(), path_ptr) };
+ let result = unsafe { BNProjectGetFileByPathOnDisk(self.handle.as_ptr(), path_ptr) };
let handle = NonNull::new(result)?;
- Some(unsafe { ProjectFile::from_raw(handle) })
+ Some(unsafe { ProjectFile::ref_from_raw(handle) })
}
/// Delete a file from the project
pub fn delete_file(&self, file: &ProjectFile) -> bool {
- unsafe { BNProjectDeleteFile(self.as_raw(), file.as_raw()) }
+ unsafe { BNProjectDeleteFile(self.handle.as_ptr(), file.handle.as_ptr()) }
}
/// A context manager to speed up bulk project operations.
@@ -733,44 +666,58 @@ impl Project {
///
/// ```no_run
/// # use binaryninja::project::Project;
- /// # let project: Project = todo!();
+ /// # let mut project: Project = todo!();
/// if let Ok(bulk) = project.bulk_operation() {
/// for file in std::fs::read_dir("/bin/").unwrap().into_iter() {
/// let file = file.unwrap();
/// let file_type = file.file_type().unwrap();
/// if file_type.is_file() && !file_type.is_symlink() {
- /// bulk.create_file_from_path(
- /// "/bin/",
- /// None,
- /// &file.file_name().to_string_lossy(),
- /// "",
- /// ).unwrap();
+ /// bulk.create_file_from_path("/bin/", None, &file.file_name().to_string_lossy(), "")
+ /// .unwrap();
/// }
/// }
/// }
/// ```
- // NOTE mut is used here, so only one lock can be aquired at once
+ // NOTE mut is used here, so only one lock can be acquired at once
pub fn bulk_operation(&mut self) -> Result<ProjectBulkOperationLock, ()> {
Ok(ProjectBulkOperationLock::lock(self))
}
}
-impl Drop for Project {
- fn drop(&mut self) {
- unsafe { BNFreeProject(self.as_raw()) }
+impl Debug for Project {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("Project")
+ .field("id", &self.id())
+ .field("name", &self.name())
+ .field("description", &self.description())
+ .finish()
}
}
-impl Clone for Project {
- fn clone(&self) -> Self {
- unsafe { Self::from_raw(NonNull::new(BNNewProjectReference(self.as_raw())).unwrap()) }
+impl ToOwned for Project {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for Project {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: NonNull::new(BNNewProjectReference(handle.handle.as_ptr())).unwrap(),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeProject(handle.handle.as_ptr());
}
}
impl CoreArrayProvider for Project {
type Raw = *mut BNProject;
type Context = ();
- type Wrapped<'a> = &'a Project;
+ type Wrapped<'a> = Guard<'a, Project>;
}
unsafe impl CoreArrayProviderInner for Project {
@@ -778,18 +725,20 @@ unsafe impl CoreArrayProviderInner for Project {
BNFreeProjectList(raw, count)
}
- unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
- Self::ref_from_raw(raw)
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
+ let raw_ptr = NonNull::new(*raw).unwrap();
+ Guard::new(Self::from_raw(raw_ptr), context)
}
}
+// TODO: Rename to bulk operation guard?
pub struct ProjectBulkOperationLock<'a> {
lock: &'a mut Project,
}
impl<'a> ProjectBulkOperationLock<'a> {
pub fn lock(project: &'a mut Project) -> Self {
- unsafe { BNProjectBeginBulkOperation(project.as_raw()) };
+ unsafe { BNProjectBeginBulkOperation(project.handle.as_ptr()) };
Self { lock: project }
}
@@ -807,282 +756,7 @@ impl std::ops::Deref for ProjectBulkOperationLock<'_> {
impl Drop for ProjectBulkOperationLock<'_> {
fn drop(&mut self) {
- unsafe { BNProjectEndBulkOperation(self.lock.as_raw()) };
- }
-}
-
-#[repr(transparent)]
-pub struct ProjectFolder {
- handle: NonNull<BNProjectFolder>,
-}
-
-impl ProjectFolder {
- pub(crate) unsafe fn from_raw(handle: NonNull<BNProjectFolder>) -> Self {
- Self { handle }
- }
-
- pub(crate) unsafe fn ref_from_raw(handle: &*mut BNProjectFolder) -> &Self {
- debug_assert!(!handle.is_null());
- mem::transmute(handle)
- }
-
- #[allow(clippy::mut_from_ref)]
- pub(crate) unsafe fn as_raw(&self) -> &mut BNProjectFolder {
- &mut *self.handle.as_ptr()
- }
-
- /// Get the project that owns this folder
- pub fn project(&self) -> Project {
- unsafe {
- Project::from_raw(NonNull::new(BNProjectFolderGetProject(self.as_raw())).unwrap())
- }
- }
-
- /// Get the unique id of this folder
- pub fn id(&self) -> BnString {
- unsafe { BnString::from_raw(BNProjectFolderGetId(self.as_raw())) }
- }
-
- /// Get the name of this folder
- pub fn name(&self) -> BnString {
- unsafe { BnString::from_raw(BNProjectFolderGetName(self.as_raw())) }
- }
-
- /// Set the name of this folder
- pub fn set_name<S: BnStrCompatible>(&self, value: S) -> bool {
- let value_raw = value.into_bytes_with_nul();
- unsafe {
- BNProjectFolderSetName(
- self.as_raw(),
- value_raw.as_ref().as_ptr() as *const ffi::c_char,
- )
- }
- }
-
- /// Get the description of this folder
- pub fn description(&self) -> BnString {
- unsafe { BnString::from_raw(BNProjectFolderGetDescription(self.as_raw())) }
- }
-
- /// Set the description of this folder
- pub fn set_description<S: BnStrCompatible>(&self, value: S) -> bool {
- let value_raw = value.into_bytes_with_nul();
- unsafe {
- BNProjectFolderSetDescription(
- self.as_raw(),
- value_raw.as_ref().as_ptr() as *const ffi::c_char,
- )
- }
- }
-
- /// Get the folder that contains this folder
- pub fn parent(&self) -> Option<ProjectFolder> {
- let result = unsafe { BNProjectFolderGetParent(self.as_raw()) };
- NonNull::new(result).map(|handle| unsafe { ProjectFolder::from_raw(handle) })
- }
-
- /// Set the folder that contains this folder
- pub fn set_folder(&self, folder: Option<&ProjectFolder>) -> bool {
- let folder_handle = folder
- .map(|x| unsafe { x.as_raw() as *mut _ })
- .unwrap_or(null_mut());
- unsafe { BNProjectFolderSetParent(self.as_raw(), folder_handle) }
- }
-
- /// Recursively export this folder to disk, returns `true' if the export succeeded
- ///
- /// * `dest` - Destination path for the exported contents
- pub fn export<S: BnStrCompatible>(&self, dest: S) -> bool {
- let dest_raw = dest.into_bytes_with_nul();
- unsafe {
- BNProjectFolderExport(
- self.as_raw(),
- dest_raw.as_ref().as_ptr() as *const ffi::c_char,
- null_mut(),
- Some(cb_progress_func_nop),
- )
- }
- }
-
- /// Recursively export this folder to disk, returns `true' if the export succeeded
- ///
- /// * `dest` - Destination path for the exported contents
- /// * `progress_func` - Progress function that will be called as contents are exporting
- pub fn export_with_progress<S, F>(&self, dest: S, mut progress: F) -> bool
- where
- S: BnStrCompatible,
- F: FnMut(usize, usize) -> bool,
- {
- let dest_raw = dest.into_bytes_with_nul();
- unsafe {
- BNProjectFolderExport(
- self.as_raw(),
- dest_raw.as_ref().as_ptr() as *const ffi::c_char,
- &mut progress as *mut _ as *mut ffi::c_void,
- Some(cb_progress_func::<F>),
- )
- }
- }
-}
-
-impl Drop for ProjectFolder {
- fn drop(&mut self) {
- unsafe { BNFreeProjectFolder(self.as_raw()) }
- }
-}
-
-impl Clone for ProjectFolder {
- fn clone(&self) -> Self {
- unsafe { Self::from_raw(NonNull::new(BNNewProjectFolderReference(self.as_raw())).unwrap()) }
- }
-}
-
-impl CoreArrayProvider for ProjectFolder {
- type Raw = *mut BNProjectFolder;
- type Context = ();
- type Wrapped<'a> = &'a Self;
-}
-
-unsafe impl CoreArrayProviderInner for ProjectFolder {
- unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
- BNFreeProjectFolderList(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 ProjectFile {
- handle: NonNull<BNProjectFile>,
-}
-
-impl ProjectFile {
- pub(crate) unsafe fn from_raw(handle: NonNull<BNProjectFile>) -> Self {
- Self { handle }
- }
-
- pub(crate) unsafe fn ref_from_raw(handle: &*mut BNProjectFile) -> &Self {
- debug_assert!(!handle.is_null());
- mem::transmute(handle)
- }
-
- #[allow(clippy::mut_from_ref)]
- pub(crate) unsafe fn as_raw(&self) -> &mut BNProjectFile {
- &mut *self.handle.as_ptr()
- }
-
- /// Get the project that owns this file
- pub fn project(&self) -> Project {
- unsafe { Project::from_raw(NonNull::new(BNProjectFileGetProject(self.as_raw())).unwrap()) }
- }
-
- /// Get the path on disk to this file's contents
- pub fn path_on_disk(&self) -> BnString {
- unsafe { BnString::from_raw(BNProjectFileGetPathOnDisk(self.as_raw())) }
- }
-
- /// Check if this file's contents exist on disk
- pub fn exists_on_disk(&self) -> bool {
- unsafe { BNProjectFileExistsOnDisk(self.as_raw()) }
- }
-
- /// Get the unique id of this file
- pub fn id(&self) -> BnString {
- unsafe { BnString::from_raw(BNProjectFileGetId(self.as_raw())) }
- }
-
- /// Get the name of this file
- pub fn name(&self) -> BnString {
- unsafe { BnString::from_raw(BNProjectFileGetName(self.as_raw())) }
- }
-
- /// Set the name of this file
- pub fn set_name<S: BnStrCompatible>(&self, value: S) -> bool {
- let value_raw = value.into_bytes_with_nul();
- unsafe {
- BNProjectFileSetName(
- self.as_raw(),
- value_raw.as_ref().as_ptr() as *const ffi::c_char,
- )
- }
- }
-
- /// Get the description of this file
- pub fn description(&self) -> BnString {
- unsafe { BnString::from_raw(BNProjectFileGetDescription(self.as_raw())) }
- }
-
- /// Set the description of this file
- pub fn set_description<S: BnStrCompatible>(&self, value: S) -> bool {
- let value_raw = value.into_bytes_with_nul();
- unsafe {
- BNProjectFileSetDescription(
- self.as_raw(),
- value_raw.as_ref().as_ptr() as *const ffi::c_char,
- )
- }
- }
-
- /// Get the file creation time
- pub fn creation_time(&self) -> SystemTime {
- systime_from_bntime(unsafe { BNProjectFileGetCreationTimestamp(self.as_raw()) }).unwrap()
- }
-
- /// Get the folder that contains this file
- pub fn folder(&self) -> Option<ProjectFolder> {
- let result = unsafe { BNProjectFileGetFolder(self.as_raw()) };
- NonNull::new(result).map(|handle| unsafe { ProjectFolder::from_raw(handle) })
- }
-
- /// Set the folder that contains this file
- pub fn set_folder(&self, folder: Option<&ProjectFolder>) -> bool {
- let folder_handle = folder
- .map(|x| unsafe { x.as_raw() as *mut _ })
- .unwrap_or(null_mut());
- unsafe { BNProjectFileSetFolder(self.as_raw(), folder_handle) }
- }
-
- /// Export this file to disk, `true' if the export succeeded
- ///
- /// * `dest` - Destination path for the exported contents
- pub fn export<S: BnStrCompatible>(&self, dest: S) -> bool {
- let dest_raw = dest.into_bytes_with_nul();
- unsafe {
- BNProjectFileExport(
- self.as_raw(),
- dest_raw.as_ref().as_ptr() as *const ffi::c_char,
- )
- }
- }
-}
-
-impl Drop for ProjectFile {
- fn drop(&mut self) {
- unsafe { BNFreeProjectFile(self.as_raw()) }
- }
-}
-
-impl Clone for ProjectFile {
- fn clone(&self) -> Self {
- unsafe { Self::from_raw(NonNull::new(BNNewProjectFileReference(self.as_raw())).unwrap()) }
- }
-}
-
-impl CoreArrayProvider for ProjectFile {
- type Raw = *mut BNProjectFile;
- type Context = ();
- type Wrapped<'a> = &'a Self;
-}
-
-unsafe impl CoreArrayProviderInner for ProjectFile {
- unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
- BNFreeProjectFileList(raw, count)
- }
-
- unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
- Self::ref_from_raw(raw)
+ unsafe { BNProjectEndBulkOperation(self.lock.handle.as_ptr()) };
}
}
@@ -1098,367 +772,3 @@ fn systime_to_bntime(time: SystemTime) -> Option<i64> {
.try_into()
.ok()
}
-
-unsafe extern "C" fn cb_progress_func<F: FnMut(usize, usize) -> bool>(
- ctxt: *mut ffi::c_void,
- progress: usize,
- total: usize,
-) -> bool {
- if ctxt.is_null() {
- return true;
- }
- let closure: &mut F = mem::transmute(ctxt);
- closure(progress, total)
-}
-
-unsafe extern "C" fn cb_progress_func_nop(
- _ctxt: *mut ffi::c_void,
- _progress: usize,
- _total: usize,
-) -> bool {
- true
-}
-
-#[cfg(test)]
-mod test {
- use std::time::SystemTime;
-
- use crate::metadata::Metadata;
- use crate::rc::Ref;
-
- use super::Project;
-
- fn unique_project(name: &str) -> String {
- format!("{}/{}", std::env::temp_dir().to_str().unwrap(), name)
- }
-
- #[test]
- fn create_delete_empty() {
- crate::headless::init();
-
- let project_name = "create_delete_empty_project";
- let project_path = unique_project(project_name);
- // create the project
- let project = Project::create(&project_path, project_name);
- project.open().unwrap();
- assert!(project.is_open());
-
- // check project data
- let project_path_received = project.path();
- assert_eq!(&project_path, project_path_received.as_str());
- let project_name_received = project.name();
- assert_eq!(project_name, project_name_received.as_str());
-
- // close the project
- project.close().unwrap();
- assert!(!project.is_open());
- drop(project);
-
- // delete the project
- std::fs::remove_dir_all(project_path).unwrap();
-
- crate::headless::shutdown();
- }
-
- #[test]
- fn create_close_open_close() {
- crate::headless::init();
-
- let project_name = "create_close_open_close";
- let project_path = unique_project(project_name);
- // create the project
- let project = Project::create(&project_path, project_name);
- project.open().unwrap();
-
- // get the project id
- let id = project.id();
-
- // close the project
- project.close().unwrap();
- drop(project);
-
- let project = Project::open_project(&project_path);
- // assert same id
- let new_id = project.id();
- assert_eq!(id, new_id);
-
- // close the project
- project.close().unwrap();
- drop(project);
-
- // delete the project
- std::fs::remove_dir_all(project_path).unwrap();
-
- crate::headless::shutdown();
- }
-
- #[test]
- fn modify_project() {
- crate::headless::init();
-
- let project_name = "modify_project";
- let project_path = unique_project(project_name);
- // create the project
- let project = Project::create(&project_path, project_name);
- project.open().unwrap();
-
- // get project id
- let id = project.id();
-
- // create data and verify that data was created
- let data_1: Ref<Metadata> = "data1".into();
- let data_2: Ref<Metadata> = "data2".into();
- assert!(project.store_metadata("key", data_1.as_ref()));
- assert_eq!(
- data_1.get_string().unwrap(),
- project.query_metadata("key").get_string().unwrap()
- );
- project.remove_metadata("key");
- assert!(project.store_metadata("key", data_2.as_ref()));
- assert_eq!(
- data_2.get_string().unwrap(),
- project.query_metadata("key").get_string().unwrap()
- );
-
- // create file that will be imported to the project
- let tmp_folder_1_name = format!(
- "tmp_folder_{}",
- std::time::SystemTime::now()
- .duration_since(std::time::UNIX_EPOCH)
- .unwrap()
- .as_millis()
- );
- let tmp_folder_2_name = format!("{tmp_folder_1_name }_2");
- let tmp_folder_1 = format!(
- "{}/{tmp_folder_1_name}",
- std::env::temp_dir().to_str().unwrap()
- );
- let tmp_folder_2 = format!(
- "{}/{tmp_folder_2_name}",
- std::env::temp_dir().to_str().unwrap()
- );
- std::fs::create_dir(&tmp_folder_1).unwrap();
- std::fs::create_dir(&tmp_folder_2).unwrap();
- let input_file_1 = format!("{tmp_folder_2}/input_1");
- let input_file_2 = format!("{tmp_folder_2}/input_2");
- let input_file_1_data = b"input_1_data";
- let input_file_2_data = b"input_1_data";
- std::fs::write(&input_file_1, input_file_1_data).unwrap();
- std::fs::write(&input_file_2, input_file_2_data).unwrap();
-
- // create and delete folders
- let folder_1_desc = "desc_folder_1";
- let folder_1 = project
- .create_folder(None, "folder_1", folder_1_desc)
- .unwrap();
- let folder_2_desc = "AAAAA";
- let folder_2_id = "1717416787371";
- let folder_2 = unsafe {
- project
- .create_folder_unsafe(Some(&folder_1), "folder_2", folder_2_desc, folder_2_id)
- .unwrap()
- };
- let folder_3_desc = ""; // TODO "çàáÁÀ";
- let folder_3 = project
- .create_folder_from_path(&tmp_folder_1, None, folder_3_desc)
- .unwrap();
- let folder_4_desc = "";
- let _folder_4 = project
- .create_folder_from_path_with_progress(
- &tmp_folder_2,
- Some(&folder_3),
- folder_4_desc,
- |_, _| true,
- )
- .unwrap();
- let folder_5 = project
- .create_folder(None, "deleted_folder", folder_4_desc)
- .unwrap();
-
- assert_eq!(project.folders().unwrap().len(), 5);
- let last_folder = project.folder_by_id(folder_5.id()).unwrap();
- project.delete_folder(&last_folder).unwrap();
- assert_eq!(project.folders().unwrap().len(), 4);
- drop(folder_5);
-
- // create, import and delete file
- let file_1_data = b"data_1";
- let file_1_desc = "desc_file_1";
- let _file_1 = project
- .create_file(file_1_data, None, "file_1", file_1_desc)
- .unwrap();
- let file_2_data = b"data_2";
- let file_2_desc = "my desc";
- let file_2_id = "12334545";
- let _file_2 = unsafe {
- project.create_file_unsafe(
- file_2_data,
- Some(&folder_2),
- "file_2",
- file_2_desc,
- file_2_id,
- SystemTime::UNIX_EPOCH,
- )
- }
- .unwrap();
- let file_3_data = b"data\x023";
- let file_3_desc = "!";
- let _file_3 = project
- .create_file_with_progress(
- file_3_data,
- Some(&folder_1),
- "file_3",
- file_3_desc,
- |_, _| true,
- )
- .unwrap();
- let file_4_time = SystemTime::now();
- let file_4_data = b"data_4\x00_4";
- let file_4_desc = "";
- let file_4_id = "123123123";
- let _file_4 = unsafe {
- project.create_file_with_progress_unsafe(
- file_4_data,
- Some(&folder_3),
- "file_4",
- file_4_desc,
- file_4_id,
- file_4_time,
- |_, _| true,
- )
- }
- .unwrap();
- let file_5_desc = "desc";
- let _file_5 = project
- .create_file_from_path(&input_file_1, None, "file_5", file_5_desc)
- .unwrap();
- let file_6_time = SystemTime::now();
- let file_6_desc = "de";
- let file_6_id = "90218347";
- let _file_6 = unsafe {
- project.create_file_from_path_unsafe(
- &input_file_2,
- Some(&folder_3),
- "file_6",
- file_6_desc,
- file_6_id,
- file_6_time,
- )
- }
- .unwrap();
- let file_7 = project
- .create_file_from_path_with_progress(
- &input_file_2,
- Some(&folder_2),
- "file_7",
- "no",
- |_, _| true,
- )
- .unwrap();
- let file_8 = unsafe {
- project.create_file_from_path_with_progress_unsafe(
- &input_file_1,
- None,
- "file_7",
- "no",
- "92736528",
- SystemTime::now(),
- |_, _| true,
- )
- }
- .unwrap();
-
- assert_eq!(project.files().unwrap().len(), 10);
- let file_a = project.file_by_id(file_8.id()).unwrap();
- let file_b = project.file_by_path(file_7.path_on_disk()).unwrap();
- project.delete_file(&file_a);
- project.delete_file(&file_b);
- assert_eq!(project.files().unwrap().len(), 8);
- drop(file_8);
- drop(file_7);
-
- project.set_name("project_name");
- project.set_description("project_description");
-
- // close the project
- project.close().unwrap();
- drop(project);
- drop(folder_1);
- drop(folder_2);
- drop(folder_3);
-
- // reopen the project and verify the information store on it
- let project = Project::open_project(&project_path);
-
- // assert same id
- assert_eq!(id, project.id());
-
- // verify metadata
- assert_eq!(
- data_2.get_string().unwrap(),
- project.query_metadata("key").get_string().unwrap()
- );
-
- // check folders
- let folders = [
- ("folder_1", None),
- ("folder_2", Some(folder_2_id)),
- (&tmp_folder_1_name, None),
- (&tmp_folder_2_name, None),
- ];
- for folder in project.folders().unwrap().iter() {
- let found = folders
- .iter()
- .find(|f| folder.name().as_str() == f.0)
- .unwrap();
- if let Some(id) = found.1 {
- assert_eq!(folder.id().as_str(), id);
- }
- }
-
- // check files
- #[rustfmt::skip]
- let files = [
- ("file_1", &file_1_data[..], None, None),
- ("file_2", &file_2_data[..], Some(file_2_id), None),
- ("file_3", &file_3_data[..], None, None),
- ("file_4", &file_4_data[..], Some(file_4_id), Some(file_4_time)),
- ("file_5", &input_file_1_data[..], None, None),
- ("file_6", &input_file_2_data[..], Some(file_6_id), Some(file_6_time)),
- ("input_1", &input_file_1_data[..], None, None),
- ("input_2", &input_file_2_data[..], None, None),
- ];
- for file in project.files().unwrap().iter() {
- let found = files.iter().find(|f| file.name().as_str() == f.0).unwrap();
- if let Some(id) = found.2 {
- assert_eq!(file.id().as_str(), id);
- }
- if let Some(time) = found.3 {
- assert_eq!(
- file.creation_time()
- .duration_since(SystemTime::UNIX_EPOCH)
- .unwrap()
- .as_secs(),
- time.duration_since(SystemTime::UNIX_EPOCH)
- .unwrap()
- .as_secs()
- );
- }
- let content = std::fs::read(file.path_on_disk().as_str()).unwrap();
- assert_eq!(content, found.1);
- }
-
- assert_eq!(project.name().as_str(), "project_name");
- assert_eq!(project.description().as_str(), "project_description");
-
- // close the project
- project.close().unwrap();
-
- // delete the project
- std::fs::remove_dir_all(project_path).unwrap();
- std::fs::remove_dir_all(tmp_folder_1).unwrap();
- std::fs::remove_dir_all(tmp_folder_2).unwrap();
-
- crate::headless::shutdown();
- }
-}
diff --git a/rust/src/project/file.rs b/rust/src/project/file.rs
new file mode 100644
index 00000000..5071dd50
--- /dev/null
+++ b/rust/src/project/file.rs
@@ -0,0 +1,167 @@
+use crate::project::{systime_from_bntime, Project, ProjectFolder};
+use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
+use crate::string::{BnStrCompatible, BnString};
+use binaryninjacore_sys::{
+ BNFreeProjectFile, BNFreeProjectFileList, BNNewProjectFileReference, BNProjectFile,
+ BNProjectFileExistsOnDisk, BNProjectFileExport, BNProjectFileGetCreationTimestamp,
+ BNProjectFileGetDescription, BNProjectFileGetFolder, BNProjectFileGetId, BNProjectFileGetName,
+ BNProjectFileGetPathOnDisk, BNProjectFileGetProject, BNProjectFileSetDescription,
+ BNProjectFileSetFolder, BNProjectFileSetName,
+};
+use std::ffi::c_char;
+use std::fmt::Debug;
+use std::ptr::{null_mut, NonNull};
+use std::time::SystemTime;
+
+#[repr(transparent)]
+pub struct ProjectFile {
+ pub(crate) handle: NonNull<BNProjectFile>,
+}
+
+impl ProjectFile {
+ pub(crate) unsafe fn from_raw(handle: NonNull<BNProjectFile>) -> Self {
+ Self { handle }
+ }
+
+ pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNProjectFile>) -> Ref<Self> {
+ Ref::new(Self { handle })
+ }
+
+ /// Get the project that owns this file
+ pub fn project(&self) -> Ref<Project> {
+ unsafe {
+ Project::ref_from_raw(
+ NonNull::new(BNProjectFileGetProject(self.handle.as_ptr())).unwrap(),
+ )
+ }
+ }
+
+ /// Get the path on disk to this file's contents
+ pub fn path_on_disk(&self) -> BnString {
+ unsafe { BnString::from_raw(BNProjectFileGetPathOnDisk(self.handle.as_ptr())) }
+ }
+
+ /// Check if this file's contents exist on disk
+ pub fn exists_on_disk(&self) -> bool {
+ unsafe { BNProjectFileExistsOnDisk(self.handle.as_ptr()) }
+ }
+
+ /// Get the unique id of this file
+ pub fn id(&self) -> BnString {
+ unsafe { BnString::from_raw(BNProjectFileGetId(self.handle.as_ptr())) }
+ }
+
+ /// Get the name of this file
+ pub fn name(&self) -> BnString {
+ unsafe { BnString::from_raw(BNProjectFileGetName(self.handle.as_ptr())) }
+ }
+
+ /// Set the name of this file
+ pub fn set_name<S: BnStrCompatible>(&self, value: S) -> bool {
+ let value_raw = value.into_bytes_with_nul();
+ unsafe {
+ BNProjectFileSetName(
+ self.handle.as_ptr(),
+ value_raw.as_ref().as_ptr() as *const c_char,
+ )
+ }
+ }
+
+ /// Get the description of this file
+ pub fn description(&self) -> BnString {
+ unsafe { BnString::from_raw(BNProjectFileGetDescription(self.handle.as_ptr())) }
+ }
+
+ /// Set the description of this file
+ pub fn set_description<S: BnStrCompatible>(&self, value: S) -> bool {
+ let value_raw = value.into_bytes_with_nul();
+ unsafe {
+ BNProjectFileSetDescription(
+ self.handle.as_ptr(),
+ value_raw.as_ref().as_ptr() as *const c_char,
+ )
+ }
+ }
+
+ /// Get the file creation time
+ pub fn creation_time(&self) -> SystemTime {
+ systime_from_bntime(unsafe { BNProjectFileGetCreationTimestamp(self.handle.as_ptr()) })
+ .unwrap()
+ }
+
+ /// Get the folder that contains this file
+ pub fn folder(&self) -> Option<Ref<ProjectFolder>> {
+ let result = unsafe { BNProjectFileGetFolder(self.handle.as_ptr()) };
+ NonNull::new(result).map(|handle| unsafe { ProjectFolder::ref_from_raw(handle) })
+ }
+
+ /// Set the folder that contains this file
+ pub fn set_folder(&self, folder: Option<&ProjectFolder>) -> bool {
+ let folder_handle = folder.map(|x| x.handle.as_ptr()).unwrap_or(null_mut());
+ unsafe { BNProjectFileSetFolder(self.handle.as_ptr(), folder_handle) }
+ }
+
+ /// Export this file to disk, `true' if the export succeeded
+ ///
+ /// * `dest` - Destination path for the exported contents
+ pub fn export<S: BnStrCompatible>(&self, dest: S) -> bool {
+ let dest_raw = dest.into_bytes_with_nul();
+ unsafe {
+ BNProjectFileExport(
+ self.handle.as_ptr(),
+ dest_raw.as_ref().as_ptr() as *const c_char,
+ )
+ }
+ }
+}
+
+impl Debug for ProjectFile {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("ProjectFile")
+ .field("id", &self.id())
+ .field("name", &self.name())
+ .field("description", &self.description())
+ .field("creation_time", &self.creation_time())
+ .field("exists_on_disk", &self.exists_on_disk())
+ .field("project", &self.project())
+ .field("folder", &self.folder())
+ .finish()
+ }
+}
+
+impl ToOwned for ProjectFile {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for ProjectFile {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: NonNull::new(BNNewProjectFileReference(handle.handle.as_ptr())).unwrap(),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeProjectFile(handle.handle.as_ptr());
+ }
+}
+
+impl CoreArrayProvider for ProjectFile {
+ type Raw = *mut BNProjectFile;
+ type Context = ();
+ type Wrapped<'a> = Guard<'a, Self>;
+}
+
+unsafe impl CoreArrayProviderInner for ProjectFile {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeProjectFileList(raw, count)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
+ let raw_ptr = NonNull::new(*raw).unwrap();
+ Guard::new(Self::from_raw(raw_ptr), context)
+ }
+}
diff --git a/rust/src/project/folder.rs b/rust/src/project/folder.rs
new file mode 100644
index 00000000..90b40051
--- /dev/null
+++ b/rust/src/project/folder.rs
@@ -0,0 +1,165 @@
+use crate::progress::{NoProgressCallback, ProgressCallback};
+use crate::project::Project;
+use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
+use crate::string::{BnStrCompatible, BnString};
+use binaryninjacore_sys::{
+ BNFreeProjectFolder, BNFreeProjectFolderList, BNNewProjectFolderReference, BNProjectFolder,
+ BNProjectFolderExport, BNProjectFolderGetDescription, BNProjectFolderGetId,
+ BNProjectFolderGetName, BNProjectFolderGetParent, BNProjectFolderGetProject,
+ BNProjectFolderSetDescription, BNProjectFolderSetName, BNProjectFolderSetParent,
+};
+use std::ffi::{c_char, c_void};
+use std::fmt::Debug;
+use std::ptr::{null_mut, NonNull};
+
+#[repr(transparent)]
+pub struct ProjectFolder {
+ pub(crate) handle: NonNull<BNProjectFolder>,
+}
+
+impl ProjectFolder {
+ pub(crate) unsafe fn from_raw(handle: NonNull<BNProjectFolder>) -> Self {
+ Self { handle }
+ }
+
+ pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNProjectFolder>) -> Ref<Self> {
+ Ref::new(Self { handle })
+ }
+
+ /// Get the project that owns this folder
+ pub fn project(&self) -> Ref<Project> {
+ unsafe {
+ Project::ref_from_raw(
+ NonNull::new(BNProjectFolderGetProject(self.handle.as_ptr())).unwrap(),
+ )
+ }
+ }
+
+ /// Get the unique id of this folder
+ pub fn id(&self) -> BnString {
+ unsafe { BnString::from_raw(BNProjectFolderGetId(self.handle.as_ptr())) }
+ }
+
+ /// Get the name of this folder
+ pub fn name(&self) -> BnString {
+ unsafe { BnString::from_raw(BNProjectFolderGetName(self.handle.as_ptr())) }
+ }
+
+ /// Set the name of this folder
+ pub fn set_name<S: BnStrCompatible>(&self, value: S) -> bool {
+ let value_raw = value.into_bytes_with_nul();
+ unsafe {
+ BNProjectFolderSetName(
+ self.handle.as_ptr(),
+ value_raw.as_ref().as_ptr() as *const c_char,
+ )
+ }
+ }
+
+ /// Get the description of this folder
+ pub fn description(&self) -> BnString {
+ unsafe { BnString::from_raw(BNProjectFolderGetDescription(self.handle.as_ptr())) }
+ }
+
+ /// Set the description of this folder
+ pub fn set_description<S: BnStrCompatible>(&self, value: S) -> bool {
+ let value_raw = value.into_bytes_with_nul();
+ unsafe {
+ BNProjectFolderSetDescription(
+ self.handle.as_ptr(),
+ value_raw.as_ref().as_ptr() as *const c_char,
+ )
+ }
+ }
+
+ /// Get the folder that contains this folder
+ pub fn parent(&self) -> Option<Ref<ProjectFolder>> {
+ let result = unsafe { BNProjectFolderGetParent(self.handle.as_ptr()) };
+ NonNull::new(result).map(|handle| unsafe { ProjectFolder::ref_from_raw(handle) })
+ }
+
+ /// Set the folder that contains this folder
+ pub fn set_folder(&self, folder: Option<&ProjectFolder>) -> bool {
+ let folder_handle = folder.map(|x| x.handle.as_ptr()).unwrap_or(null_mut());
+ unsafe { BNProjectFolderSetParent(self.handle.as_ptr(), folder_handle) }
+ }
+
+ // TODO: Take Path?
+ /// Recursively export this folder to disk, returns `true' if the export succeeded
+ ///
+ /// * `dest` - Destination path for the exported contents
+ pub fn export<S: BnStrCompatible>(&self, dest: S) -> bool {
+ self.export_with_progress(dest, NoProgressCallback)
+ }
+
+ // TODO: Take Path?
+ /// Recursively export this folder to disk, returns `true' if the export succeeded
+ ///
+ /// * `dest` - Destination path for the exported contents
+ /// * `progress` - [`ProgressCallback`] that will be called as contents are exporting
+ pub fn export_with_progress<S, P>(&self, dest: S, mut progress: P) -> bool
+ where
+ S: BnStrCompatible,
+ P: ProgressCallback,
+ {
+ let dest_raw = dest.into_bytes_with_nul();
+
+ let success = unsafe {
+ BNProjectFolderExport(
+ self.handle.as_ptr(),
+ dest_raw.as_ref().as_ptr() as *const c_char,
+ &mut progress as *mut P as *mut c_void,
+ Some(P::cb_progress_callback),
+ )
+ };
+
+ success
+ }
+}
+
+impl Debug for ProjectFolder {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("ProjectFolder")
+ .field("id", &self.id())
+ .field("name", &self.name())
+ .field("description", &self.description())
+ .finish()
+ }
+}
+
+impl ToOwned for ProjectFolder {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for ProjectFolder {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: NonNull::new(BNNewProjectFolderReference(handle.handle.as_ptr())).unwrap(),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeProjectFolder(handle.handle.as_ptr());
+ }
+}
+
+impl CoreArrayProvider for ProjectFolder {
+ type Raw = *mut BNProjectFolder;
+ type Context = ();
+ type Wrapped<'a> = Guard<'a, Self>;
+}
+
+unsafe impl CoreArrayProviderInner for ProjectFolder {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeProjectFolderList(raw, count)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
+ let raw_ptr = NonNull::new(*raw).unwrap();
+ Guard::new(Self::from_raw(raw_ptr), context)
+ }
+}
diff --git a/rust/src/rc.rs b/rust/src/rc.rs
index 342633b9..a096d062 100644
--- a/rust/src/rc.rs
+++ b/rust/src/rc.rs
@@ -57,9 +57,7 @@ impl<T: RefCountable> Ref<T> {
pub(crate) unsafe fn into_raw(obj: Self) -> T {
let res = ptr::read(&obj.contents);
-
mem::forget(obj);
-
res
}
}
@@ -153,8 +151,17 @@ impl<'a, T> Guard<'a, T> {
}
}
+impl<T> Debug for Guard<'_, T>
+where
+ T: Debug,
+{
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ self.contents.fmt(f)
+ }
+}
+
#[allow(private_bounds)]
-impl<'a, T> Guard<'a, T>
+impl<T> Guard<'_, T>
where
T: RefCountable,
{
@@ -164,13 +171,13 @@ where
}
}
-impl<'a, T> AsRef<T> for Guard<'a, T> {
+impl<T> AsRef<T> for Guard<'_, T> {
fn as_ref(&self) -> &T {
&self.contents
}
}
-impl<'a, T> Deref for Guard<'a, T> {
+impl<T> Deref for Guard<'_, T> {
type Target = T;
fn deref(&self) -> &T {
@@ -178,13 +185,13 @@ impl<'a, T> Deref for Guard<'a, T> {
}
}
-impl<'a, T> DerefMut for Guard<'a, T> {
+impl<T> DerefMut for Guard<'_, T> {
fn deref_mut(&mut self) -> &mut T {
&mut self.contents
}
}
-impl<'a, T> Borrow<T> for Guard<'a, T> {
+impl<T> Borrow<T> for Guard<'_, T> {
fn borrow(&self) -> &T {
&self.contents
}
@@ -203,6 +210,7 @@ pub(crate) unsafe trait CoreArrayProviderInner: CoreArrayProvider {
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a>;
}
+// TODO: I would really like if we impld Debug for this, but lifetimes are hard!
#[allow(private_bounds)]
pub struct Array<P: CoreArrayProviderInner> {
contents: *mut P::Raw,
@@ -210,19 +218,6 @@ pub struct Array<P: CoreArrayProviderInner> {
context: P::Context,
}
-unsafe impl<P> Sync for Array<P>
-where
- P: CoreArrayProviderInner,
- P::Context: Sync,
-{
-}
-unsafe impl<P> Send for Array<P>
-where
- P: CoreArrayProviderInner,
- P::Context: Send,
-{
-}
-
#[allow(private_bounds)]
impl<P: CoreArrayProviderInner> Array<P> {
pub(crate) unsafe fn new(raw: *mut P::Raw, count: usize, context: P::Context) -> Self {
@@ -242,10 +237,13 @@ impl<P: CoreArrayProviderInner> Array<P> {
pub fn is_empty(&self) -> bool {
self.count == 0
}
-}
-#[allow(private_bounds)]
-impl<P: CoreArrayProviderInner> Array<P> {
+ pub fn to_vec(&self) -> Vec<P::Wrapped<'_>> {
+ let mut res = Vec::with_capacity(self.count);
+ res.extend(self.iter());
+ res
+ }
+
#[inline]
pub fn get(&self, index: usize) -> P::Wrapped<'_> {
unsafe {
@@ -262,6 +260,19 @@ impl<P: CoreArrayProviderInner> Array<P> {
}
}
+unsafe impl<P> Sync for Array<P>
+where
+ P: CoreArrayProviderInner,
+ P::Context: Sync,
+{
+}
+unsafe impl<P> Send for Array<P>
+where
+ P: CoreArrayProviderInner,
+ P::Context: Send,
+{
+}
+
impl<'a, P: CoreArrayProviderInner> IntoIterator for &'a Array<P> {
type Item = P::Wrapped<'a>;
type IntoIter = ArrayIter<'a, P>;
diff --git a/rust/src/references.rs b/rust/src/references.rs
index d12c33c5..52fb6713 100644
--- a/rust/src/references.rs
+++ b/rust/src/references.rs
@@ -1,53 +1,70 @@
+#![allow(dead_code)]
use crate::architecture::CoreArchitecture;
use crate::function::Function;
-use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Guard, Ref};
+use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Ref};
use binaryninjacore_sys::{BNFreeCodeReferences, BNFreeDataReferences, BNReferenceSource};
-use std::mem::ManuallyDrop;
/// A struct representing a single code cross-reference.
-/// Taking a cue from [`crate::linearview::LinearDisassemblyLine`], this struct uses [ManuallyDrop] to
-/// prevent destructors from being run on the [`Function`] object allocated by
-/// the core in `BNGetCodeReferences` (et al). The reference is cleaned up on [Drop] of
-/// the enclosing array object.
#[derive(Debug)]
pub struct CodeReference {
- arch: CoreArchitecture,
- func: ManuallyDrop<Ref<Function>>,
- pub address: u64,
-}
-
-/// A struct representing a single data cross-reference.
-/// Data references have no associated metadata, so this object has only
-/// a single [`DataReference::address`] attribute.
-pub struct DataReference {
+ pub arch: Option<CoreArchitecture>,
+ pub func: Option<Ref<Function>>,
pub address: u64,
}
impl CodeReference {
- pub(crate) unsafe fn new(handle: &BNReferenceSource) -> Self {
- let func = ManuallyDrop::new(Function::from_raw(handle.func));
- let arch = CoreArchitecture::from_raw(handle.arch);
- let address = handle.addr;
+ pub(crate) fn from_raw(value: &BNReferenceSource) -> Self {
Self {
- func,
- arch,
- address,
+ func: match value.func.is_null() {
+ false => Some(unsafe { Function::from_raw(value.func) }.to_owned()),
+ true => None,
+ },
+ arch: match value.func.is_null() {
+ false => Some(unsafe { CoreArchitecture::from_raw(value.arch) }),
+ true => None,
+ },
+ address: value.addr,
+ }
+ }
+
+ pub(crate) fn from_owned_raw(value: BNReferenceSource) -> Self {
+ let owned = Self::from_raw(&value);
+ Self::free_raw(value);
+ owned
+ }
+
+ pub(crate) fn into_raw(value: Self) -> BNReferenceSource {
+ BNReferenceSource {
+ func: match value.func {
+ Some(func) => unsafe { Ref::into_raw(func) }.handle,
+ None => std::ptr::null_mut(),
+ },
+ arch: value.arch.map(|a| a.handle).unwrap_or(std::ptr::null_mut()),
+ addr: value.address,
}
}
-}
-impl<'a> CodeReference {
- /// A handle to the referenced function bound by the [CodeReference] object's lifetime.
- /// A user can call `.to_owned()` to promote this into its own ref-counted struct
- /// and use it after the lifetime of the [CodeReference].
- pub fn function(&'a self) -> &'a Function {
- self.func.as_ref()
+ pub(crate) fn into_owned_raw(value: &Self) -> BNReferenceSource {
+ BNReferenceSource {
+ func: match &value.func {
+ Some(func) => func.handle,
+ None => std::ptr::null_mut(),
+ },
+ arch: value.arch.map(|a| a.handle).unwrap_or(std::ptr::null_mut()),
+ addr: value.address,
+ }
+ }
+
+ pub(crate) fn free_raw(value: BNReferenceSource) {
+ let _ = unsafe { Function::ref_from_raw(value.func) };
}
- /// A handle to the [CodeReference]'s [CoreArchitecture]. This type is [Copy] so reference
- /// shenanigans are not needed here.
- pub fn architecture(&self) -> CoreArchitecture {
- self.arch
+ pub fn new(address: u64, func: Option<Ref<Function>>, arch: Option<CoreArchitecture>) -> Self {
+ Self {
+ func,
+ arch,
+ address,
+ }
}
}
@@ -56,19 +73,27 @@ impl<'a> CodeReference {
impl CoreArrayProvider for CodeReference {
type Raw = BNReferenceSource;
type Context = ();
- type Wrapped<'a> = Guard<'a, CodeReference>;
+ type Wrapped<'a> = Self;
}
unsafe impl CoreArrayProviderInner for CodeReference {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeCodeReferences(raw, count)
}
+
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
- Guard::new(CodeReference::new(raw), &())
+ CodeReference::from_raw(raw)
}
}
-// Data Reference Array<T> boilerplate
+// TODO: This only exists so that Array can free.
+// TODO: Is there any way we can have this instead be Array<Location> of some sort?
+/// A struct representing a single data cross-reference.
+/// Data references have no associated metadata, so this object has only
+/// a single [`DataReference::address`] attribute.
+pub struct DataReference {
+ pub address: u64,
+}
impl CoreArrayProvider for DataReference {
type Raw = u64;
@@ -80,6 +105,7 @@ unsafe impl CoreArrayProviderInner for DataReference {
unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
BNFreeDataReferences(raw)
}
+
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
DataReference { address: *raw }
}
diff --git a/rust/src/relocation.rs b/rust/src/relocation.rs
index f9ece043..8fa9a86b 100644
--- a/rust/src/relocation.rs
+++ b/rust/src/relocation.rs
@@ -1,9 +1,9 @@
+use crate::low_level_il::RegularLowLevelILFunction;
use crate::rc::Guard;
use crate::string::BnStrCompatible;
use crate::{
- architecture::{Architecture, CoreArchitecture},
- binaryview::BinaryView,
- llil,
+ architecture::CoreArchitecture,
+ binary_view::BinaryView,
rc::{CoreArrayProvider, CoreArrayProviderInner, Ref, RefCountable},
symbol::Symbol,
};
@@ -168,6 +168,7 @@ impl RelocationInfo {
target: self.target,
dataRelocation: self.data_relocation,
relocationDataCache: self.relocation_data_cache,
+ // TODO: How to handle this?
prev: core::ptr::null_mut(),
next: core::ptr::null_mut(),
}
@@ -180,6 +181,9 @@ impl Default for RelocationInfo {
}
}
+// TODO: There is NO freeing of the relocation
+// TODO: A quick look it seem that the relocation ptr is always not owned so this is _fine_
+// TODO: REALLY need to come back to this at some point.
pub struct Relocation(*mut BNRelocation);
impl Relocation {
@@ -228,6 +232,7 @@ unsafe impl CoreArrayProviderInner for Relocation {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeRelocationList(raw, count);
}
+
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
Guard::new(Relocation(*raw), &())
}
@@ -259,7 +264,8 @@ pub trait RelocationHandler: 'static + Sized + AsRef<CoreRelocationHandler> {
&self,
_data: &[u8],
_addr: u64,
- _il: &llil::RegularFunction<CoreArchitecture>,
+ // TODO: Are we sure this is not a liftedilfunction?
+ _il: &RegularLowLevelILFunction<CoreArchitecture>,
_reloc: &Relocation,
) -> RelocationOperand {
RelocationOperand::AutocoerceExternPtr
@@ -280,7 +286,7 @@ pub trait RelocationHandlerExt: RelocationHandler {
BNRelocationHandlerDefaultApplyRelocation(
self.as_ref().0,
bv.handle,
- arch.handle().as_ref().0,
+ arch.handle,
reloc.0,
dest.as_mut_ptr(),
dest.len(),
@@ -323,7 +329,7 @@ impl RelocationHandler for CoreRelocationHandler {
BNRelocationHandlerGetRelocationInfo(
self.0,
bv.handle,
- arch.handle().as_ref().0,
+ arch.handle,
raw_info.as_mut_ptr(),
raw_info.len(),
)
@@ -345,7 +351,7 @@ impl RelocationHandler for CoreRelocationHandler {
BNRelocationHandlerApplyRelocation(
self.0,
bv.handle,
- arch.handle().as_ref().0,
+ arch.handle,
reloc.0,
dest.as_mut_ptr(),
dest.len(),
@@ -357,7 +363,7 @@ impl RelocationHandler for CoreRelocationHandler {
&self,
data: &[u8],
addr: u64,
- il: &llil::RegularFunction<CoreArchitecture>,
+ il: &RegularLowLevelILFunction<CoreArchitecture>,
reloc: &Relocation,
) -> RelocationOperand {
unsafe {
@@ -430,7 +436,7 @@ where
R: 'static + RelocationHandler<Handle = CustomRelocationHandlerHandle<R>> + Send + Sync,
{
let custom_handler = unsafe { &*(ctxt as *mut R) };
- let bv = unsafe { BinaryView::from_raw(BNNewViewReference(bv)) };
+ let bv = unsafe { BinaryView::ref_from_raw(BNNewViewReference(bv)) };
let arch = unsafe { CoreArchitecture::from_raw(arch) };
let result = unsafe { core::slice::from_raw_parts_mut(result, count) };
let mut info = result
@@ -457,7 +463,7 @@ where
R: 'static + RelocationHandler<Handle = CustomRelocationHandlerHandle<R>> + Send + Sync,
{
let custom_handler = unsafe { &*(ctxt as *mut R) };
- let bv = unsafe { BinaryView::from_raw(BNNewViewReference(bv)) };
+ let bv = unsafe { BinaryView::ref_from_raw(BNNewViewReference(bv)) };
let arch = unsafe { CoreArchitecture::from_raw(arch) };
let reloc = unsafe { Relocation::from_raw(reloc) };
let dest = unsafe { core::slice::from_raw_parts_mut(dest, len) };
@@ -490,8 +496,7 @@ where
return RelocationOperand::Invalid.into();
}
let arch = unsafe { CoreArchitecture::from_raw(arch) };
-
- let il = unsafe { llil::RegularFunction::from_raw(arch, il) };
+ let il = unsafe { RegularLowLevelILFunction::from_raw(arch, il) };
custom_handler
.get_operand_for_external_relocation(data, addr, &il, &reloc)
@@ -500,7 +505,9 @@ where
let name = name.into_bytes_with_nul();
- let raw = Box::leak(Box::new(MaybeUninit::<RelocationHandlerBuilder<_>>::zeroed()));
+ let raw = Box::leak(Box::new(
+ MaybeUninit::<RelocationHandlerBuilder<_>>::zeroed(),
+ ));
let mut custom_handler = BNCustomRelocationHandler {
context: raw.as_mut_ptr() as *mut _,
freeObject: Some(cb_free::<R>),
@@ -521,7 +528,7 @@ where
});
BNArchitectureRegisterRelocationHandler(
- arch.handle().as_ref().0,
+ arch.handle,
name.as_ref().as_ptr() as *const _,
handle.handle().as_ref().0,
);
diff --git a/rust/src/section.rs b/rust/src/section.rs
index 1e45db8b..1a03ca36 100644
--- a/rust/src/section.rs
+++ b/rust/src/section.rs
@@ -19,7 +19,7 @@ use std::ops::Range;
use binaryninjacore_sys::*;
-use crate::binaryview::BinaryView;
+use crate::binary_view::BinaryView;
use crate::rc::*;
use crate::string::*;
@@ -66,15 +66,21 @@ pub struct Section {
}
impl Section {
- pub(crate) unsafe fn from_raw(raw: *mut BNSection) -> Self {
- Self { handle: raw }
+ unsafe fn from_raw(handle: *mut BNSection) -> Self {
+ debug_assert!(!handle.is_null());
+ Self { handle }
+ }
+
+ pub(crate) unsafe fn ref_from_raw(handle: *mut BNSection) -> Ref<Self> {
+ debug_assert!(!handle.is_null());
+ Ref::new(Self { handle })
}
/// You need to create a section builder, customize that section, then add it to a binary view:
///
/// ```no_run
/// # use binaryninja::section::Section;
- /// # use binaryninja::binaryview::BinaryViewExt;
+ /// # use binaryninja::binary_view::BinaryViewExt;
/// let bv = binaryninja::load("example").unwrap();
/// bv.add_section(Section::builder("example", 0..1024).align(4).entry_size(4))
/// ```
@@ -103,7 +109,7 @@ impl Section {
}
pub fn is_empty(&self) -> bool {
- unsafe { BNSectionGetLength(self.handle) as usize == 0 }
+ self.len() == 0
}
pub fn address_range(&self) -> Range<u64> {
@@ -141,13 +147,16 @@ impl Section {
impl fmt::Debug for Section {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- write!(
- f,
- "<section '{}' @ {:x}-{:x}>",
- self.name(),
- self.start(),
- self.end()
- )
+ f.debug_struct("Section")
+ .field("name", &self.name())
+ .field("address_range", &self.address_range())
+ .field("section_type", &self.section_type())
+ .field("semantics", &self.semantics())
+ .field("linked_section", &self.linked_section())
+ .field("align", &self.align())
+ .field("entry_size", &self.entry_size())
+ .field("auto_defined", &self.auto_defined())
+ .finish()
}
}
@@ -181,6 +190,7 @@ unsafe impl CoreArrayProviderInner for Section {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeSectionList(raw, count);
}
+
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
Guard::new(Section::from_raw(*raw), context)
}
@@ -266,7 +276,7 @@ impl<S: BnStrCompatible> SectionBuilder<S> {
let len = self.range.end.wrapping_sub(start);
unsafe {
- let nul_str = std::ffi::CStr::from_bytes_with_nul_unchecked(b"\x00").as_ptr();
+ let nul_str = c"".as_ptr();
let name_ptr = name.as_ref().as_ptr() as *mut _;
let ty_ptr = ty.map_or(nul_str, |s| s.as_ref().as_ptr() as *mut _);
let linked_section_ptr =
diff --git a/rust/src/segment.rs b/rust/src/segment.rs
index 4c06a7ba..c6ad0c34 100644
--- a/rust/src/segment.rs
+++ b/rust/src/segment.rs
@@ -15,10 +15,11 @@
//! Labeled segments in a binary file that aren't loaded in to memory
use binaryninjacore_sys::*;
+use std::fmt::{Debug, Formatter};
use std::ops::Range;
-use crate::binaryview::BinaryView;
+use crate::binary_view::BinaryView;
use crate::rc::*;
fn set_bit(val: u32, bit_mask: u32, new_val: bool) -> u32 {
@@ -111,15 +112,21 @@ pub struct Segment {
}
impl Segment {
- pub(crate) unsafe fn from_raw(raw: *mut BNSegment) -> Self {
- Self { handle: raw }
+ pub(crate) unsafe fn from_raw(handle: *mut BNSegment) -> Self {
+ assert!(!handle.is_null());
+ Self { handle }
+ }
+
+ pub(crate) unsafe fn ref_from_raw(handle: *mut BNSegment) -> Ref<Self> {
+ assert!(!handle.is_null());
+ Ref::new(Self { handle })
}
/// You need to create a segment builder, customize that segment, then add it to a binary view:
///
/// ```no_run
/// # use binaryninja::segment::Segment;
- /// # use binaryninja::binaryview::BinaryViewExt;
+ /// # use binaryninja::binary_view::BinaryViewExt;
/// let bv = binaryninja::load("example").unwrap();
/// bv.add_segment(Segment::builder(0..0x1000).writable(true).readable(true))
/// ```
@@ -181,6 +188,23 @@ impl Segment {
}
}
+impl Debug for Segment {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("Segment")
+ .field("address_range", &self.address_range())
+ .field("parent_backing", &self.parent_backing())
+ .field("executable", &self.executable())
+ .field("writable", &self.writable())
+ .field("readable", &self.readable())
+ .field("contains_data", &self.contains_data())
+ .field("contains_code", &self.contains_code())
+ .field("deny_write", &self.deny_write())
+ .field("deny_execute", &self.deny_execute())
+ .field("auto_defined", &self.auto_defined())
+ .finish()
+ }
+}
+
impl ToOwned for Segment {
type Owned = Ref<Self>;
@@ -211,6 +235,7 @@ unsafe impl CoreArrayProviderInner for Segment {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeSegmentList(raw, count);
}
+
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
Guard::new(Segment::from_raw(*raw), context)
}
diff --git a/rust/src/settings.rs b/rust/src/settings.rs
index f37a5a34..8bc8565f 100644
--- a/rust/src/settings.rs
+++ b/rust/src/settings.rs
@@ -14,38 +14,41 @@
//! An interface for reading, writing, and creating new settings
-pub use binaryninjacore_sys::BNSettingsScope as SettingsScope;
use binaryninjacore_sys::*;
-use std::os::raw::c_char;
+use std::ffi::c_char;
+use std::fmt::Debug;
-use crate::binaryview::BinaryView;
+use crate::binary_view::BinaryView;
use crate::rc::*;
use crate::string::{BnStrCompatible, BnString};
-use std::ptr;
+use crate::function::Function;
+
+pub type SettingsScope = BNSettingsScope;
+
+pub const DEFAULT_INSTANCE_ID: &str = "default";
+pub const GLOBAL_INSTANCE_ID: &str = "";
#[derive(PartialEq, Eq, Hash)]
pub struct Settings {
pub(crate) handle: *mut BNSettings,
}
-unsafe impl Send for Settings {}
-unsafe impl Sync for Settings {}
-
impl Settings {
pub(crate) unsafe fn from_raw(handle: *mut BNSettings) -> Ref<Self> {
debug_assert!(!handle.is_null());
-
Ref::new(Self { handle })
}
- pub fn new<S: BnStrCompatible>(instance_id: S) -> Ref<Self> {
+ pub fn new() -> Ref<Self> {
+ Self::new_with_id(GLOBAL_INSTANCE_ID)
+ }
+
+ pub fn new_with_id<S: BnStrCompatible>(instance_id: S) -> Ref<Self> {
let instance_id = instance_id.into_bytes_with_nul();
unsafe {
let handle = BNCreateSettings(instance_id.as_ref().as_ptr() as *mut _);
-
debug_assert!(!handle.is_null());
-
Ref::new(Self { handle })
}
}
@@ -60,12 +63,20 @@ impl Settings {
}
pub fn deserialize_schema<S: BnStrCompatible>(&self, schema: S) -> bool {
+ self.deserialize_schema_with_scope(schema, SettingsScope::SettingsAutoScope)
+ }
+
+ pub fn deserialize_schema_with_scope<S: BnStrCompatible>(
+ &self,
+ schema: S,
+ scope: SettingsScope,
+ ) -> bool {
let schema = schema.into_bytes_with_nul();
unsafe {
BNSettingsDeserializeSchema(
self.handle,
schema.as_ref().as_ptr() as *mut _,
- BNSettingsScope::SettingsAutoScope,
+ scope,
true,
)
}
@@ -77,126 +88,148 @@ impl Settings {
unsafe { BNSettingsContains(self.handle, key.as_ref().as_ptr() as *mut _) }
}
+ pub fn keys(&self) -> Array<BnString> {
+ let mut count = 0;
+ let result = unsafe { BNSettingsKeysList(self.handle, &mut count) };
+ assert!(!result.is_null());
+ unsafe { Array::new(result as *mut *mut c_char, count, ()) }
+ }
+
// TODO Update the settings API to take an optional BinaryView or Function. Separate functions or...?
- pub fn get_bool<S: BnStrCompatible>(
+ pub fn get_bool<S: BnStrCompatible>(&self, key: S) -> bool {
+ self.get_bool_with_opts(key, &mut QueryOptions::default())
+ }
+
+ pub fn get_bool_with_opts<S: BnStrCompatible>(
&self,
key: S,
- view: Option<&BinaryView>,
- scope: Option<Box<SettingsScope>>,
+ options: &mut QueryOptions,
) -> bool {
let key = key.into_bytes_with_nul();
- let view_handle = match view {
+ let view_ptr = match options.view.as_ref() {
Some(view) => view.handle,
- _ => ptr::null_mut() as *mut _,
+ _ => std::ptr::null_mut(),
};
- let scope_ptr = match scope {
- Some(mut scope) => scope.as_mut(),
- _ => ptr::null_mut() as *mut _,
+ let func_ptr = match options.function.as_ref() {
+ Some(func) => func.handle,
+ _ => std::ptr::null_mut(),
};
unsafe {
BNSettingsGetBool(
self.handle,
key.as_ref().as_ptr() as *mut _,
- view_handle,
- std::ptr::null_mut(),
- scope_ptr,
+ view_ptr,
+ func_ptr,
+ &mut options.scope,
)
}
}
- pub fn get_double<S: BnStrCompatible>(
+ pub fn get_double<S: BnStrCompatible>(&self, key: S) -> f64 {
+ self.get_double_with_opts(key, &mut QueryOptions::default())
+ }
+
+ pub fn get_double_with_opts<S: BnStrCompatible>(
&self,
key: S,
- view: Option<&BinaryView>,
- scope: Option<Box<SettingsScope>>,
+ options: &mut QueryOptions,
) -> f64 {
let key = key.into_bytes_with_nul();
- let view_handle = match view {
+ let view_ptr = match options.view.as_ref() {
Some(view) => view.handle,
- _ => ptr::null_mut() as *mut _,
+ _ => std::ptr::null_mut(),
};
- let scope_ptr = match scope {
- Some(mut scope) => scope.as_mut(),
- _ => ptr::null_mut() as *mut _,
+ let func_ptr = match options.function.as_ref() {
+ Some(func) => func.handle,
+ _ => std::ptr::null_mut(),
};
unsafe {
BNSettingsGetDouble(
self.handle,
key.as_ref().as_ptr() as *mut _,
- view_handle,
- std::ptr::null_mut(),
- scope_ptr,
+ view_ptr,
+ func_ptr,
+ &mut options.scope,
)
}
}
- pub fn get_integer<S: BnStrCompatible>(
+ pub fn get_integer<S: BnStrCompatible>(&self, key: S) -> u64 {
+ self.get_integer_with_opts(key, &mut QueryOptions::default())
+ }
+
+ pub fn get_integer_with_opts<S: BnStrCompatible>(
&self,
key: S,
- view: Option<&BinaryView>,
- scope: Option<Box<SettingsScope>>,
+ options: &mut QueryOptions,
) -> u64 {
let key = key.into_bytes_with_nul();
- let view_handle = match view {
+ let view_ptr = match options.view.as_ref() {
Some(view) => view.handle,
- _ => ptr::null_mut() as *mut _,
+ _ => std::ptr::null_mut(),
};
- let scope_ptr = match scope {
- Some(mut scope) => scope.as_mut(),
- _ => ptr::null_mut() as *mut _,
+ let func_ptr = match options.function.as_ref() {
+ Some(func) => func.handle,
+ _ => std::ptr::null_mut(),
};
unsafe {
BNSettingsGetUInt64(
self.handle,
key.as_ref().as_ptr() as *mut _,
- view_handle,
- std::ptr::null_mut(),
- scope_ptr,
+ view_ptr,
+ func_ptr,
+ &mut options.scope,
)
}
}
- pub fn get_string<S: BnStrCompatible>(
+ pub fn get_string<S: BnStrCompatible>(&self, key: S) -> BnString {
+ self.get_string_with_opts(key, &mut QueryOptions::default())
+ }
+
+ pub fn get_string_with_opts<S: BnStrCompatible>(
&self,
key: S,
- view: Option<&BinaryView>,
- scope: Option<Box<SettingsScope>>,
+ options: &mut QueryOptions,
) -> BnString {
let key = key.into_bytes_with_nul();
- let view_handle = match view {
+ let view_ptr = match options.view.as_ref() {
Some(view) => view.handle,
- _ => ptr::null_mut() as *mut _,
+ _ => std::ptr::null_mut(),
};
- let scope_ptr = match scope {
- Some(mut scope) => scope.as_mut(),
- _ => ptr::null_mut() as *mut _,
+ let func_ptr = match options.function.as_ref() {
+ Some(func) => func.handle,
+ _ => std::ptr::null_mut(),
};
unsafe {
BnString::from_raw(BNSettingsGetString(
self.handle,
key.as_ref().as_ptr() as *mut _,
- view_handle,
- std::ptr::null_mut(),
- scope_ptr,
+ view_ptr,
+ func_ptr,
+ &mut options.scope,
))
}
}
- pub fn get_string_list<S: BnStrCompatible>(
+ pub fn get_string_list<S: BnStrCompatible>(&self, key: S) -> Array<BnString> {
+ self.get_string_list_with_opts(key, &mut QueryOptions::default())
+ }
+
+ pub fn get_string_list_with_opts<S: BnStrCompatible>(
&self,
key: S,
- view: Option<&BinaryView>,
- scope: Option<Box<SettingsScope>>,
+ options: &mut QueryOptions,
) -> Array<BnString> {
let key = key.into_bytes_with_nul();
- let view_handle = match view {
+ let view_ptr = match options.view.as_ref() {
Some(view) => view.handle,
- _ => ptr::null_mut() as *mut _,
+ _ => std::ptr::null_mut(),
};
- let scope_ptr = match scope {
- Some(mut scope) => scope.as_mut(),
- _ => ptr::null_mut() as *mut _,
+ let func_ptr = match options.function.as_ref() {
+ Some(func) => func.handle,
+ _ => std::ptr::null_mut(),
};
let mut size: usize = 0;
unsafe {
@@ -204,9 +237,9 @@ impl Settings {
BNSettingsGetStringList(
self.handle,
key.as_ref().as_ptr() as *mut _,
- view_handle,
- std::ptr::null_mut(),
- scope_ptr,
+ view_ptr,
+ func_ptr,
+ &mut options.scope,
&mut size,
) as *mut *mut c_char,
size,
@@ -215,139 +248,153 @@ impl Settings {
}
}
- pub fn get_json<S: BnStrCompatible>(
+ pub fn get_json<S: BnStrCompatible>(&self, key: S) -> BnString {
+ self.get_json_with_opts(key, &mut QueryOptions::default())
+ }
+
+ pub fn get_json_with_opts<S: BnStrCompatible>(
&self,
key: S,
- view: Option<&BinaryView>,
- scope: Option<Box<SettingsScope>>,
+ options: &mut QueryOptions,
) -> BnString {
let key = key.into_bytes_with_nul();
- let view_handle = match view {
+ let view_ptr = match options.view.as_ref() {
Some(view) => view.handle,
- _ => ptr::null_mut() as *mut _,
+ _ => std::ptr::null_mut(),
};
- let scope_ptr = match scope {
- Some(mut scope) => scope.as_mut(),
- _ => ptr::null_mut() as *mut _,
+ let func_ptr = match options.function.as_ref() {
+ Some(func) => func.handle,
+ _ => std::ptr::null_mut(),
};
unsafe {
BnString::from_raw(BNSettingsGetJson(
self.handle,
key.as_ref().as_ptr() as *mut _,
- view_handle,
- std::ptr::null_mut(),
- scope_ptr,
+ view_ptr,
+ func_ptr,
+ &mut options.scope,
))
}
}
- pub fn set_bool<S: BnStrCompatible>(
+ pub fn set_bool<S: BnStrCompatible>(&self, key: S, value: bool) {
+ self.set_bool_with_opts(key, value, &QueryOptions::default())
+ }
+
+ pub fn set_bool_with_opts<S: BnStrCompatible>(
&self,
key: S,
value: bool,
- view: Option<&BinaryView>,
- scope: Option<SettingsScope>,
+ options: &QueryOptions,
) {
let key = key.into_bytes_with_nul();
- let view_handle = match view {
+ let view_ptr = match options.view.as_ref() {
Some(view) => view.handle,
- _ => ptr::null_mut() as *mut _,
+ _ => std::ptr::null_mut(),
};
- let scope = match scope {
- Some(scope) => scope,
- _ => SettingsScope::SettingsAutoScope,
+ let func_ptr = match options.function.as_ref() {
+ Some(func) => func.handle,
+ _ => std::ptr::null_mut(),
};
unsafe {
BNSettingsSetBool(
self.handle,
- view_handle,
- std::ptr::null_mut(),
- scope,
+ view_ptr,
+ func_ptr,
+ options.scope,
key.as_ref().as_ptr() as *mut _,
value,
);
}
}
- pub fn set_double<S: BnStrCompatible>(
+ pub fn set_double<S: BnStrCompatible>(&self, key: S, value: f64) {
+ self.set_double_with_opts(key, value, &QueryOptions::default())
+ }
+ pub fn set_double_with_opts<S: BnStrCompatible>(
&self,
key: S,
value: f64,
- view: Option<&BinaryView>,
- scope: Option<SettingsScope>,
+ options: &QueryOptions,
) {
let key = key.into_bytes_with_nul();
- let view_handle = match view {
+ let view_ptr = match options.view.as_ref() {
Some(view) => view.handle,
- _ => ptr::null_mut() as *mut _,
+ _ => std::ptr::null_mut(),
};
- let scope = match scope {
- Some(scope) => scope,
- _ => SettingsScope::SettingsAutoScope,
+ let func_ptr = match options.function.as_ref() {
+ Some(func) => func.handle,
+ _ => std::ptr::null_mut(),
};
unsafe {
BNSettingsSetDouble(
self.handle,
- view_handle,
- std::ptr::null_mut(),
- scope,
+ view_ptr,
+ func_ptr,
+ options.scope,
key.as_ref().as_ptr() as *mut _,
value,
);
}
}
- pub fn set_integer<S: BnStrCompatible>(
+ pub fn set_integer<S: BnStrCompatible>(&self, key: S, value: u64) {
+ self.set_integer_with_opts(key, value, &QueryOptions::default())
+ }
+
+ pub fn set_integer_with_opts<S: BnStrCompatible>(
&self,
key: S,
value: u64,
- view: Option<&BinaryView>,
- scope: Option<SettingsScope>,
+ options: &QueryOptions,
) {
let key = key.into_bytes_with_nul();
- let view_handle = match view {
+ let view_ptr = match options.view.as_ref() {
Some(view) => view.handle,
- _ => ptr::null_mut() as *mut _,
+ _ => std::ptr::null_mut(),
};
- let scope = match scope {
- Some(scope) => scope,
- _ => SettingsScope::SettingsAutoScope,
+ let func_ptr = match options.function.as_ref() {
+ Some(func) => func.handle,
+ _ => std::ptr::null_mut(),
};
unsafe {
BNSettingsSetUInt64(
self.handle,
- view_handle,
- std::ptr::null_mut(),
- scope,
+ view_ptr,
+ func_ptr,
+ options.scope,
key.as_ref().as_ptr() as *mut _,
value,
);
}
}
- pub fn set_string<S1: BnStrCompatible, S2: BnStrCompatible>(
+ pub fn set_string<S1: BnStrCompatible, S2: BnStrCompatible>(&self, key: S1, value: S2) {
+ self.set_string_with_opts(key, value, &QueryOptions::default())
+ }
+
+ pub fn set_string_with_opts<S1: BnStrCompatible, S2: BnStrCompatible>(
&self,
key: S1,
value: S2,
- view: Option<&BinaryView>,
- scope: Option<SettingsScope>,
+ options: &QueryOptions,
) {
let key = key.into_bytes_with_nul();
let value = value.into_bytes_with_nul();
- let view_handle = match view {
+ let view_ptr = match options.view.as_ref() {
Some(view) => view.handle,
- _ => ptr::null_mut() as *mut _,
+ _ => std::ptr::null_mut(),
};
- let scope = match scope {
- Some(scope) => scope,
- _ => SettingsScope::SettingsAutoScope,
+ let func_ptr = match options.function.as_ref() {
+ Some(func) => func.handle,
+ _ => std::ptr::null_mut(),
};
unsafe {
BNSettingsSetString(
self.handle,
- view_handle,
- std::ptr::null_mut(),
- scope,
+ view_ptr,
+ func_ptr,
+ options.scope,
key.as_ref().as_ptr() as *mut _,
value.as_ref().as_ptr() as *mut _,
);
@@ -358,69 +405,114 @@ impl Settings {
&self,
key: S1,
value: I,
- view: Option<&BinaryView>,
- scope: Option<SettingsScope>,
+ ) -> bool {
+ self.set_string_list_with_opts(key, value, &QueryOptions::default())
+ }
+
+ pub fn set_string_list_with_opts<
+ S1: BnStrCompatible,
+ S2: BnStrCompatible,
+ I: Iterator<Item = S2>,
+ >(
+ &self,
+ key: S1,
+ value: I,
+ options: &QueryOptions,
) -> bool {
let key = key.into_bytes_with_nul();
- let mut list: Vec<_> = value
- .map(|s| s.into_bytes_with_nul().as_ref().as_ptr() as *const c_char)
+ let raw_list: Vec<_> = value.map(|s| s.into_bytes_with_nul()).collect();
+ let mut raw_list_ptr: Vec<_> = raw_list
+ .iter()
+ .map(|s| s.as_ref().as_ptr() as *const c_char)
.collect();
- let view_handle = match view {
+ let view_ptr = match options.view.as_ref() {
Some(view) => view.handle,
- None => ptr::null_mut() as *mut _,
+ _ => std::ptr::null_mut(),
};
-
- let scope = match scope {
- Some(scope) => scope,
- None => SettingsScope::SettingsAutoScope,
+ let func_ptr = match options.function.as_ref() {
+ Some(func) => func.handle,
+ _ => std::ptr::null_mut(),
};
-
unsafe {
BNSettingsSetStringList(
self.handle,
- view_handle,
- std::ptr::null_mut(),
- scope,
+ view_ptr,
+ func_ptr,
+ options.scope,
key.as_ref().as_ptr() as *mut _,
- list.as_mut_ptr(),
- list.len(),
+ raw_list_ptr.as_mut_ptr(),
+ raw_list_ptr.len(),
)
}
}
- pub fn set_json<S1: BnStrCompatible, S2: BnStrCompatible>(
+ pub fn set_json<S1: BnStrCompatible, S2: BnStrCompatible>(&self, key: S1, value: S2) -> bool {
+ self.set_json_with_opts(key, value, &QueryOptions::default())
+ }
+
+ pub fn set_json_with_opts<S1: BnStrCompatible, S2: BnStrCompatible>(
&self,
key: S1,
value: S2,
- view: Option<&BinaryView>,
- scope: Option<SettingsScope>,
+ options: &QueryOptions,
) -> bool {
let key = key.into_bytes_with_nul();
let value = value.into_bytes_with_nul();
-
- let view_handle = match view {
+ let view_ptr = match options.view.as_ref() {
Some(view) => view.handle,
- None => ptr::null_mut() as *mut _,
+ _ => std::ptr::null_mut(),
};
-
- let scope = match scope {
- Some(scope) => scope,
- None => SettingsScope::SettingsAutoScope,
+ let func_ptr = match options.function.as_ref() {
+ Some(func) => func.handle,
+ _ => std::ptr::null_mut(),
};
-
unsafe {
BNSettingsSetJson(
self.handle,
- view_handle,
- std::ptr::null_mut(),
- scope,
+ view_ptr,
+ func_ptr,
+ options.scope,
key.as_ref().as_ptr() as *mut _,
value.as_ref().as_ptr() as *mut _,
)
}
}
+ pub fn get_property_string<S: BnStrCompatible>(&self, key: S, property: S) -> BnString {
+ let key = key.into_bytes_with_nul();
+ let property = property.into_bytes_with_nul();
+ unsafe {
+ BnString::from_raw(BNSettingsQueryPropertyString(
+ self.handle,
+ key.as_ref().as_ptr() as *mut _,
+ property.as_ref().as_ptr() as *mut _,
+ ))
+ }
+ }
+
+ pub fn get_property_string_list<S: BnStrCompatible>(
+ &self,
+ key: S,
+ property: S,
+ ) -> Array<BnString> {
+ let key = key.into_bytes_with_nul();
+ let property = property.into_bytes_with_nul();
+ let mut size: usize = 0;
+ unsafe {
+ Array::new(
+ BNSettingsQueryPropertyStringList(
+ self.handle,
+ key.as_ref().as_ptr() as *mut _,
+ property.as_ref().as_ptr() as *mut _,
+ &mut size,
+ ) as *mut *mut c_char,
+ size,
+ (),
+ )
+ }
+ }
+
pub fn update_bool_property<S: BnStrCompatible>(&self, key: S, property: S, value: bool) {
let key = key.into_bytes_with_nul();
let property = property.into_bytes_with_nul();
@@ -482,8 +574,10 @@ impl Settings {
) {
let key = key.into_bytes_with_nul();
let property = property.into_bytes_with_nul();
- let mut list: Vec<_> = value
- .map(|s| s.into_bytes_with_nul().as_ref().as_ptr() as *const c_char)
+ let raw_list: Vec<_> = value.map(|s| s.into_bytes_with_nul()).collect();
+ let mut raw_list_ptr: Vec<_> = raw_list
+ .iter()
+ .map(|s| s.as_ref().as_ptr() as *const c_char)
.collect();
unsafe {
@@ -491,8 +585,8 @@ impl Settings {
self.handle,
key.as_ref().as_ptr() as *mut _,
property.as_ref().as_ptr() as *mut _,
- list.as_mut_ptr(),
- list.len(),
+ raw_list_ptr.as_mut_ptr(),
+ raw_list_ptr.len(),
);
}
}
@@ -534,6 +628,15 @@ impl Settings {
// TODO: register_setting but type-safely turn it into json
}
+impl Default for Ref<Settings> {
+ fn default() -> Self {
+ Settings::new_with_id(DEFAULT_INSTANCE_ID)
+ }
+}
+
+unsafe impl Send for Settings {}
+unsafe impl Sync for Settings {}
+
impl ToOwned for Settings {
type Owned = Ref<Self>;
@@ -553,3 +656,57 @@ unsafe impl RefCountable for Settings {
BNFreeSettings(handle.handle);
}
}
+
+#[derive(Debug, Clone)]
+pub struct QueryOptions<'a> {
+ pub scope: SettingsScope,
+ pub view: Option<&'a BinaryView>,
+ pub function: Option<Ref<Function>>,
+}
+
+impl<'a> QueryOptions<'a> {
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ pub fn new_with_view(view: &'a BinaryView) -> Self {
+ Self {
+ view: Some(view),
+ ..Default::default()
+ }
+ }
+
+ pub fn new_with_func(func: Ref<Function>) -> Self {
+ Self {
+ function: Some(func),
+ ..Default::default()
+ }
+ }
+
+ /// Set the query to target a specific view, this will be overridden if a function is targeted.
+ pub fn with_view(mut self, view: &'a BinaryView) -> Self {
+ self.view = Some(view);
+ self
+ }
+
+ pub fn with_scope(mut self, scope: SettingsScope) -> Self {
+ self.scope = scope;
+ self
+ }
+
+ /// Set the query to target a specific function, this will override the target view.
+ pub fn with_function(mut self, function: Ref<Function>) -> Self {
+ self.function = Some(function);
+ self
+ }
+}
+
+impl Default for QueryOptions<'_> {
+ fn default() -> Self {
+ Self {
+ view: None,
+ scope: SettingsScope::SettingsDefaultScope,
+ function: None,
+ }
+ }
+}
diff --git a/rust/src/string.rs b/rust/src/string.rs
index bfb998d5..dde8f550 100644
--- a/rust/src/string.rs
+++ b/rust/src/string.rs
@@ -14,18 +14,18 @@
//! String wrappers for core-owned strings and strings being passed to the core
+use crate::rc::*;
+use crate::types::QualifiedName;
use std::borrow::Cow;
-use std::ffi::{CStr, CString};
+use std::ffi::{c_char, CStr, CString};
use std::fmt;
use std::hash::{Hash, Hasher};
use std::mem;
use std::ops::Deref;
-use std::os::raw;
-
-use crate::rc::*;
-use crate::types::QualifiedName;
+use std::path::{Path, PathBuf};
-pub(crate) fn raw_to_string(ptr: *const raw::c_char) -> Option<String> {
+// TODO: Remove or refactor this.
+pub(crate) fn raw_to_string(ptr: *const c_char) -> Option<String> {
if ptr.is_null() {
None
} else {
@@ -33,11 +33,21 @@ pub(crate) fn raw_to_string(ptr: *const raw::c_char) -> Option<String> {
}
}
-/// Is the quivalent of `core::ffi::CString` but using the allocation and free
-/// functions provided by binaryninja_sys.
+// TODO: Make this pass in an iterator over something more generic...
+pub(crate) fn strings_to_string_list(strings: &[String]) -> *mut *mut c_char {
+ use binaryninjacore_sys::BNAllocStringList;
+ let bn_str_list = strings
+ .iter()
+ .map(|s| BnString::new(s.as_str()))
+ .collect::<Vec<_>>();
+ let mut raw_str_list = bn_str_list.iter().map(|s| s.as_ptr()).collect::<Vec<_>>();
+ unsafe { BNAllocStringList(raw_str_list.as_mut_ptr(), raw_str_list.len()) }
+}
+
+/// Is the equivalent of `core::ffi::CString` but using the alloc and free from `binaryninjacore-sys`.
#[repr(transparent)]
pub struct BnString {
- raw: *mut raw::c_char,
+ raw: *mut c_char,
}
/// A nul-terminated C string allocated by the core.
@@ -52,37 +62,32 @@ pub struct BnString {
impl BnString {
pub fn new<S: BnStrCompatible>(s: S) -> Self {
use binaryninjacore_sys::BNAllocString;
-
let raw = s.into_bytes_with_nul();
-
unsafe {
let ptr = raw.as_ref().as_ptr() as *mut _;
-
- Self {
- raw: BNAllocString(ptr),
- }
+ Self::from_raw(BNAllocString(ptr))
}
}
/// Construct a BnString from an owned const char* allocated by BNAllocString
- pub(crate) unsafe fn from_raw(raw: *mut raw::c_char) -> Self {
+ pub(crate) unsafe fn from_raw(raw: *mut c_char) -> Self {
Self { raw }
}
- pub(crate) fn into_raw(self) -> *mut raw::c_char {
- let res = self.raw;
-
+ /// Consumes the `BnString`, returning a raw pointer to the string.
+ ///
+ /// After calling this function, the caller is responsible for the
+ /// memory previously managed by the `BnString`.
+ ///
+ /// This is typically used to pass a string back through the core where the core is expected to free.
+ pub fn into_raw(value: Self) -> *mut c_char {
+ let res = value.raw;
// we're surrendering ownership over the *mut c_char to
// the core, so ensure we don't free it
- mem::forget(self);
-
+ mem::forget(value);
res
}
- pub(crate) fn as_raw(&self) -> &raw::c_char {
- unsafe { &*self.raw }
- }
-
pub fn as_str(&self) -> &str {
unsafe { CStr::from_ptr(self.raw).to_str().unwrap() }
}
@@ -107,7 +112,6 @@ impl BnString {
impl Drop for BnString {
fn drop(&mut self) {
use binaryninjacore_sys::BNFreeString;
-
unsafe {
BNFreeString(self.raw);
}
@@ -166,7 +170,7 @@ impl fmt::Debug for BnString {
}
impl CoreArrayProvider for BnString {
- type Raw = *mut raw::c_char;
+ type Raw = *mut c_char;
type Context = ();
type Wrapped<'a> = &'a str;
}
@@ -176,6 +180,7 @@ unsafe impl CoreArrayProviderInner for BnString {
use binaryninjacore_sys::BNFreeStringList;
BNFreeStringList(raw, count);
}
+
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
CStr::from_ptr(*raw).to_str().unwrap()
}
@@ -183,6 +188,7 @@ unsafe impl CoreArrayProviderInner for BnString {
pub unsafe trait BnStrCompatible {
type Result: AsRef<[u8]>;
+
fn into_bytes_with_nul(self) -> Self::Result;
}
@@ -210,7 +216,7 @@ unsafe impl BnStrCompatible for CString {
}
}
-unsafe impl<'a> BnStrCompatible for &'a str {
+unsafe impl BnStrCompatible for &str {
type Result = Vec<u8>;
fn into_bytes_with_nul(self) -> Self::Result {
@@ -227,7 +233,7 @@ unsafe impl BnStrCompatible for String {
}
}
-unsafe impl<'a> BnStrCompatible for &'a String {
+unsafe impl BnStrCompatible for &String {
type Result = Vec<u8>;
fn into_bytes_with_nul(self) -> Self::Result {
@@ -243,21 +249,47 @@ unsafe impl<'a> BnStrCompatible for &'a Cow<'a, str> {
}
}
+unsafe impl BnStrCompatible for Cow<'_, str> {
+ type Result = Vec<u8>;
+
+ fn into_bytes_with_nul(self) -> Self::Result {
+ self.to_string().into_bytes_with_nul()
+ }
+}
+
unsafe impl BnStrCompatible for &QualifiedName {
type Result = Vec<u8>;
fn into_bytes_with_nul(self) -> Self::Result {
- self.string().into_bytes_with_nul()
+ self.to_string().into_bytes_with_nul()
+ }
+}
+
+unsafe impl BnStrCompatible for PathBuf {
+ type Result = Vec<u8>;
+
+ fn into_bytes_with_nul(self) -> Self::Result {
+ self.as_path().into_bytes_with_nul()
+ }
+}
+
+unsafe impl BnStrCompatible for &Path {
+ type Result = Vec<u8>;
+
+ fn into_bytes_with_nul(self) -> Self::Result {
+ self.as_os_str().as_encoded_bytes().to_vec()
}
}
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)
}
diff --git a/rust/src/symbol.rs b/rust/src/symbol.rs
index d7a73a0a..21df31a7 100644
--- a/rust/src/symbol.rs
+++ b/rust/src/symbol.rs
@@ -119,12 +119,12 @@ pub struct SymbolBuilder {
}
impl SymbolBuilder {
- pub fn new(ty: SymbolType, raw_name: &str, addr: u64) -> Self {
+ pub fn new<T: Into<String>>(ty: SymbolType, raw_name: T, addr: u64) -> Self {
Self {
ty,
binding: Binding::None,
addr,
- raw_name: raw_name.to_owned(),
+ raw_name: raw_name.into(),
short_name: None,
full_name: None,
ordinal: 0,
@@ -136,13 +136,13 @@ impl SymbolBuilder {
self
}
- pub fn short_name(mut self, short_name: &str) -> Self {
- self.short_name = Some(short_name.to_owned());
+ pub fn short_name<T: Into<String>>(mut self, short_name: T) -> Self {
+ self.short_name = Some(short_name.into());
self
}
- pub fn full_name(mut self, full_name: &str) -> Self {
- self.full_name = Some(full_name.to_owned());
+ pub fn full_name<T: Into<String>>(mut self, full_name: T) -> Self {
+ self.full_name = Some(full_name.into());
self
}
@@ -233,7 +233,10 @@ impl Symbol {
/// ```no_run
/// # use binaryninja::symbol::Symbol;
/// # use binaryninja::symbol::SymbolType;
- /// Symbol::builder(SymbolType::Data, "hello", 0x1337).short_name("hello").full_name("hello").create();
+ /// 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)
@@ -267,7 +270,7 @@ impl Symbol {
unsafe { BNIsSymbolAutoDefined(self.handle) }
}
- /// Wether this symbol has external linkage
+ /// Whether this symbol has external linkage
pub fn external(&self) -> bool {
self.binding() == Binding::Weak || self.binding() == Binding::Global
}
@@ -326,6 +329,7 @@ unsafe impl CoreArrayProviderInner for Symbol {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeSymbolList(raw, count);
}
+
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
Guard::new(Symbol::from_raw(*raw), context)
}
diff --git a/rust/src/tags.rs b/rust/src/tags.rs
index 29266ed0..b378f7ed 100644
--- a/rust/src/tags.rs
+++ b/rust/src/tags.rs
@@ -15,28 +15,36 @@
//! Interfaces for creating and modifying tags in a BinaryView.
use binaryninjacore_sys::*;
+use std::fmt::{Debug, Formatter};
use crate::architecture::CoreArchitecture;
-use crate::binaryview::BinaryView;
+use crate::binary_view::BinaryView;
use crate::function::Function;
use crate::rc::*;
use crate::string::*;
+pub type TagTypeType = BNTagTypeType;
+pub type TagReferenceType = BNTagReferenceType;
+
pub struct Tag {
pub(crate) handle: *mut BNTag,
}
impl Tag {
- pub(crate) unsafe fn from_raw(handle: *mut BNTag) -> Ref<Self> {
+ pub(crate) unsafe fn from_raw(handle: *mut BNTag) -> Self {
debug_assert!(!handle.is_null());
+ Self { handle }
+ }
+ pub(crate) unsafe fn ref_from_raw(handle: *mut BNTag) -> Ref<Self> {
+ debug_assert!(!handle.is_null());
Ref::new(Self { handle })
}
pub fn new<S: BnStrCompatible>(t: &TagType, data: S) -> Ref<Self> {
let data = data.into_bytes_with_nul();
- unsafe { Self::from_raw(BNCreateTag(t.handle, data.as_ref().as_ptr() as *mut _)) }
+ unsafe { Self::ref_from_raw(BNCreateTag(t.handle, data.as_ref().as_ptr() as *mut _)) }
}
pub fn id(&self) -> BnString {
@@ -47,8 +55,8 @@ impl Tag {
unsafe { BnString::from_raw(BNTagGetData(self.handle)) }
}
- pub fn t(&self) -> Ref<TagType> {
- unsafe { TagType::from_raw(BNTagGetType(self.handle)) }
+ pub fn ty(&self) -> Ref<TagType> {
+ unsafe { TagType::ref_from_raw(BNTagGetType(self.handle)) }
}
pub fn set_data<S: BnStrCompatible>(&self, data: S) {
@@ -59,6 +67,24 @@ impl Tag {
}
}
+impl Debug for Tag {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("Tag")
+ .field("id", &self.id())
+ .field("data", &self.data())
+ .field("type", &self.ty())
+ .finish()
+ }
+}
+
+impl PartialEq for Tag {
+ fn eq(&self, other: &Self) -> bool {
+ self.id() == other.id()
+ }
+}
+
+impl Eq for Tag {}
+
unsafe impl RefCountable for Tag {
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
Ref::new(Self {
@@ -82,13 +108,14 @@ impl ToOwned for Tag {
impl CoreArrayProvider for Tag {
type Raw = *mut BNTag;
type Context = ();
- type Wrapped<'a> = Guard<'a, Self>;
+ type Wrapped<'a> = Guard<'a, Tag>;
}
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)
}
@@ -97,16 +124,13 @@ unsafe impl CoreArrayProviderInner for Tag {
unsafe impl Send for Tag {}
unsafe impl Sync for Tag {}
-pub type TagTypeType = BNTagTypeType;
-
pub struct TagType {
pub(crate) handle: *mut BNTagType,
}
impl TagType {
- pub(crate) unsafe fn from_raw(handle: *mut BNTagType) -> Ref<Self> {
+ pub(crate) unsafe fn ref_from_raw(handle: *mut BNTagType) -> Ref<Self> {
debug_assert!(!handle.is_null());
-
Ref::new(Self { handle })
}
@@ -115,7 +139,7 @@ impl TagType {
name: N,
icon: I,
) -> Ref<Self> {
- let tag_type = unsafe { Self::from_raw(BNCreateTagType(view.handle)) };
+ let tag_type = unsafe { Self::ref_from_raw(BNCreateTagType(view.handle)) };
tag_type.set_name(name);
tag_type.set_icon(icon);
tag_type
@@ -155,7 +179,7 @@ impl TagType {
unsafe { BNTagTypeSetVisible(self.handle, visible) }
}
- pub fn t(&self) -> TagTypeType {
+ pub fn ty(&self) -> TagTypeType {
unsafe { BNTagTypeGetType(self.handle) }
}
@@ -167,7 +191,19 @@ impl TagType {
}
pub fn view(&self) -> Ref<BinaryView> {
- unsafe { BinaryView::from_raw(BNTagTypeGetView(self.handle)) }
+ unsafe { BinaryView::ref_from_raw(BNTagTypeGetView(self.handle)) }
+ }
+}
+
+impl Debug for TagType {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("TagType")
+ .field("id", &self.id())
+ .field("name", &self.name())
+ .field("icon", &self.icon())
+ .field("visible", &self.visible())
+ .field("type", &self.ty())
+ .finish()
}
}
@@ -194,46 +230,27 @@ impl ToOwned for TagType {
unsafe impl Send for TagType {}
unsafe impl Sync for TagType {}
-pub type TagReferenceType = BNTagReferenceType;
-
+#[derive(Debug, Clone, PartialEq)]
pub struct TagReference {
- ref_type: TagReferenceType,
- auto_defined: bool,
- tag: Ref<Tag>,
- arch: CoreArchitecture,
- func: Ref<Function>,
- addr: u64,
+ pub arch: CoreArchitecture,
+ pub func: Ref<Function>,
+ pub addr: u64,
+ pub auto_defined: bool,
+ pub reference_type: TagReferenceType,
+ pub tag: Ref<Tag>,
}
-impl TagReference {
- unsafe fn from_borrowed_raw(value: &BNTagReference) -> Self {
+impl From<&BNTagReference> for TagReference {
+ fn from(value: &BNTagReference) -> Self {
Self {
- ref_type: value.refType,
+ reference_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(),
+ tag: unsafe { Tag::ref_from_raw(value.tag).to_owned() },
+ arch: unsafe { CoreArchitecture::from_raw(value.arch) },
+ func: unsafe { Function::from_raw(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 {
@@ -246,7 +263,8 @@ 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)
+ raw.into()
}
}
diff --git a/rust/src/templatesimplifier.rs b/rust/src/template_simplifier.rs
index 5959167d..dd815f69 100644
--- a/rust/src/templatesimplifier.rs
+++ b/rust/src/template_simplifier.rs
@@ -12,7 +12,7 @@ pub fn simplify_str_to_str<S: BnStrCompatible>(input: S) -> BnString {
pub fn simplify_str_to_fqn<S: BnStrCompatible>(input: S, simplify: bool) -> QualifiedName {
let name = input.into_bytes_with_nul();
unsafe {
- QualifiedName(BNRustSimplifyStrToFQN(
+ QualifiedName::from_owned_raw(BNRustSimplifyStrToFQN(
name.as_ref().as_ptr() as *mut _,
simplify,
))
diff --git a/rust/src/type_archive.rs b/rust/src/type_archive.rs
new file mode 100644
index 00000000..f10cda50
--- /dev/null
+++ b/rust/src/type_archive.rs
@@ -0,0 +1,1231 @@
+use crate::progress::{NoProgressCallback, ProgressCallback};
+use binaryninjacore_sys::*;
+use std::ffi::{c_char, c_void, CStr};
+use std::fmt::{Debug, Display, Formatter};
+use std::hash::Hash;
+use std::path::{Path, PathBuf};
+use std::ptr::NonNull;
+
+use crate::data_buffer::DataBuffer;
+use crate::metadata::Metadata;
+use crate::platform::Platform;
+use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
+use crate::string::{raw_to_string, BnStrCompatible, BnString};
+use crate::type_container::TypeContainer;
+use crate::types::{QualifiedName, QualifiedNameAndType, QualifiedNameTypeAndId, Type};
+
+#[repr(transparent)]
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct TypeArchiveSnapshotId(pub String);
+
+impl TypeArchiveSnapshotId {
+ pub fn unset() -> Self {
+ Self("".to_string())
+ }
+}
+
+impl Display for TypeArchiveSnapshotId {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ f.write_fmt(format_args!("{}", self.0))
+ }
+}
+
+impl CoreArrayProvider for TypeArchiveSnapshotId {
+ type Raw = *mut c_char;
+ type Context = ();
+ type Wrapped<'a> = TypeArchiveSnapshotId;
+}
+
+unsafe impl CoreArrayProviderInner for TypeArchiveSnapshotId {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeStringList(raw, count)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ let str = CStr::from_ptr(*raw).to_str().unwrap().to_string();
+ TypeArchiveSnapshotId(str)
+ }
+}
+
+/// 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.
+pub struct TypeArchive {
+ pub(crate) handle: NonNull<BNTypeArchive>,
+}
+
+impl TypeArchive {
+ pub(crate) unsafe fn from_raw(handle: NonNull<BNTypeArchive>) -> Self {
+ Self { handle }
+ }
+
+ pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNTypeArchive>) -> Ref<Self> {
+ Ref::new(Self { handle })
+ }
+
+ /// Open the Type Archive at the given path, if it exists.
+ pub fn open(path: impl AsRef<Path>) -> Option<Ref<TypeArchive>> {
+ let raw_path = path.as_ref().into_bytes_with_nul();
+ let handle = unsafe { BNOpenTypeArchive(raw_path.as_ptr() as *const c_char) };
+ NonNull::new(handle).map(|handle| unsafe { TypeArchive::ref_from_raw(handle) })
+ }
+
+ /// Create a Type Archive at the given path, returning `None` if it could not be created.
+ ///
+ /// If the file has already been created and is not a valid type archive this will return `None`.
+ pub fn create(path: impl AsRef<Path>, platform: &Platform) -> Option<Ref<TypeArchive>> {
+ let raw_path = path.as_ref().into_bytes_with_nul();
+ let handle =
+ unsafe { BNCreateTypeArchive(raw_path.as_ptr() as *const c_char, platform.handle) };
+ NonNull::new(handle).map(|handle| unsafe { TypeArchive::ref_from_raw(handle) })
+ }
+
+ /// Create a Type Archive at the given path and id, returning None if it could not be created.
+ ///
+ /// If the file has already been created and is not a valid type archive this will return `None`.
+ pub fn create_with_id<I: BnStrCompatible>(
+ path: impl AsRef<Path>,
+ id: I,
+ platform: &Platform,
+ ) -> Option<Ref<TypeArchive>> {
+ let raw_path = path.as_ref().into_bytes_with_nul();
+ let id = id.into_bytes_with_nul();
+ let handle = unsafe {
+ BNCreateTypeArchiveWithId(
+ raw_path.as_ptr() as *const c_char,
+ platform.handle,
+ id.as_ref().as_ptr() as *const c_char,
+ )
+ };
+ NonNull::new(handle).map(|handle| unsafe { TypeArchive::ref_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<Ref<TypeArchive>> {
+ let id = id.into_bytes_with_nul();
+ let handle = unsafe { BNLookupTypeArchiveById(id.as_ref().as_ptr() as *const c_char) };
+ NonNull::new(handle).map(|handle| unsafe { TypeArchive::ref_from_raw(handle) })
+ }
+
+ /// Get the path to the Type Archive's file
+ pub fn path(&self) -> Option<PathBuf> {
+ let result = unsafe { BNGetTypeArchivePath(self.handle.as_ptr()) };
+ match result.is_null() {
+ false => {
+ let bn_res = unsafe { BnString::from_raw(result) };
+ Some(PathBuf::from(bn_res.to_string()))
+ }
+ true => None,
+ }
+ }
+
+ /// Get the guid for a Type Archive
+ pub fn id(&self) -> Option<BnString> {
+ let result = unsafe { BNGetTypeArchiveId(self.handle.as_ptr()) };
+ (!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.handle.as_ptr()) };
+ 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) -> TypeArchiveSnapshotId {
+ let result = unsafe { BNGetTypeArchiveCurrentSnapshotId(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ TypeArchiveSnapshotId(unsafe { BnString::from_raw(result) }.to_string())
+ }
+
+ /// Revert the type archive's current snapshot to the given snapshot
+ pub fn set_current_snapshot_id(&self, id: &TypeArchiveSnapshotId) {
+ unsafe {
+ BNSetTypeArchiveCurrentSnapshot(self.handle.as_ptr(), id.0.as_ptr() as *const c_char)
+ }
+ }
+
+ /// Get a list of every snapshot's id
+ pub fn all_snapshot_ids(&self) -> Array<TypeArchiveSnapshotId> {
+ let mut count = 0;
+ let result = unsafe { BNGetTypeArchiveAllSnapshotIds(self.handle.as_ptr(), &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: &TypeArchiveSnapshotId,
+ ) -> Option<Array<BnString>> {
+ let mut count = 0;
+ let result = unsafe {
+ BNGetTypeArchiveSnapshotParentIds(
+ self.handle.as_ptr(),
+ snapshot.0.as_ptr() as *const 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: &TypeArchiveSnapshotId,
+ ) -> Option<Array<BnString>> {
+ let mut count = 0;
+ let result = unsafe {
+ BNGetTypeArchiveSnapshotChildIds(
+ self.handle.as_ptr(),
+ snapshot.0.as_ptr() as *const c_char,
+ &mut count,
+ )
+ };
+ (!result.is_null()).then(|| unsafe { Array::new(result, count, ()) })
+ }
+
+ /// Add a named type 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.
+ ///
+ /// * `named_type` - Named type to add
+ pub fn add_type(&self, named_type: QualifiedNameAndType) -> bool {
+ self.add_types(vec![named_type])
+ }
+
+ /// 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.
+ ///
+ /// * `named_types` - Names and definitions of new types
+ pub fn add_types(&self, named_types: Vec<QualifiedNameAndType>) -> bool {
+ let new_types_raw: Vec<_> = named_types
+ .into_iter()
+ .map(QualifiedNameAndType::into_raw)
+ .collect();
+ let result = unsafe {
+ BNAddTypeArchiveTypes(
+ self.handle.as_ptr(),
+ new_types_raw.as_ptr(),
+ new_types_raw.len(),
+ )
+ };
+ for new_type in new_types_raw {
+ QualifiedNameAndType::free_raw(new_type);
+ }
+ result
+ }
+
+ /// Change the name of an existing type in the type archive. Returns false if failed.
+ ///
+ /// * `old_name` - Old type name in archive
+ /// * `new_name` - New type name
+ pub fn rename_type(&self, old_name: QualifiedName, new_name: QualifiedName) -> bool {
+ if let Some(id) = self.get_type_id(old_name) {
+ self.rename_type_by_id(id, new_name)
+ } else {
+ false
+ }
+ }
+
+ /// Change the name of an existing type in the type archive. Returns false if failed.
+ ///
+ /// * `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) -> bool {
+ let id = id.into_bytes_with_nul();
+ let raw_name = QualifiedName::into_raw(new_name);
+ let result = unsafe {
+ BNRenameTypeArchiveType(
+ self.handle.as_ptr(),
+ id.as_ref().as_ptr() as *const c_char,
+ &raw_name,
+ )
+ };
+ QualifiedName::free_raw(raw_name);
+ result
+ }
+
+ /// Delete an existing type in the type archive.
+ pub fn delete_type(&self, name: QualifiedName) -> bool {
+ if let Some(type_id) = self.get_type_id(name) {
+ self.delete_type_by_id(type_id)
+ } else {
+ false
+ }
+ }
+
+ /// Delete an existing type in the type archive.
+ pub fn delete_type_by_id<S: BnStrCompatible>(&self, id: S) -> bool {
+ let id = id.into_bytes_with_nul();
+ let result = unsafe {
+ BNDeleteTypeArchiveType(self.handle.as_ptr(), id.as_ref().as_ptr() as *const c_char)
+ };
+ result
+ }
+
+ /// Retrieve a stored type in the archive
+ ///
+ /// * `name` - Type name
+ pub fn get_type_by_name<S: BnStrCompatible>(&self, name: QualifiedName) -> Option<Ref<Type>> {
+ self.get_type_by_name_from_snapshot(name, &TypeArchiveSnapshotId::unset())
+ }
+
+ /// Retrieve a stored type in the archive
+ ///
+ /// * `name` - Type name
+ /// * `snapshot` - Snapshot id to search for types
+ pub fn get_type_by_name_from_snapshot(
+ &self,
+ name: QualifiedName,
+ snapshot: &TypeArchiveSnapshotId,
+ ) -> Option<Ref<Type>> {
+ let raw_name = QualifiedName::into_raw(name);
+ let result = unsafe {
+ BNGetTypeArchiveTypeByName(
+ self.handle.as_ptr(),
+ &raw_name,
+ snapshot.0.as_ptr() as *const c_char,
+ )
+ };
+ QualifiedName::free_raw(raw_name);
+ (!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) })
+ }
+
+ /// Retrieve a stored type in the archive by id
+ ///
+ /// * `id` - Type id
+ pub fn get_type_by_id<I: BnStrCompatible>(&self, id: I) -> Option<Ref<Type>> {
+ self.get_type_by_id_from_snapshot(id, &TypeArchiveSnapshotId::unset())
+ }
+
+ /// 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_from_snapshot<I: BnStrCompatible>(
+ &self,
+ id: I,
+ snapshot: &TypeArchiveSnapshotId,
+ ) -> Option<Ref<Type>> {
+ let id = id.into_bytes_with_nul();
+ let result = unsafe {
+ BNGetTypeArchiveTypeById(
+ self.handle.as_ptr(),
+ id.as_ref().as_ptr() as *const c_char,
+ snapshot.0.as_ptr() as *const c_char,
+ )
+ };
+ (!result.is_null()).then(|| unsafe { Type::ref_from_raw(result) })
+ }
+
+ /// Retrieve a type's name by its id
+ ///
+ /// * `id` - Type id
+ pub fn get_type_name_by_id<I: BnStrCompatible>(&self, id: I) -> QualifiedName {
+ self.get_type_name_by_id_from_snapshot(id, &TypeArchiveSnapshotId::unset())
+ }
+
+ /// 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_from_snapshot<I: BnStrCompatible>(
+ &self,
+ id: I,
+ snapshot: &TypeArchiveSnapshotId,
+ ) -> QualifiedName {
+ let id = id.into_bytes_with_nul();
+ let result = unsafe {
+ BNGetTypeArchiveTypeName(
+ self.handle.as_ptr(),
+ id.as_ref().as_ptr() as *const c_char,
+ snapshot.0.as_ptr() as *const c_char,
+ )
+ };
+ QualifiedName::from_owned_raw(result)
+ }
+
+ /// Retrieve a type's id by its name
+ ///
+ /// * `name` - Type name
+ pub fn get_type_id(&self, name: QualifiedName) -> Option<BnString> {
+ self.get_type_id_from_snapshot(name, &TypeArchiveSnapshotId::unset())
+ }
+
+ /// Retrieve a type's id by its name
+ ///
+ /// * `name` - Type name
+ /// * `snapshot` - Snapshot id to search for types
+ pub fn get_type_id_from_snapshot(
+ &self,
+ name: QualifiedName,
+ snapshot: &TypeArchiveSnapshotId,
+ ) -> Option<BnString> {
+ let raw_name = QualifiedName::into_raw(name);
+ let result = unsafe {
+ BNGetTypeArchiveTypeId(
+ self.handle.as_ptr(),
+ &raw_name,
+ snapshot.0.as_ptr() as *const c_char,
+ )
+ };
+ QualifiedName::free_raw(raw_name);
+ (!result.is_null()).then(|| unsafe { BnString::from_raw(result) })
+ }
+
+ /// Retrieve all stored types in the archive at a snapshot
+ pub fn get_types_and_ids(&self) -> Array<QualifiedNameTypeAndId> {
+ self.get_types_and_ids_from_snapshot(&TypeArchiveSnapshotId::unset())
+ }
+
+ /// Retrieve all stored types in the archive at a snapshot
+ ///
+ /// * `snapshot` - Snapshot id to search for types
+ pub fn get_types_and_ids_from_snapshot(
+ &self,
+ snapshot: &TypeArchiveSnapshotId,
+ ) -> Array<QualifiedNameTypeAndId> {
+ let mut count = 0;
+ let result = unsafe {
+ BNGetTypeArchiveTypes(
+ self.handle.as_ptr(),
+ snapshot.0.as_ptr() as *const 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
+ pub fn get_type_ids(&self) -> Array<BnString> {
+ self.get_type_ids_from_snapshot(&TypeArchiveSnapshotId::unset())
+ }
+
+ /// 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_from_snapshot(&self, snapshot: &TypeArchiveSnapshotId) -> Array<BnString> {
+ let mut count = 0;
+ let result = unsafe {
+ BNGetTypeArchiveTypeIds(
+ self.handle.as_ptr(),
+ snapshot.0.as_ptr() as *const 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
+ pub fn get_type_names(&self) -> Array<QualifiedName> {
+ self.get_type_names_from_snapshot(&TypeArchiveSnapshotId::unset())
+ }
+
+ /// 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_from_snapshot(
+ &self,
+ snapshot: &TypeArchiveSnapshotId,
+ ) -> Array<QualifiedName> {
+ let mut count = 0;
+ let result = unsafe {
+ BNGetTypeArchiveTypeNames(
+ self.handle.as_ptr(),
+ snapshot.0.as_ptr() as *const 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 the latest snapshot
+ pub fn get_type_names_and_ids(&self) -> (Array<QualifiedName>, Array<BnString>) {
+ self.get_type_names_and_ids_from_snapshot(&TypeArchiveSnapshotId::unset())
+ }
+
+ /// Get a list of all types' names and ids in the archive at a specific snapshot
+ ///
+ /// * `snapshot` - Snapshot id to search for types
+ pub fn get_type_names_and_ids_from_snapshot(
+ &self,
+ snapshot: &TypeArchiveSnapshotId,
+ ) -> (Array<QualifiedName>, Array<BnString>) {
+ let mut count = 0;
+ let mut names = std::ptr::null_mut();
+ let mut ids = std::ptr::null_mut();
+ let result = unsafe {
+ BNGetTypeArchiveTypeNamesAndIds(
+ self.handle.as_ptr(),
+ snapshot.0.as_ptr() as *const 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
+ pub fn get_outgoing_direct_references<I: BnStrCompatible>(&self, id: I) -> Array<BnString> {
+ self.get_outgoing_direct_references_from_snapshot(id, &TypeArchiveSnapshotId::unset())
+ }
+
+ /// 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_from_snapshot<I: BnStrCompatible>(
+ &self,
+ id: I,
+ snapshot: &TypeArchiveSnapshotId,
+ ) -> Array<BnString> {
+ let id = id.into_bytes_with_nul();
+ let mut count = 0;
+ let result = unsafe {
+ BNGetTypeArchiveOutgoingDirectTypeReferences(
+ self.handle.as_ptr(),
+ id.as_ref().as_ptr() as *const c_char,
+ snapshot.0.as_ptr() as *const 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
+ ///
+ /// * `id` - Source type id
+ pub fn get_outgoing_recursive_references<I: BnStrCompatible>(&self, id: I) -> Array<BnString> {
+ self.get_outgoing_recursive_references_from_snapshot(id, &TypeArchiveSnapshotId::unset())
+ }
+
+ /// Get all types a given type references, and any types that the referenced types reference
+ ///
+ /// * `id` - Source type id
+ /// * `snapshot` - Snapshot id to search for types
+ pub fn get_outgoing_recursive_references_from_snapshot<I: BnStrCompatible>(
+ &self,
+ id: I,
+ snapshot: &TypeArchiveSnapshotId,
+ ) -> Array<BnString> {
+ let id = id.into_bytes_with_nul();
+ let mut count = 0;
+ let result = unsafe {
+ BNGetTypeArchiveOutgoingRecursiveTypeReferences(
+ self.handle.as_ptr(),
+ id.as_ref().as_ptr() as *const c_char,
+ snapshot.0.as_ptr() as *const 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
+ pub fn get_incoming_direct_references<I: BnStrCompatible>(&self, id: I) -> Array<BnString> {
+ self.get_incoming_direct_references_with_snapshot(id, &TypeArchiveSnapshotId::unset())
+ }
+
+ /// 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_with_snapshot<I: BnStrCompatible>(
+ &self,
+ id: I,
+ snapshot: &TypeArchiveSnapshotId,
+ ) -> Array<BnString> {
+ let id = id.into_bytes_with_nul();
+ let mut count = 0;
+ let result = unsafe {
+ BNGetTypeArchiveIncomingDirectTypeReferences(
+ self.handle.as_ptr(),
+ id.as_ref().as_ptr() as *const c_char,
+ snapshot.0.as_ptr() as *const 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
+ pub fn get_incoming_recursive_references<I: BnStrCompatible>(&self, id: I) -> Array<BnString> {
+ self.get_incoming_recursive_references_with_snapshot(id, &TypeArchiveSnapshotId::unset())
+ }
+
+ /// 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_with_snapshot<I: BnStrCompatible>(
+ &self,
+ id: I,
+ snapshot: &TypeArchiveSnapshotId,
+ ) -> Array<BnString> {
+ let id = id.into_bytes_with_nul();
+ let mut count = 0;
+ let result = unsafe {
+ BNGetTypeArchiveIncomingRecursiveTypeReferences(
+ self.handle.as_ptr(),
+ id.as_ref().as_ptr() as *const c_char,
+ snapshot.0.as_ptr() as *const 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.handle.as_ptr(), key.as_ref().as_ptr() as *const 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.handle.as_ptr(),
+ key.as_ref().as_ptr() as *const 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.handle.as_ptr(),
+ key.as_ref().as_ptr() as *const c_char,
+ )
+ }
+ }
+
+ /// Turn a given `snapshot` id into a data stream
+ pub fn serialize_snapshot<S: BnStrCompatible>(
+ &self,
+ snapshot: &TypeArchiveSnapshotId,
+ ) -> DataBuffer {
+ let result = unsafe {
+ BNTypeArchiveSerializeSnapshot(
+ self.handle.as_ptr(),
+ snapshot.0.as_ptr() as *const 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) -> TypeArchiveSnapshotId {
+ let result =
+ unsafe { BNTypeArchiveDeserializeSnapshot(self.handle.as_ptr(), data.as_raw()) };
+ assert!(!result.is_null());
+ TypeArchiveSnapshotId(unsafe { BnString::from_raw(result) }.to_string())
+ }
+
+ /// 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 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.handle.as_ptr(), &mut notification) }
+ TypeArchiveCallbackHandle {
+ callback,
+ type_archive: self.to_owned(),
+ }
+ }
+
+ // 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.handle.as_ptr()) }
+ }
+
+ // TODO: Make this AsRef<Path>?
+ /// 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 c_char) }
+ }
+
+ ///// 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.handle.as_ptr()) };
+ unsafe { TypeContainer::from_raw(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: &[TypeArchiveSnapshotId],
+ ) -> TypeArchiveSnapshotId
+ where
+ P: BnStrCompatible,
+ F: FnMut(&TypeArchiveSnapshotId) -> bool,
+ {
+ unsafe extern "C" fn cb_callback<F: FnMut(&TypeArchiveSnapshotId) -> bool>(
+ ctxt: *mut c_void,
+ id: *const c_char,
+ ) -> bool {
+ let fun: &mut F = &mut *(ctxt as *mut F);
+ let id_str = raw_to_string(id).unwrap();
+ fun(&TypeArchiveSnapshotId(id_str))
+ }
+
+ // SAFETY TypeArchiveSnapshotId and `*const c_char` are transparent
+ let parents_raw = parents.as_ptr() as *const *const c_char;
+
+ let result = unsafe {
+ BNTypeArchiveNewSnapshotTransaction(
+ self.handle.as_ptr(),
+ Some(cb_callback::<F>),
+ &mut function as *mut F as *mut c_void,
+ parents_raw,
+ parents.len(),
+ )
+ };
+ assert!(!result.is_null());
+ let id_str = unsafe { BnString::from_raw(result) };
+ TypeArchiveSnapshotId(id_str.to_string())
+ }
+
+ /// 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, M, MI, MK>(
+ &self,
+ base_snapshot: B,
+ first_snapshot: F,
+ second_snapshot: S,
+ merge_conflicts: M,
+ ) -> Result<BnString, Array<BnString>>
+ where
+ B: BnStrCompatible,
+ F: BnStrCompatible,
+ S: BnStrCompatible,
+ M: IntoIterator<Item = (MI, MK)>,
+ MI: BnStrCompatible,
+ MK: BnStrCompatible,
+ {
+ self.merge_snapshots_with_progress(
+ base_snapshot,
+ first_snapshot,
+ second_snapshot,
+ merge_conflicts,
+ NoProgressCallback,
+ )
+ }
+
+ /// 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_with_progress<B, F, S, M, MI, MK, P>(
+ &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,
+ M: IntoIterator<Item = (MI, MK)>,
+ MI: BnStrCompatible,
+ MK: BnStrCompatible,
+ P: ProgressCallback,
+ {
+ 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 c_char` are transparent
+ let merge_keys_raw = merge_keys.as_ptr() as *const *const c_char;
+ let merge_values_raw = merge_values.as_ptr() as *const *const c_char;
+
+ let mut conflicts_errors = std::ptr::null_mut();
+ let mut conflicts_errors_count = 0;
+
+ let mut result = std::ptr::null_mut();
+
+ let success = unsafe {
+ BNTypeArchiveMergeSnapshots(
+ self.handle.as_ptr(),
+ base_snapshot.as_ref().as_ptr() as *const c_char,
+ first_snapshot.as_ref().as_ptr() as *const c_char,
+ second_snapshot.as_ref().as_ptr() as *const c_char,
+ merge_keys_raw,
+ merge_values_raw,
+ merge_keys.len(),
+ &mut conflicts_errors,
+ &mut conflicts_errors_count,
+ &mut result,
+ Some(P::cb_progress_callback),
+ &mut progress as *mut P as *mut 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 ToOwned for TypeArchive {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for TypeArchive {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: NonNull::new(BNNewTypeArchiveReference(handle.handle.as_ptr())).unwrap(),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeTypeArchiveReference(handle.handle.as_ptr());
+ }
+}
+
+impl PartialEq for TypeArchive {
+ fn eq(&self, other: &Self) -> bool {
+ self.id() == other.id()
+ }
+}
+impl Eq for TypeArchive {}
+
+impl Hash for TypeArchive {
+ fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
+ (self.handle.as_ptr() as usize).hash(state);
+ }
+}
+
+impl Debug for TypeArchive {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("TypeArchive")
+ .field("id", &self.id())
+ .field("path", &self.path())
+ .field("current_snapshot_id", &self.current_snapshot_id())
+ .field("platform", &self.platform())
+ .finish()
+ }
+}
+
+impl CoreArrayProvider for TypeArchive {
+ type Raw = *mut BNTypeArchive;
+ type Context = ();
+ type Wrapped<'a> = Guard<'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> {
+ let raw_ptr = NonNull::new(*raw).unwrap();
+ Guard::new(Self::from_raw(raw_ptr), context)
+ }
+}
+
+pub struct TypeArchiveCallbackHandle<T: TypeArchiveNotificationCallback> {
+ callback: *mut T,
+ type_archive: Ref<TypeArchive>,
+}
+
+impl<T: TypeArchiveNotificationCallback> Drop for TypeArchiveCallbackHandle<T> {
+ fn drop(&mut self) {
+ let mut notification = BNTypeArchiveNotification {
+ context: self.callback as *mut 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.handle.as_ptr(),
+ &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);
+ // `archive` is owned by the caller.
+ let archive = unsafe { TypeArchive::from_raw(NonNull::new(archive).unwrap()) };
+ ctxt.type_added(
+ &archive,
+ unsafe { 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);
+ // `archive` is owned by the caller.
+ let archive = unsafe { TypeArchive::from_raw(NonNull::new(archive).unwrap()) };
+ ctxt.type_updated(
+ &archive,
+ unsafe { 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);
+ // `old_name` is freed by the caller
+ let old_name = QualifiedName::from_raw(&*old_name);
+ // `new_name` is freed by the caller
+ let new_name = QualifiedName::from_raw(&*new_name);
+ // `archive` is owned by the caller.
+ let archive = unsafe { TypeArchive::from_raw(NonNull::new(archive).unwrap()) };
+ ctxt.type_renamed(
+ &archive,
+ unsafe { 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);
+ // `archive` is owned by the caller.
+ let archive = unsafe { TypeArchive::from_raw(NonNull::new(archive).unwrap()) };
+ ctxt.type_deleted(
+ &archive,
+ unsafe { CStr::from_ptr(id).to_string_lossy().as_ref() },
+ &Type { handle: definition },
+ )
+}
+
+#[repr(transparent)]
+pub struct TypeArchiveMergeConflict {
+ handle: NonNull<BNTypeArchiveMergeConflict>,
+}
+
+impl TypeArchiveMergeConflict {
+ pub(crate) unsafe fn from_raw(handle: NonNull<BNTypeArchiveMergeConflict>) -> Self {
+ Self { handle }
+ }
+
+ #[allow(unused)]
+ pub(crate) unsafe fn ref_from_raw(handle: NonNull<BNTypeArchiveMergeConflict>) -> Ref<Self> {
+ Ref::new(Self { handle })
+ }
+
+ pub fn get_type_archive(&self) -> Option<Ref<TypeArchive>> {
+ let value = unsafe { BNTypeArchiveMergeConflictGetTypeArchive(self.handle.as_ptr()) };
+ NonNull::new(value).map(|handle| unsafe { TypeArchive::ref_from_raw(handle) })
+ }
+
+ pub fn type_id(&self) -> BnString {
+ let value = unsafe { BNTypeArchiveMergeConflictGetTypeId(self.handle.as_ptr()) };
+ assert!(!value.is_null());
+ unsafe { BnString::from_raw(value) }
+ }
+
+ pub fn base_snapshot_id(&self) -> TypeArchiveSnapshotId {
+ let value = unsafe { BNTypeArchiveMergeConflictGetBaseSnapshotId(self.handle.as_ptr()) };
+ assert!(!value.is_null());
+ let id = unsafe { BnString::from_raw(value) }.to_string();
+ TypeArchiveSnapshotId(id)
+ }
+
+ pub fn first_snapshot_id(&self) -> TypeArchiveSnapshotId {
+ let value = unsafe { BNTypeArchiveMergeConflictGetFirstSnapshotId(self.handle.as_ptr()) };
+ assert!(!value.is_null());
+ let id = unsafe { BnString::from_raw(value) }.to_string();
+ TypeArchiveSnapshotId(id)
+ }
+
+ pub fn second_snapshot_id(&self) -> TypeArchiveSnapshotId {
+ let value = unsafe { BNTypeArchiveMergeConflictGetSecondSnapshotId(self.handle.as_ptr()) };
+ assert!(!value.is_null());
+ let id = unsafe { BnString::from_raw(value) }.to_string();
+ TypeArchiveSnapshotId(id)
+ }
+
+ // TODO: This needs documentation!
+ pub fn success<S: BnStrCompatible>(&self, value: S) -> bool {
+ let value = value.into_bytes_with_nul();
+ unsafe {
+ BNTypeArchiveMergeConflictSuccess(
+ self.handle.as_ptr(),
+ value.as_ref().as_ptr() as *const c_char,
+ )
+ }
+ }
+}
+
+impl Debug for TypeArchiveMergeConflict {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("TypeArchiveMergeConflict")
+ .field("type_id", &self.type_id())
+ .field("base_snapshot_id", &self.base_snapshot_id())
+ .field("first_snapshot_id", &self.first_snapshot_id())
+ .field("second_snapshot_id", &self.second_snapshot_id())
+ .finish()
+ }
+}
+
+impl ToOwned for TypeArchiveMergeConflict {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for TypeArchiveMergeConflict {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: NonNull::new(BNNewTypeArchiveMergeConflictReference(
+ handle.handle.as_ptr(),
+ ))
+ .unwrap(),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeTypeArchiveMergeConflict(handle.handle.as_ptr());
+ }
+}
+
+impl CoreArrayProvider for TypeArchiveMergeConflict {
+ type Raw = *mut BNTypeArchiveMergeConflict;
+ type Context = ();
+ type Wrapped<'a> = Guard<'a, Self>;
+}
+
+unsafe impl CoreArrayProviderInner for TypeArchiveMergeConflict {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeTypeArchiveMergeConflictList(raw, count)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
+ let raw_ptr = NonNull::new(*raw).unwrap();
+ Guard::new(Self::from_raw(raw_ptr), context)
+ }
+}
diff --git a/rust/src/type_container.rs b/rust/src/type_container.rs
new file mode 100644
index 00000000..f5689acf
--- /dev/null
+++ b/rust/src/type_container.rs
@@ -0,0 +1,439 @@
+// TODO: Add these!
+// The `TypeContainer` class should not generally be instantiated directly. Instances
+// can be retrieved from the following properties and methods in the API:
+// * [BinaryView::type_container]
+// * [BinaryView::auto_type_container]
+// * [BinaryView::user_type_container]
+// * [Platform::type_container]
+// * [TypeLibrary::type_container]
+// * [DebugInfo::get_type_container]
+
+use crate::platform::Platform;
+use crate::progress::{NoProgressCallback, ProgressCallback};
+use crate::rc::{Array, Ref};
+use crate::string::{raw_to_string, BnStrCompatible, BnString};
+use crate::type_parser::{TypeParserError, TypeParserResult};
+use crate::types::{QualifiedName, QualifiedNameAndType, Type};
+use binaryninjacore_sys::*;
+use std::collections::HashMap;
+use std::ffi::{c_char, c_void};
+use std::fmt::{Debug, Formatter};
+use std::ptr::NonNull;
+
+pub type TypeContainerType = BNTypeContainerType;
+
+/// A `TypeContainer` is a generic interface to access various Binary Ninja models
+/// that contain types. Types are stored with both a unique id and a unique name.
+#[repr(transparent)]
+pub struct TypeContainer {
+ pub handle: NonNull<BNTypeContainer>,
+}
+
+impl TypeContainer {
+ pub(crate) unsafe fn from_raw(handle: NonNull<BNTypeContainer>) -> Self {
+ // NOTE: There does not seem to be any shared ref counting for type containers, it seems if the
+ // NOTE: binary view is freed the type container will be freed and cause this to become invalid
+ // NOTE: but this is how the C++ and Python bindings operate so i guess its fine?
+ // TODO: I really dont get how some of the usage of the TypeContainer doesnt free the underlying container.
+ // TODO: So for now we always duplicate the type container
+ // let cloned_ptr = NonNull::new(BNDuplicateTypeContainer(handle.as_ptr()));
+ // Self {
+ // handle: cloned_ptr.unwrap(),
+ // }
+ Self { handle }
+ }
+
+ /// Get an id string for the Type Container. This will be unique within a given
+ /// analysis session, but may not be globally unique.
+ pub fn id(&self) -> BnString {
+ let result = unsafe { BNTypeContainerGetId(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ /// Get a user-friendly name for the Type Container.
+ pub fn name(&self) -> BnString {
+ let result = unsafe { BNTypeContainerGetName(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ /// Get the type of underlying model the Type Container is accessing.
+ pub fn container_type(&self) -> TypeContainerType {
+ unsafe { BNTypeContainerGetType(self.handle.as_ptr()) }
+ }
+
+ /// If the Type Container supports mutable operations (add, rename, delete)
+ pub fn is_mutable(&self) -> bool {
+ unsafe { BNTypeContainerIsMutable(self.handle.as_ptr()) }
+ }
+
+ /// Get the Platform object associated with this Type Container. All Type Containers
+ /// have exactly one associated Platform (as opposed to, e.g. Type Libraries).
+ pub fn platform(&self) -> Ref<Platform> {
+ let result = unsafe { BNTypeContainerGetPlatform(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { Platform::ref_from_raw(result) }
+ }
+
+ /// Add or update types to a Type Container. If the Type Container already contains
+ /// a type with the same name as a type being added, the existing type will be
+ /// replaced with the definition given to this function, and references will be
+ /// updated in the source model.
+ pub fn add_types<I, T>(&self, types: I) -> bool
+ where
+ I: IntoIterator<Item = T>,
+ T: Into<QualifiedNameAndType>,
+ {
+ self.add_types_with_progress(types, NoProgressCallback)
+ }
+
+ pub fn add_types_with_progress<I, T, P>(&self, types: I, mut progress: P) -> bool
+ where
+ I: IntoIterator<Item = T>,
+ T: Into<QualifiedNameAndType>,
+ P: ProgressCallback,
+ {
+ // TODO: I dislike how this iter unzip looks like... but its how to avoid allocating again...
+ let (raw_names, mut raw_types): (Vec<BNQualifiedName>, Vec<_>) = types
+ .into_iter()
+ .map(|t| {
+ let t = t.into();
+ // Leaked to be freed after the call to core.
+ (
+ QualifiedName::into_raw(t.name),
+ unsafe { Ref::into_raw(t.ty) }.handle,
+ )
+ })
+ .unzip();
+
+ let mut result_names = std::ptr::null_mut();
+ let mut result_ids = std::ptr::null_mut();
+ let mut result_count = 0;
+
+ let success = unsafe {
+ BNTypeContainerAddTypes(
+ self.handle.as_ptr(),
+ raw_names.as_ptr(),
+ raw_types.as_mut_ptr(),
+ raw_types.len(),
+ Some(P::cb_progress_callback),
+ &mut progress as *mut P as *mut c_void,
+ &mut result_names,
+ &mut result_ids,
+ &mut result_count,
+ )
+ };
+
+ for name in raw_names {
+ QualifiedName::free_raw(name);
+ }
+ for ty in raw_types {
+ let _ = unsafe { Type::ref_from_raw(ty) };
+ }
+ success
+ }
+
+ /// Rename a type in the Type Container. All references to this type will be updated
+ /// (by id) to use the new name.
+ ///
+ /// Returns true if the type was renamed.
+ pub fn rename_type<T: Into<QualifiedName>, S: BnStrCompatible>(
+ &self,
+ name: T,
+ type_id: S,
+ ) -> bool {
+ let type_id = type_id.into_bytes_with_nul();
+ let raw_name = QualifiedName::into_raw(name.into());
+ let success = unsafe {
+ BNTypeContainerRenameType(
+ self.handle.as_ptr(),
+ type_id.as_ref().as_ptr() as *const c_char,
+ &raw_name,
+ )
+ };
+ QualifiedName::free_raw(raw_name);
+ success
+ }
+
+ /// Delete a type in the Type Container. Behavior of references to this type is
+ /// not specified and you may end up with broken references if any still exist.
+ ///
+ /// Returns true if the type was deleted.
+ pub fn delete_type<S: BnStrCompatible>(&self, type_id: S) -> bool {
+ let type_id = type_id.into_bytes_with_nul();
+ unsafe {
+ BNTypeContainerDeleteType(
+ self.handle.as_ptr(),
+ type_id.as_ref().as_ptr() as *const c_char,
+ )
+ }
+ }
+
+ /// Get the unique id of the type in the Type Container with the given name.
+ ///
+ /// If no type with that name exists, returns None.
+ pub fn type_id<T: Into<QualifiedName>>(&self, name: T) -> Option<BnString> {
+ let mut result = std::ptr::null_mut();
+ let raw_name = QualifiedName::into_raw(name.into());
+ let success =
+ unsafe { BNTypeContainerGetTypeId(self.handle.as_ptr(), &raw_name, &mut result) };
+ QualifiedName::free_raw(raw_name);
+ success.then(|| unsafe { BnString::from_raw(result) })
+ }
+
+ /// Get the unique name of the type in the Type Container with the given id.
+ ///
+ /// If no type with that id exists, returns None.
+ pub fn type_name<S: BnStrCompatible>(&self, type_id: S) -> Option<QualifiedName> {
+ let type_id = type_id.into_bytes_with_nul();
+ let mut result = BNQualifiedName::default();
+ let success = unsafe {
+ BNTypeContainerGetTypeName(
+ self.handle.as_ptr(),
+ type_id.as_ref().as_ptr() as *const c_char,
+ &mut result,
+ )
+ };
+ success.then(|| QualifiedName::from_owned_raw(result))
+ }
+
+ /// Get the definition of the type in the Type Container with the given id.
+ ///
+ /// If no type with that id exists, returns None.
+ pub fn type_by_id<S: BnStrCompatible>(&self, type_id: S) -> Option<Ref<Type>> {
+ let type_id = type_id.into_bytes_with_nul();
+ let mut result = std::ptr::null_mut();
+ let success = unsafe {
+ BNTypeContainerGetTypeById(
+ self.handle.as_ptr(),
+ type_id.as_ref().as_ptr() as *const c_char,
+ &mut result,
+ )
+ };
+ success.then(|| unsafe { Type::ref_from_raw(result) })
+ }
+
+ /// Get the definition of the type in the Type Container with the given name.
+ ///
+ /// If no type with that name exists, returns None.
+ pub fn type_by_name<T: Into<QualifiedName>>(&self, name: T) -> Option<Ref<Type>> {
+ let mut result = std::ptr::null_mut();
+ let raw_name = QualifiedName::into_raw(name.into());
+ let success =
+ unsafe { BNTypeContainerGetTypeByName(self.handle.as_ptr(), &raw_name, &mut result) };
+ QualifiedName::free_raw(raw_name);
+ success.then(|| unsafe { Type::ref_from_raw(result) })
+ }
+
+ /// Get a mapping of all types in a Type Container.
+ pub fn types(&self) -> Option<HashMap<String, (QualifiedName, Ref<Type>)>> {
+ let mut type_ids = std::ptr::null_mut();
+ let mut type_names = std::ptr::null_mut();
+ let mut type_types = std::ptr::null_mut();
+ let mut type_count = 0;
+ let success = unsafe {
+ BNTypeContainerGetTypes(
+ self.handle.as_ptr(),
+ &mut type_ids,
+ &mut type_names,
+ &mut type_types,
+ &mut type_count,
+ )
+ };
+ success.then(|| unsafe {
+ let raw_ids = std::slice::from_raw_parts(type_ids, type_count);
+ let raw_names = std::slice::from_raw_parts(type_names, type_count);
+ let raw_types = std::slice::from_raw_parts(type_types, type_count);
+ let mut map = HashMap::new();
+ for (idx, raw_id) in raw_ids.iter().enumerate() {
+ let id = raw_to_string(*raw_id).expect("Valid string");
+ // Take the qualified name as a ref as the name should not be freed.
+ let name = QualifiedName::from_raw(&raw_names[idx]);
+ // Take the type as an owned ref, as the returned type was not already incremented.
+ let ty = Type::from_raw(raw_types[idx]).to_owned();
+ map.insert(id, (name, ty));
+ }
+ BNFreeStringList(type_ids, type_count);
+ BNFreeTypeNameList(type_names, type_count);
+ BNFreeTypeList(type_types, type_count);
+ map
+ })
+ }
+
+ /// Get all type ids in a Type Container.
+ pub fn type_ids(&self) -> Option<Array<BnString>> {
+ let mut type_ids = std::ptr::null_mut();
+ let mut type_count = 0;
+ let success = unsafe {
+ BNTypeContainerGetTypeIds(self.handle.as_ptr(), &mut type_ids, &mut type_count)
+ };
+ success.then(|| unsafe { Array::new(type_ids, type_count, ()) })
+ }
+
+ /// Get all type names in a Type Container.
+ pub fn type_names(&self) -> Option<Array<QualifiedName>> {
+ let mut type_ids = std::ptr::null_mut();
+ let mut type_count = 0;
+ let success = unsafe {
+ BNTypeContainerGetTypeNames(self.handle.as_ptr(), &mut type_ids, &mut type_count)
+ };
+ success.then(|| unsafe { Array::new(type_ids, type_count, ()) })
+ }
+
+ /// Get a mapping of all type ids and type names in a Type Container.
+ pub fn type_names_and_ids(&self) -> Option<(Array<BnString>, Array<QualifiedName>)> {
+ let mut type_ids = std::ptr::null_mut();
+ let mut type_names = std::ptr::null_mut();
+ let mut type_count = 0;
+ let success = unsafe {
+ BNTypeContainerGetTypeNamesAndIds(
+ self.handle.as_ptr(),
+ &mut type_ids,
+ &mut type_names,
+ &mut type_count,
+ )
+ };
+ success.then(|| unsafe {
+ let ids = Array::new(type_ids, type_count, ());
+ let names = Array::new(type_names, type_count, ());
+ (ids, names)
+ })
+ }
+
+ /// Parse a single type and name from a string containing their definition, with
+ /// knowledge of the types in the Type Container.
+ ///
+ /// * `source` - Source code to parse
+ /// * `import_dependencies` - If Type Library / Type Archive types should be imported during parsing
+ pub fn parse_type_string<S: BnStrCompatible>(
+ &self,
+ source: S,
+ import_dependencies: bool,
+ ) -> Result<QualifiedNameAndType, Array<TypeParserError>> {
+ let source = source.into_bytes_with_nul();
+ let mut result = BNQualifiedNameAndType::default();
+ let mut errors = std::ptr::null_mut();
+ let mut error_count = 0;
+ let success = unsafe {
+ BNTypeContainerParseTypeString(
+ self.handle.as_ptr(),
+ source.as_ref().as_ptr() as *const c_char,
+ import_dependencies,
+ &mut result,
+ &mut errors,
+ &mut error_count,
+ )
+ };
+ if success {
+ Ok(QualifiedNameAndType::from_owned_raw(result))
+ } else {
+ assert!(!errors.is_null());
+ Err(unsafe { Array::new(errors, error_count, ()) })
+ }
+ }
+
+ /// Parse an entire block of source into types, variables, and functions, with
+ /// knowledge of the types in the Type Container.
+ ///
+ /// * `source` - Source code to parse
+ /// * `file_name` - Name of the file containing the source (optional: exists on disk)
+ /// * `options` - String arguments to pass as options, e.g. command line arguments
+ /// * `include_dirs` - List of directories to include in the header search path
+ /// * `auto_type_source` - Source of types if used for automatically generated types
+ /// * `import_dependencies` - If Type Library / Type Archive types should be imported during parsing
+ pub fn parse_types_from_source<S, F, O, D, A>(
+ &self,
+ source: S,
+ filename: F,
+ options: O,
+ include_directories: D,
+ auto_type_source: A,
+ import_dependencies: bool,
+ ) -> Result<TypeParserResult, Array<TypeParserError>>
+ where
+ S: BnStrCompatible,
+ F: BnStrCompatible,
+ O: IntoIterator,
+ O::Item: BnStrCompatible,
+ D: IntoIterator,
+ D::Item: BnStrCompatible,
+ A: BnStrCompatible,
+ {
+ let source = source.into_bytes_with_nul();
+ let filename = filename.into_bytes_with_nul();
+ let options: Vec<_> = options
+ .into_iter()
+ .map(|o| o.into_bytes_with_nul())
+ .collect();
+ let options_raw: Vec<*const c_char> = options
+ .iter()
+ .map(|o| o.as_ref().as_ptr() as *const c_char)
+ .collect();
+ let include_directories: Vec<_> = include_directories
+ .into_iter()
+ .map(|d| d.into_bytes_with_nul())
+ .collect();
+ let include_directories_raw: Vec<*const c_char> = include_directories
+ .iter()
+ .map(|d| d.as_ref().as_ptr() as *const c_char)
+ .collect();
+ let auto_type_source = auto_type_source.into_bytes_with_nul();
+ let mut raw_result = BNTypeParserResult::default();
+ let mut errors = std::ptr::null_mut();
+ let mut error_count = 0;
+ let success = unsafe {
+ BNTypeContainerParseTypesFromSource(
+ self.handle.as_ptr(),
+ source.as_ref().as_ptr() as *const c_char,
+ filename.as_ref().as_ptr() as *const c_char,
+ options_raw.as_ptr(),
+ options_raw.len(),
+ include_directories_raw.as_ptr(),
+ include_directories_raw.len(),
+ auto_type_source.as_ref().as_ptr() as *const c_char,
+ import_dependencies,
+ &mut raw_result,
+ &mut errors,
+ &mut error_count,
+ )
+ };
+ if success {
+ let result = TypeParserResult::from_raw(&raw_result);
+ // NOTE: This is safe because the core allocated the TypeParserResult
+ TypeParserResult::free_raw(raw_result);
+ Ok(result)
+ } else {
+ assert!(!errors.is_null());
+ Err(unsafe { Array::new(errors, error_count, ()) })
+ }
+ }
+}
+
+impl Debug for TypeContainer {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("TypeContainer")
+ .field("id", &self.id())
+ .field("name", &self.name())
+ .field("container_type", &self.container_type())
+ .field("is_mutable", &self.is_mutable())
+ .finish()
+ }
+}
+
+impl Drop for TypeContainer {
+ fn drop(&mut self) {
+ unsafe { BNFreeTypeContainer(self.handle.as_ptr()) }
+ }
+}
+
+impl Clone for TypeContainer {
+ fn clone(&self) -> Self {
+ unsafe {
+ let cloned_ptr = NonNull::new(BNDuplicateTypeContainer(self.handle.as_ptr()));
+ Self {
+ handle: cloned_ptr.unwrap(),
+ }
+ }
+ }
+}
diff --git a/rust/src/typelibrary.rs b/rust/src/type_library.rs
index 22ac9f9e..ee978513 100644
--- a/rust/src/typelibrary.rs
+++ b/rust/src/type_library.rs
@@ -45,13 +45,13 @@ impl TypeLibrary {
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 { BNNewTypeLibrary(arch.handle, 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) };
+ let result = unsafe { BNGetArchitectureTypeLibraries(arch.handle, &mut count) };
assert!(!result.is_null());
unsafe { Array::new(result, count, ()) }
}
@@ -89,7 +89,7 @@ impl TypeLibrary {
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)
+ BNLookupTypeLibraryByName(arch.handle, name.as_ref().as_ptr() as *const ffi::c_char)
};
ptr::NonNull::new(handle).map(|h| unsafe { TypeLibrary::from_raw(h) })
}
@@ -98,7 +98,7 @@ impl TypeLibrary {
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)
+ BNLookupTypeLibraryByGuid(arch.handle, guid.as_ref().as_ptr() as *const ffi::c_char)
};
ptr::NonNull::new(handle).map(|h| unsafe { TypeLibrary::from_raw(h) })
}
@@ -107,7 +107,7 @@ impl TypeLibrary {
pub fn arch(&self) -> CoreArchitecture {
let arch = unsafe { BNGetTypeLibraryArchitecture(self.as_raw()) };
assert!(!arch.is_null());
- CoreArchitecture(arch)
+ unsafe { CoreArchitecture::from_raw(arch) }
}
/// The primary name associated with this type library
@@ -272,10 +272,10 @@ impl TypeLibrary {
/// 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)
- }
+ pub fn add_named_object(&self, name: QualifiedName, type_: &Type) {
+ let mut raw_name = QualifiedName::into_raw(name);
+ unsafe { BNAddTypeLibraryNamedObject(self.as_raw(), &mut raw_name, type_.handle) }
+ QualifiedName::free_raw(raw_name);
}
/// Directly inserts a named object into the type library's object store.
@@ -285,10 +285,10 @@ impl TypeLibrary {
/// 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)
- }
+ pub fn add_named_type(&self, name: QualifiedName, type_: &Type) {
+ let mut raw_name = QualifiedName::into_raw(name);
+ unsafe { BNAddTypeLibraryNamedType(self.as_raw(), &mut raw_name, type_.handle) }
+ QualifiedName::free_raw(raw_name);
}
/// Manually flag NamedTypeReferences to the given QualifiedName as originating from another source
@@ -299,31 +299,36 @@ impl TypeLibrary {
/// Use this api with extreme caution.
///
/// </div>
- pub fn add_type_source<S: BnStrCompatible>(&self, name: &QualifiedName, source: S) {
+ pub fn add_type_source<S: BnStrCompatible>(&self, name: QualifiedName, source: S) {
let source = source.into_bytes_with_nul();
+ let mut raw_name = QualifiedName::into_raw(name);
unsafe {
BNAddTypeLibraryNamedTypeSource(
self.as_raw(),
- &name.0 as *const _ as *mut _,
+ &mut raw_name,
source.as_ref().as_ptr() as *const ffi::c_char,
)
}
+ QualifiedName::free_raw(raw_name);
}
/// 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 _) };
+ pub fn get_named_object(&self, name: QualifiedName) -> Option<Ref<Type>> {
+ let mut raw_name = QualifiedName::into_raw(name);
+ let t = unsafe { BNGetTypeLibraryNamedObject(self.as_raw(), &mut raw_name) };
+ QualifiedName::free_raw(raw_name);
(!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 _) };
+ pub fn get_named_type(&self, name: QualifiedName) -> Option<Ref<Type>> {
+ let mut raw_name = QualifiedName::into_raw(name);
+ let t = unsafe { BNGetTypeLibraryNamedType(self.as_raw(), &mut raw_name) };
+ QualifiedName::free_raw(raw_name);
(!t.is_null()).then(|| unsafe { Type::ref_from_raw(t) })
}
diff --git a/rust/src/type_parser.rs b/rust/src/type_parser.rs
new file mode 100644
index 00000000..656f0c9d
--- /dev/null
+++ b/rust/src/type_parser.rs
@@ -0,0 +1,697 @@
+#![allow(unused)]
+use binaryninjacore_sys::*;
+use std::ffi::{c_char, c_void};
+use std::fmt::Debug;
+use std::ptr::NonNull;
+
+use crate::platform::Platform;
+use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref};
+use crate::string::{raw_to_string, BnStrCompatible, BnString};
+use crate::type_container::TypeContainer;
+use crate::types::{QualifiedName, QualifiedNameAndType, Type};
+
+pub type TypeParserErrorSeverity = BNTypeParserErrorSeverity;
+pub type TypeParserOption = BNTypeParserOption;
+
+/// Register a custom parser with the API
+pub fn register_type_parser<S: BnStrCompatible, T: TypeParser>(
+ name: S,
+ parser: T,
+) -> (&'static mut T, CoreTypeParser) {
+ let parser = Box::leak(Box::new(parser));
+ let mut callback = BNTypeParserCallbacks {
+ context: parser as *mut _ as *mut c_void,
+ getOptionText: Some(cb_get_option_text::<T>),
+ preprocessSource: Some(cb_preprocess_source::<T>),
+ parseTypesFromSource: Some(cb_parse_types_from_source::<T>),
+ parseTypeString: Some(cb_parse_type_string::<T>),
+ freeString: Some(cb_free_string),
+ freeResult: Some(cb_free_result),
+ freeErrorList: Some(cb_free_error_list),
+ };
+ let result = unsafe {
+ BNRegisterTypeParser(
+ name.into_bytes_with_nul().as_ref().as_ptr() as *const _,
+ &mut callback,
+ )
+ };
+ let core = unsafe { CoreTypeParser::from_raw(NonNull::new(result).unwrap()) };
+ (parser, core)
+}
+
+#[repr(transparent)]
+pub struct CoreTypeParser {
+ handle: NonNull<BNTypeParser>,
+}
+
+impl CoreTypeParser {
+ pub(crate) unsafe fn from_raw(handle: NonNull<BNTypeParser>) -> Self {
+ Self { handle }
+ }
+
+ pub fn parsers() -> Array<CoreTypeParser> {
+ let mut count = 0;
+ let result = unsafe { BNGetTypeParserList(&mut count) };
+ unsafe { Array::new(result, count, ()) }
+ }
+
+ pub fn parser_by_name<S: BnStrCompatible>(name: S) -> Option<CoreTypeParser> {
+ let name_raw = name.into_bytes_with_nul();
+ let result = unsafe { BNGetTypeParserByName(name_raw.as_ref().as_ptr() as *const c_char) };
+ NonNull::new(result).map(|x| unsafe { Self::from_raw(x) })
+ }
+
+ pub fn name(&self) -> BnString {
+ let result = unsafe { BNGetTypeParserName(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+}
+
+impl TypeParser for CoreTypeParser {
+ fn get_option_text(&self, option: TypeParserOption, value: &str) -> Option<String> {
+ let mut output = std::ptr::null_mut();
+ let value_cstr = BnString::new(value);
+ let result = unsafe {
+ BNGetTypeParserOptionText(
+ self.handle.as_ptr(),
+ option,
+ value_cstr.as_ptr(),
+ &mut output,
+ )
+ };
+ result.then(|| {
+ assert!(!output.is_null());
+ value_cstr.to_string()
+ })
+ }
+
+ fn preprocess_source(
+ &self,
+ source: &str,
+ file_name: &str,
+ platform: &Platform,
+ existing_types: &TypeContainer,
+ options: &[String],
+ include_dirs: &[String],
+ ) -> Result<String, Vec<TypeParserError>> {
+ let source_cstr = BnString::new(source);
+ let file_name_cstr = BnString::new(file_name);
+ let mut result = std::ptr::null_mut();
+ let mut errors = std::ptr::null_mut();
+ let mut error_count = 0;
+ let success = unsafe {
+ BNTypeParserPreprocessSource(
+ self.handle.as_ptr(),
+ source_cstr.as_ptr(),
+ file_name_cstr.as_ptr(),
+ platform.handle,
+ existing_types.handle.as_ptr(),
+ options.as_ptr() as *const *const c_char,
+ options.len(),
+ include_dirs.as_ptr() as *const *const c_char,
+ include_dirs.len(),
+ &mut result,
+ &mut errors,
+ &mut error_count,
+ )
+ };
+ if success {
+ assert!(!result.is_null());
+ let bn_result = unsafe { BnString::from_raw(result) };
+ Ok(bn_result.to_string())
+ } else {
+ let errors: Array<TypeParserError> = unsafe { Array::new(errors, error_count, ()) };
+ Err(errors.to_vec())
+ }
+ }
+
+ fn parse_types_from_source(
+ &self,
+ source: &str,
+ file_name: &str,
+ platform: &Platform,
+ existing_types: &TypeContainer,
+ options: &[String],
+ include_dirs: &[String],
+ auto_type_source: &str,
+ ) -> Result<TypeParserResult, Vec<TypeParserError>> {
+ let source_cstr = BnString::new(source);
+ let file_name_cstr = BnString::new(file_name);
+ let auto_type_source = BnString::new(auto_type_source);
+ let mut raw_result = BNTypeParserResult::default();
+ let mut errors = std::ptr::null_mut();
+ let mut error_count = 0;
+ let success = unsafe {
+ BNTypeParserParseTypesFromSource(
+ self.handle.as_ptr(),
+ source_cstr.as_ptr(),
+ file_name_cstr.as_ptr(),
+ platform.handle,
+ existing_types.handle.as_ptr(),
+ options.as_ptr() as *const *const c_char,
+ options.len(),
+ include_dirs.as_ptr() as *const *const c_char,
+ include_dirs.len(),
+ auto_type_source.as_ptr(),
+ &mut raw_result,
+ &mut errors,
+ &mut error_count,
+ )
+ };
+ if success {
+ let result = TypeParserResult::from_raw(&raw_result);
+ // NOTE: This is safe because the core allocated the TypeParserResult
+ TypeParserResult::free_raw(raw_result);
+ Ok(result)
+ } else {
+ let errors: Array<TypeParserError> = unsafe { Array::new(errors, error_count, ()) };
+ Err(errors.to_vec())
+ }
+ }
+
+ fn parse_type_string(
+ &self,
+ source: &str,
+ platform: &Platform,
+ existing_types: &TypeContainer,
+ ) -> Result<QualifiedNameAndType, Vec<TypeParserError>> {
+ let source_cstr = BnString::new(source);
+ let mut output = BNQualifiedNameAndType::default();
+ let mut errors = std::ptr::null_mut();
+ let mut error_count = 0;
+ let result = unsafe {
+ BNTypeParserParseTypeString(
+ self.handle.as_ptr(),
+ source_cstr.as_ptr(),
+ platform.handle,
+ existing_types.handle.as_ptr(),
+ &mut output,
+ &mut errors,
+ &mut error_count,
+ )
+ };
+ if result {
+ Ok(QualifiedNameAndType::from_owned_raw(output))
+ } else {
+ let errors: Array<TypeParserError> = unsafe { Array::new(errors, error_count, ()) };
+ Err(errors.to_vec())
+ }
+ }
+}
+
+impl Default for CoreTypeParser {
+ fn default() -> Self {
+ // TODO: This should return a ref
+ unsafe { Self::from_raw(NonNull::new(BNGetDefaultTypeParser()).unwrap()) }
+ }
+}
+
+// TODO: Impl this on platform.
+pub trait TypeParser {
+ /// Get the string representation of an option for passing to parse_type_*.
+ /// Returns a string representing the option if the parser supports it,
+ /// otherwise None
+ ///
+ /// * `option` - Option type
+ /// * `value` - Option value
+ fn get_option_text(&self, option: TypeParserOption, value: &str) -> Option<String>;
+
+ /// Preprocess a block of source, returning the source that would be parsed
+ ///
+ /// * `source` - Source code to process
+ /// * `file_name` - Name of the file containing the source (does not need to exist on disk)
+ /// * `platform` - Platform to assume the source is relevant to
+ /// * `existing_types` - Optional collection of all existing types to use for parsing context
+ /// * `options` - Optional string arguments to pass as options, e.g. command line arguments
+ /// * `include_dirs` - Optional list of directories to include in the header search path
+ fn preprocess_source(
+ &self,
+ source: &str,
+ file_name: &str,
+ platform: &Platform,
+ existing_types: &TypeContainer,
+ options: &[String],
+ include_dirs: &[String],
+ ) -> Result<String, Vec<TypeParserError>>;
+
+ /// Parse an entire block of source into types, variables, and functions
+ ///
+ /// * `source` - Source code to parse
+ /// * `file_name` - Name of the file containing the source (optional: exists on disk)
+ /// * `platform` - Platform to assume the types are relevant to
+ /// * `existing_types` - Optional container of all existing types to use for parsing context
+ /// * `options` - Optional string arguments to pass as options, e.g. command line arguments
+ /// * `include_dirs` - Optional list of directories to include in the header search path
+ /// * `auto_type_source` - Optional source of types if used for automatically generated types
+ fn parse_types_from_source(
+ &self,
+ source: &str,
+ file_name: &str,
+ platform: &Platform,
+ existing_types: &TypeContainer,
+ options: &[String],
+ include_dirs: &[String],
+ auto_type_source: &str,
+ ) -> Result<TypeParserResult, Vec<TypeParserError>>;
+
+ /// Parse a single type and name from a string containing their definition.
+ ///
+ /// * `source` - Source code to parse
+ /// * `platform` - Platform to assume the types are relevant to
+ /// * `existing_types` - Optional container of all existing types to use for parsing context
+ fn parse_type_string(
+ &self,
+ source: &str,
+ platform: &Platform,
+ existing_types: &TypeContainer,
+ ) -> Result<QualifiedNameAndType, Vec<TypeParserError>>;
+}
+
+impl CoreArrayProvider for CoreTypeParser {
+ type Raw = *mut BNTypeParser;
+ type Context = ();
+ type Wrapped<'a> = Self;
+}
+
+unsafe impl CoreArrayProviderInner for CoreTypeParser {
+ unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
+ BNFreeTypeParserList(raw)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ // TODO: Because handle is a NonNull we should prob make Self::Raw that as well...
+ let handle = NonNull::new(*raw).unwrap();
+ CoreTypeParser::from_raw(handle)
+ }
+}
+
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct TypeParserError {
+ pub severity: TypeParserErrorSeverity,
+ pub message: String,
+ pub file_name: String,
+ pub line: u64,
+ pub column: u64,
+}
+
+impl TypeParserError {
+ pub(crate) fn from_raw(value: &BNTypeParserError) -> Self {
+ Self {
+ severity: value.severity,
+ message: raw_to_string(value.message).unwrap(),
+ file_name: raw_to_string(value.fileName).unwrap(),
+ line: value.line,
+ column: value.column,
+ }
+ }
+
+ pub(crate) fn from_owned_raw(value: BNTypeParserError) -> Self {
+ let owned = Self::from_raw(&value);
+ Self::free_raw(value);
+ owned
+ }
+
+ pub(crate) fn into_raw(value: Self) -> BNTypeParserError {
+ BNTypeParserError {
+ severity: value.severity,
+ message: BnString::into_raw(BnString::new(value.message)),
+ fileName: BnString::into_raw(BnString::new(value.file_name)),
+ line: value.line,
+ column: value.column,
+ }
+ }
+
+ pub(crate) fn free_raw(value: BNTypeParserError) {
+ let _ = unsafe { BnString::from_raw(value.message) };
+ let _ = unsafe { BnString::from_raw(value.fileName) };
+ }
+
+ pub fn new(
+ severity: TypeParserErrorSeverity,
+ message: String,
+ file_name: String,
+ line: u64,
+ column: u64,
+ ) -> Self {
+ Self {
+ severity,
+ message,
+ file_name,
+ line,
+ column,
+ }
+ }
+}
+
+impl CoreArrayProvider for TypeParserError {
+ type Raw = BNTypeParserError;
+ type Context = ();
+ type Wrapped<'a> = Self;
+}
+
+unsafe impl CoreArrayProviderInner for TypeParserError {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ unsafe { BNFreeTypeParserErrors(raw, count) }
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ Self::from_raw(raw)
+ }
+}
+
+#[derive(Debug, Eq, PartialEq, Default)]
+pub struct TypeParserResult {
+ pub types: Vec<ParsedType>,
+ pub variables: Vec<ParsedType>,
+ pub functions: Vec<ParsedType>,
+}
+
+impl TypeParserResult {
+ pub(crate) fn from_raw(value: &BNTypeParserResult) -> Self {
+ let raw_types = unsafe { std::slice::from_raw_parts(value.types, value.typeCount) };
+ let types = raw_types.iter().map(ParsedType::from_raw).collect();
+ let raw_variables =
+ unsafe { std::slice::from_raw_parts(value.variables, value.variableCount) };
+ let variables = raw_variables.iter().map(ParsedType::from_raw).collect();
+ let raw_functions =
+ unsafe { std::slice::from_raw_parts(value.functions, value.functionCount) };
+ let functions = raw_functions.iter().map(ParsedType::from_raw).collect();
+ TypeParserResult {
+ types,
+ variables,
+ functions,
+ }
+ }
+
+ /// Return a rust allocated type parser result, free using [`Self::free_owned_raw`].
+ ///
+ /// Under no circumstance should you call [`Self::free_raw`] on the returned result.
+ pub(crate) fn into_raw(value: Self) -> BNTypeParserResult {
+ let boxed_raw_types: Box<[BNParsedType]> = value
+ .types
+ .into_iter()
+ // NOTE: Freed with [`Self::free_owned_raw`].
+ .map(ParsedType::into_raw)
+ .collect();
+ let boxed_raw_variables: Box<[BNParsedType]> = value
+ .variables
+ .into_iter()
+ // NOTE: Freed with [`Self::free_owned_raw`].
+ .map(ParsedType::into_raw)
+ .collect();
+ let boxed_raw_functions: Box<[BNParsedType]> = value
+ .functions
+ .into_iter()
+ // NOTE: Freed with [`Self::free_owned_raw`].
+ .map(ParsedType::into_raw)
+ .collect();
+ BNTypeParserResult {
+ typeCount: boxed_raw_types.len(),
+ // NOTE: Freed with [`Self::free_owned_raw`].
+ types: Box::leak(boxed_raw_types).as_mut_ptr(),
+ variableCount: boxed_raw_variables.len(),
+ // NOTE: Freed with [`Self::free_owned_raw`].
+ variables: Box::leak(boxed_raw_variables).as_mut_ptr(),
+ functionCount: boxed_raw_functions.len(),
+ // NOTE: Freed with [`Self::free_owned_raw`].
+ functions: Box::leak(boxed_raw_functions).as_mut_ptr(),
+ }
+ }
+
+ pub(crate) fn free_raw(mut value: BNTypeParserResult) {
+ // SAFETY: `value` must be a properly initialized BNTypeParserResult.
+ // SAFETY: `value` must be core allocated.
+ unsafe { BNFreeTypeParserResult(&mut value) };
+ }
+
+ pub(crate) fn free_owned_raw(value: BNTypeParserResult) {
+ let raw_types = std::ptr::slice_from_raw_parts_mut(value.types, value.typeCount);
+ // Free the rust allocated types list
+ let boxed_types = unsafe { Box::from_raw(raw_types) };
+ for parsed_type in boxed_types {
+ ParsedType::free_raw(parsed_type);
+ }
+ let raw_variables =
+ std::ptr::slice_from_raw_parts_mut(value.variables, value.variableCount);
+ // Free the rust allocated variables list
+ let boxed_variables = unsafe { Box::from_raw(raw_variables) };
+ for parsed_type in boxed_variables {
+ ParsedType::free_raw(parsed_type);
+ }
+ let raw_functions =
+ std::ptr::slice_from_raw_parts_mut(value.functions, value.functionCount);
+ // Free the rust allocated functions list
+ let boxed_functions = unsafe { Box::from_raw(raw_functions) };
+ for parsed_type in boxed_functions {
+ ParsedType::free_raw(parsed_type);
+ }
+ }
+}
+
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct ParsedType {
+ name: QualifiedName,
+ ty: Ref<Type>,
+ user: bool,
+}
+
+impl ParsedType {
+ pub(crate) fn from_raw(value: &BNParsedType) -> Self {
+ Self {
+ name: QualifiedName::from_raw(&value.name),
+ ty: unsafe { Type::from_raw(value.type_).to_owned() },
+ user: value.isUser,
+ }
+ }
+
+ pub(crate) fn from_owned_raw(value: BNParsedType) -> Self {
+ let owned = Self::from_raw(&value);
+ Self::free_raw(value);
+ owned
+ }
+
+ pub(crate) fn into_raw(value: Self) -> BNParsedType {
+ BNParsedType {
+ name: QualifiedName::into_raw(value.name),
+ type_: unsafe { Ref::into_raw(value.ty) }.handle,
+ isUser: value.user,
+ }
+ }
+
+ pub(crate) fn free_raw(value: BNParsedType) {
+ QualifiedName::free_raw(value.name);
+ let _ = unsafe { Type::ref_from_raw(value.type_) };
+ }
+
+ pub fn new(name: QualifiedName, ty: Ref<Type>, user: bool) -> Self {
+ Self { name, ty, user }
+ }
+}
+
+impl CoreArrayProvider for ParsedType {
+ type Raw = BNParsedType;
+ type Context = ();
+ type Wrapped<'b> = Self;
+}
+
+unsafe impl CoreArrayProviderInner for ParsedType {
+ unsafe fn free(_raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
+ // Expected to be freed with BNFreeTypeParserResult
+ // TODO ^ because of the above, we should not provide an array provider for this
+ }
+
+ unsafe fn wrap_raw<'b>(raw: &'b Self::Raw, _context: &'b Self::Context) -> Self::Wrapped<'b> {
+ ParsedType::from_raw(raw)
+ }
+}
+
+unsafe extern "C" fn cb_get_option_text<T: TypeParser>(
+ ctxt: *mut ::std::os::raw::c_void,
+ option: BNTypeParserOption,
+ value: *const c_char,
+ result: *mut *mut c_char,
+) -> bool {
+ let ctxt: &mut T = &mut *(ctxt as *mut T);
+ if let Some(inner_result) = ctxt.get_option_text(option, &raw_to_string(value).unwrap()) {
+ let bn_inner_result = BnString::new(inner_result);
+ // NOTE: Dropped by `cb_free_string`
+ *result = BnString::into_raw(bn_inner_result);
+ true
+ } else {
+ *result = std::ptr::null_mut();
+ false
+ }
+}
+
+unsafe extern "C" fn cb_preprocess_source<T: TypeParser>(
+ ctxt: *mut c_void,
+ source: *const c_char,
+ file_name: *const c_char,
+ platform: *mut BNPlatform,
+ existing_types: *mut BNTypeContainer,
+ options: *const *const c_char,
+ option_count: usize,
+ include_dirs: *const *const c_char,
+ include_dir_count: usize,
+ result: *mut *mut c_char,
+ errors: *mut *mut BNTypeParserError,
+ error_count: *mut usize,
+) -> bool {
+ let ctxt: &mut T = &mut *(ctxt as *mut T);
+ let platform = Platform { handle: platform };
+ let existing_types_ptr = NonNull::new(existing_types).unwrap();
+ let existing_types = TypeContainer::from_raw(existing_types_ptr);
+ let options_raw = unsafe { std::slice::from_raw_parts(options, option_count) };
+ let options: Vec<_> = options_raw
+ .iter()
+ .filter_map(|&r| raw_to_string(r))
+ .collect();
+ let includes_raw = unsafe { std::slice::from_raw_parts(include_dirs, include_dir_count) };
+ let includes: Vec<_> = includes_raw
+ .iter()
+ .filter_map(|&r| raw_to_string(r))
+ .collect();
+ match ctxt.preprocess_source(
+ &raw_to_string(source).unwrap(),
+ &raw_to_string(file_name).unwrap(),
+ &platform,
+ &existing_types,
+ &options,
+ &includes,
+ ) {
+ Ok(inner_result) => {
+ let bn_inner_result = BnString::new(inner_result);
+ // NOTE: Dropped by `cb_free_string`
+ *result = BnString::into_raw(bn_inner_result);
+ *errors = std::ptr::null_mut();
+ *error_count = 0;
+ true
+ }
+ Err(inner_errors) => {
+ *result = std::ptr::null_mut();
+ *error_count = inner_errors.len();
+ // NOTE: Leaking errors here, dropped by `cb_free_error_list`.
+ let inner_errors: Box<[_]> = inner_errors
+ .into_iter()
+ .map(TypeParserError::into_raw)
+ .collect();
+ // NOTE: Dropped by `cb_free_error_list`
+ *errors = Box::leak(inner_errors).as_mut_ptr();
+ false
+ }
+ }
+}
+
+unsafe extern "C" fn cb_parse_types_from_source<T: TypeParser>(
+ ctxt: *mut c_void,
+ source: *const c_char,
+ file_name: *const c_char,
+ platform: *mut BNPlatform,
+ existing_types: *mut BNTypeContainer,
+ options: *const *const c_char,
+ option_count: usize,
+ include_dirs: *const *const c_char,
+ include_dir_count: usize,
+ auto_type_source: *const c_char,
+ result: *mut BNTypeParserResult,
+ errors: *mut *mut BNTypeParserError,
+ error_count: *mut usize,
+) -> bool {
+ let ctxt: &mut T = &mut *(ctxt as *mut T);
+ let platform = Platform { handle: platform };
+ let existing_types_ptr = NonNull::new(existing_types).unwrap();
+ let existing_types = TypeContainer::from_raw(existing_types_ptr);
+ let options_raw = unsafe { std::slice::from_raw_parts(options, option_count) };
+ let options: Vec<_> = options_raw
+ .iter()
+ .filter_map(|&r| raw_to_string(r))
+ .collect();
+ let includes_raw = unsafe { std::slice::from_raw_parts(include_dirs, include_dir_count) };
+ let includes: Vec<_> = includes_raw
+ .iter()
+ .filter_map(|&r| raw_to_string(r))
+ .collect();
+ match ctxt.parse_types_from_source(
+ &raw_to_string(source).unwrap(),
+ &raw_to_string(file_name).unwrap(),
+ &platform,
+ &existing_types,
+ &options,
+ &includes,
+ &raw_to_string(auto_type_source).unwrap(),
+ ) {
+ Ok(type_parser_result) => {
+ *result = TypeParserResult::into_raw(type_parser_result);
+ *errors = std::ptr::null_mut();
+ *error_count = 0;
+ true
+ }
+ Err(inner_errors) => {
+ *error_count = inner_errors.len();
+ let inner_errors: Box<[_]> = inner_errors
+ .into_iter()
+ .map(TypeParserError::into_raw)
+ .collect();
+ *result = Default::default();
+ // NOTE: Dropped by cb_free_error_list
+ *errors = Box::leak(inner_errors).as_mut_ptr();
+ false
+ }
+ }
+}
+
+unsafe extern "C" fn cb_parse_type_string<T: TypeParser>(
+ ctxt: *mut c_void,
+ source: *const c_char,
+ platform: *mut BNPlatform,
+ existing_types: *mut BNTypeContainer,
+ result: *mut BNQualifiedNameAndType,
+ errors: *mut *mut BNTypeParserError,
+ error_count: *mut usize,
+) -> bool {
+ let ctxt: &mut T = &mut *(ctxt as *mut T);
+ let platform = Platform { handle: platform };
+ let existing_types_ptr = NonNull::new(existing_types).unwrap();
+ let existing_types = TypeContainer::from_raw(existing_types_ptr);
+ match ctxt.parse_type_string(&raw_to_string(source).unwrap(), &platform, &existing_types) {
+ Ok(inner_result) => {
+ *result = QualifiedNameAndType::into_raw(inner_result);
+ *errors = std::ptr::null_mut();
+ *error_count = 0;
+ true
+ }
+ Err(inner_errors) => {
+ *error_count = inner_errors.len();
+ let inner_errors: Box<[_]> = inner_errors
+ .into_iter()
+ .map(TypeParserError::into_raw)
+ .collect();
+ *result = Default::default();
+ // NOTE: Dropped by cb_free_error_list
+ *errors = Box::leak(inner_errors).as_mut_ptr();
+ false
+ }
+ }
+}
+
+unsafe extern "C" fn cb_free_string(_ctxt: *mut c_void, string: *mut c_char) {
+ // SAFETY: The returned string is just BnString
+ let _ = BnString::from_raw(string);
+}
+
+unsafe extern "C" fn cb_free_result(_ctxt: *mut c_void, result: *mut BNTypeParserResult) {
+ TypeParserResult::free_owned_raw(*result);
+}
+
+unsafe extern "C" fn cb_free_error_list(
+ _ctxt: *mut c_void,
+ errors: *mut BNTypeParserError,
+ error_count: usize,
+) {
+ let errors = std::ptr::slice_from_raw_parts_mut(errors, error_count);
+ let boxed_errors = Box::from_raw(errors);
+ for error in boxed_errors {
+ TypeParserError::free_raw(error);
+ }
+}
diff --git a/rust/src/type_printer.rs b/rust/src/type_printer.rs
new file mode 100644
index 00000000..12c71645
--- /dev/null
+++ b/rust/src/type_printer.rs
@@ -0,0 +1,981 @@
+#![allow(unused)]
+
+use crate::binary_view::BinaryView;
+use crate::disassembly::InstructionTextToken;
+use crate::platform::Platform;
+use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Ref};
+use crate::string::{raw_to_string, BnStrCompatible, BnString};
+use crate::type_container::TypeContainer;
+use crate::types::{NamedTypeReference, QualifiedName, QualifiedNameAndType, Type};
+use binaryninjacore_sys::*;
+use std::ffi::{c_char, c_int, c_void};
+use std::ptr::NonNull;
+
+pub type TokenEscapingType = BNTokenEscapingType;
+pub type TypeDefinitionLineType = BNTypeDefinitionLineType;
+
+/// Register a custom parser with the API
+pub fn register_type_printer<S: BnStrCompatible, T: TypePrinter>(
+ name: S,
+ parser: T,
+) -> (&'static mut T, CoreTypePrinter) {
+ let parser = Box::leak(Box::new(parser));
+ let mut callback = BNTypePrinterCallbacks {
+ context: parser as *mut _ as *mut c_void,
+ getTypeTokens: Some(cb_get_type_tokens::<T>),
+ getTypeTokensBeforeName: Some(cb_get_type_tokens_before_name::<T>),
+ getTypeTokensAfterName: Some(cb_get_type_tokens_after_name::<T>),
+ getTypeString: Some(cb_get_type_string::<T>),
+ getTypeStringBeforeName: Some(cb_get_type_string_before_name::<T>),
+ getTypeStringAfterName: Some(cb_get_type_string_after_name::<T>),
+ getTypeLines: Some(cb_get_type_lines::<T>),
+ printAllTypes: Some(cb_print_all_types::<T>),
+ freeTokens: Some(cb_free_tokens),
+ freeString: Some(cb_free_string),
+ freeLines: Some(cb_free_lines),
+ };
+ let result = unsafe {
+ BNRegisterTypePrinter(
+ name.into_bytes_with_nul().as_ref().as_ptr() as *const c_char,
+ &mut callback,
+ )
+ };
+ let core = unsafe { CoreTypePrinter::from_raw(NonNull::new(result).unwrap()) };
+ (parser, core)
+}
+
+#[repr(transparent)]
+pub struct CoreTypePrinter {
+ handle: NonNull<BNTypePrinter>,
+}
+
+impl CoreTypePrinter {
+ pub(crate) unsafe fn from_raw(handle: NonNull<BNTypePrinter>) -> CoreTypePrinter {
+ Self { handle }
+ }
+
+ pub fn printers() -> Array<CoreTypePrinter> {
+ let mut count = 0;
+ let result = unsafe { BNGetTypePrinterList(&mut count) };
+ assert!(!result.is_null());
+ unsafe { Array::new(result, count, ()) }
+ }
+
+ pub fn printer_by_name<S: BnStrCompatible>(name: S) -> Option<CoreTypePrinter> {
+ let name_raw = name.into_bytes_with_nul();
+ let result = unsafe { BNGetTypePrinterByName(name_raw.as_ref().as_ptr() as *const c_char) };
+ NonNull::new(result).map(|x| unsafe { Self::from_raw(x) })
+ }
+
+ pub fn name(&self) -> BnString {
+ let result = unsafe { BNGetTypePrinterName(self.handle.as_ptr()) };
+ assert!(!result.is_null());
+ unsafe { BnString::from_raw(result) }
+ }
+
+ pub fn get_type_tokens<T: Into<QualifiedName>>(
+ &self,
+ type_: &Type,
+ platform: &Platform,
+ name: T,
+ base_confidence: u8,
+ escaping: TokenEscapingType,
+ ) -> Option<Array<InstructionTextToken>> {
+ let mut result_count = 0;
+ let mut result = std::ptr::null_mut();
+ let mut raw_name = QualifiedName::into_raw(name.into());
+ let success = unsafe {
+ BNGetTypePrinterTypeTokens(
+ self.handle.as_ptr(),
+ type_.handle,
+ platform.handle,
+ &mut raw_name,
+ base_confidence,
+ escaping,
+ &mut result,
+ &mut result_count,
+ )
+ };
+ QualifiedName::free_raw(raw_name);
+ success.then(|| {
+ assert!(!result.is_null());
+ unsafe { Array::new(result, result_count, ()) }
+ })
+ }
+
+ pub fn get_type_tokens_before_name(
+ &self,
+ type_: &Type,
+ platform: &Platform,
+ base_confidence: u8,
+ parent_type: &Type,
+ escaping: TokenEscapingType,
+ ) -> Option<Array<InstructionTextToken>> {
+ let mut result_count = 0;
+ let mut result = std::ptr::null_mut();
+ let success = unsafe {
+ BNGetTypePrinterTypeTokensBeforeName(
+ self.handle.as_ptr(),
+ type_.handle,
+ platform.handle,
+ base_confidence,
+ parent_type.handle,
+ escaping,
+ &mut result,
+ &mut result_count,
+ )
+ };
+ success.then(|| {
+ assert!(!result.is_null());
+ unsafe { Array::new(result, result_count, ()) }
+ })
+ }
+
+ pub fn get_type_tokens_after_name(
+ &self,
+ type_: &Type,
+ platform: &Platform,
+ base_confidence: u8,
+ parent_type: &Type,
+ escaping: TokenEscapingType,
+ ) -> Option<Array<InstructionTextToken>> {
+ let mut result_count = 0;
+ let mut result = std::ptr::null_mut();
+ let success = unsafe {
+ BNGetTypePrinterTypeTokensAfterName(
+ self.handle.as_ptr(),
+ type_.handle,
+ platform.handle,
+ base_confidence,
+ parent_type.handle,
+ escaping,
+ &mut result,
+ &mut result_count,
+ )
+ };
+ success.then(|| {
+ assert!(!result.is_null());
+ unsafe { Array::new(result, result_count, ()) }
+ })
+ }
+
+ pub fn get_type_string<T: Into<QualifiedName>>(
+ &self,
+ type_: &Type,
+ platform: &Platform,
+ name: T,
+ escaping: TokenEscapingType,
+ ) -> Option<BnString> {
+ let mut result = std::ptr::null_mut();
+ let mut raw_name = QualifiedName::into_raw(name.into());
+ let success = unsafe {
+ BNGetTypePrinterTypeString(
+ self.handle.as_ptr(),
+ type_.handle,
+ platform.handle,
+ &mut raw_name,
+ escaping,
+ &mut result,
+ )
+ };
+ QualifiedName::free_raw(raw_name);
+ success.then(|| unsafe {
+ assert!(!result.is_null());
+ BnString::from_raw(result)
+ })
+ }
+
+ pub fn get_type_string_before_name(
+ &self,
+ type_: &Type,
+ platform: &Platform,
+ escaping: BNTokenEscapingType,
+ ) -> Option<BnString> {
+ let mut result = std::ptr::null_mut();
+ let success = unsafe {
+ BNGetTypePrinterTypeStringAfterName(
+ self.handle.as_ptr(),
+ type_.handle,
+ platform.handle,
+ escaping,
+ &mut result,
+ )
+ };
+ success.then(|| unsafe {
+ assert!(!result.is_null());
+ BnString::from_raw(result)
+ })
+ }
+
+ pub fn get_type_string_after_name(
+ &self,
+ type_: &Type,
+ platform: &Platform,
+ escaping: TokenEscapingType,
+ ) -> Option<BnString> {
+ let mut result = std::ptr::null_mut();
+ let success = unsafe {
+ BNGetTypePrinterTypeStringBeforeName(
+ self.handle.as_ptr(),
+ type_.handle,
+ platform.handle,
+ escaping,
+ &mut result,
+ )
+ };
+ success.then(|| unsafe {
+ assert!(!result.is_null());
+ BnString::from_raw(result)
+ })
+ }
+
+ pub fn get_type_lines<T: Into<QualifiedName>>(
+ &self,
+ type_: &Type,
+ types: &TypeContainer,
+ name: T,
+ padding_cols: isize,
+ collapsed: bool,
+ escaping: TokenEscapingType,
+ ) -> Option<Array<TypeDefinitionLine>> {
+ let mut result_count = 0;
+ let mut result = std::ptr::null_mut();
+ let mut raw_name = QualifiedName::into_raw(name.into());
+ let success = unsafe {
+ BNGetTypePrinterTypeLines(
+ self.handle.as_ptr(),
+ type_.handle,
+ types.handle.as_ptr(),
+ &mut raw_name,
+ padding_cols as c_int,
+ collapsed,
+ escaping,
+ &mut result,
+ &mut result_count,
+ )
+ };
+ QualifiedName::free_raw(raw_name);
+ success.then(|| {
+ assert!(!result.is_null());
+ unsafe { Array::<TypeDefinitionLine>::new(result, result_count, ()) }
+ })
+ }
+
+ /// Print all types to a single big string, including headers, sections, etc
+ ///
+ /// * `types` - All types to print
+ /// * `data` - Binary View in which all the types are defined
+ /// * `padding_cols` - Maximum number of bytes represented by each padding line
+ /// * `escaping` - Style of escaping literals which may not be parsable
+ pub fn default_print_all_types<T, I>(
+ &self,
+ types: T,
+ data: &BinaryView,
+ padding_cols: isize,
+ escaping: TokenEscapingType,
+ ) -> Option<BnString>
+ where
+ T: Iterator<Item = I>,
+ I: Into<QualifiedNameAndType>,
+ {
+ let mut result = std::ptr::null_mut();
+ let (mut raw_names, mut raw_types): (Vec<BNQualifiedName>, Vec<_>) = types
+ .map(|t| {
+ let t = t.into();
+ // Leak both to the core and then free afterwards.
+ (
+ QualifiedName::into_raw(t.name),
+ unsafe { Ref::into_raw(t.ty) }.handle,
+ )
+ })
+ .unzip();
+ let success = unsafe {
+ BNTypePrinterDefaultPrintAllTypes(
+ self.handle.as_ptr(),
+ raw_names.as_mut_ptr(),
+ raw_types.as_mut_ptr(),
+ raw_types.len(),
+ data.handle,
+ padding_cols as c_int,
+ escaping,
+ &mut result,
+ )
+ };
+ for raw_name in raw_names {
+ QualifiedName::free_raw(raw_name);
+ }
+ for raw_type in raw_types {
+ let _ = unsafe { Type::ref_from_raw(raw_type) };
+ }
+ success.then(|| unsafe {
+ assert!(!result.is_null());
+ BnString::from_raw(result)
+ })
+ }
+
+ pub fn print_all_types<T, I>(
+ &self,
+ types: T,
+ data: &BinaryView,
+ padding_cols: isize,
+ escaping: TokenEscapingType,
+ ) -> Option<BnString>
+ where
+ T: IntoIterator<Item = I>,
+ I: Into<QualifiedNameAndType>,
+ {
+ let mut result = std::ptr::null_mut();
+ // TODO: I dislike how this iter unzip looks like... but its how to avoid allocating again...
+ let (mut raw_names, mut raw_types): (Vec<BNQualifiedName>, Vec<_>) = types
+ .into_iter()
+ .map(|t| {
+ let t = t.into();
+ // Leak both to the core and then free afterwards.
+ (
+ QualifiedName::into_raw(t.name),
+ unsafe { Ref::into_raw(t.ty) }.handle,
+ )
+ })
+ .unzip();
+ let success = unsafe {
+ BNTypePrinterPrintAllTypes(
+ self.handle.as_ptr(),
+ raw_names.as_mut_ptr(),
+ raw_types.as_mut_ptr(),
+ raw_types.len(),
+ data.handle,
+ padding_cols as c_int,
+ escaping,
+ &mut result,
+ )
+ };
+ for raw_name in raw_names {
+ QualifiedName::free_raw(raw_name);
+ }
+ for raw_type in raw_types {
+ let _ = unsafe { Type::ref_from_raw(raw_type) };
+ }
+ success.then(|| unsafe {
+ assert!(!result.is_null());
+ BnString::from_raw(result)
+ })
+ }
+}
+
+impl Default for CoreTypePrinter {
+ fn default() -> Self {
+ // TODO: Remove this entirely, there is no "default", its view specific lets not make this some defined behavior.
+ let default_settings = crate::settings::Settings::new();
+ let name = default_settings.get_string("analysis.types.printerName");
+ Self::printer_by_name(name).unwrap()
+ }
+}
+
+impl CoreArrayProvider for CoreTypePrinter {
+ type Raw = *mut BNTypePrinter;
+ type Context = ();
+ type Wrapped<'a> = Self;
+}
+
+unsafe impl CoreArrayProviderInner for CoreTypePrinter {
+ unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
+ BNFreeTypePrinterList(raw)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ // TODO: Because handle is a NonNull we should prob make Self::Raw that as well...
+ let handle = NonNull::new(*raw).unwrap();
+ CoreTypePrinter::from_raw(handle)
+ }
+}
+
+pub trait TypePrinter {
+ /// Generate a single-line text representation of a type, Returns a List
+ /// of text tokens representing the type.
+ ///
+ /// * `type_` - Type to print
+ /// * `platform` - Platform responsible for this type
+ /// * `name` - Name of the type
+ /// * `base_confidence` - Confidence to use for tokens created for this type
+ /// * `escaping` - Style of escaping literals which may not be parsable
+ fn get_type_tokens<T: Into<QualifiedName>>(
+ &self,
+ type_: Ref<Type>,
+ platform: Option<Ref<Platform>>,
+ name: T,
+ base_confidence: u8,
+ escaping: TokenEscapingType,
+ ) -> Option<Vec<InstructionTextToken>>;
+
+ /// In a single-line text representation of a type, generate the tokens that
+ /// should be printed before the type's name. Returns a list of text tokens
+ /// representing the type
+ ///
+ /// * `type_` - Type to print
+ /// * `platform` - Platform responsible for this type
+ /// * `base_confidence` - Confidence to use for tokens created for this type
+ /// * `parent_type` - Type of the parent of this type, or None
+ /// * `escaping` - Style of escaping literals which may not be parsable
+ fn get_type_tokens_before_name(
+ &self,
+ type_: Ref<Type>,
+ platform: Option<Ref<Platform>>,
+ base_confidence: u8,
+ parent_type: Option<Ref<Type>>,
+ escaping: TokenEscapingType,
+ ) -> Option<Vec<InstructionTextToken>>;
+
+ /// In a single-line text representation of a type, generate the tokens
+ /// that should be printed after the type's name. Returns a list of text
+ /// tokens representing the type
+ ///
+ /// * `type_` - Type to print
+ /// * `platform` - Platform responsible for this type
+ /// * `base_confidence` - Confidence to use for tokens created for this type
+ /// * `parent_type` - Type of the parent of this type, or None
+ /// * `escaping` - Style of escaping literals which may not be parsable
+ fn get_type_tokens_after_name(
+ &self,
+ type_: Ref<Type>,
+ platform: Option<Ref<Platform>>,
+ base_confidence: u8,
+ parent_type: Option<Ref<Type>>,
+ escaping: TokenEscapingType,
+ ) -> Option<Vec<InstructionTextToken>>;
+
+ /// Generate a single-line text representation of a type. Returns a string
+ /// representing the type
+ ///
+ /// * `type_` - Type to print
+ /// * `platform` - Platform responsible for this type
+ /// * `name` - Name of the type
+ /// * `escaping` - Style of escaping literals which may not be parsable
+ fn get_type_string<T: Into<QualifiedName>>(
+ &self,
+ type_: Ref<Type>,
+ platform: Option<Ref<Platform>>,
+ name: T,
+ escaping: TokenEscapingType,
+ ) -> Option<String>;
+
+ /// In a single-line text representation of a type, generate the string that
+ /// should be printed before the type's name. Returns a string representing
+ /// the type
+ ///
+ /// * `type_` - Type to print
+ /// * `platform` - Platform responsible for this type
+ /// * `escaping` - Style of escaping literals which may not be parsable
+ fn get_type_string_before_name(
+ &self,
+ type_: Ref<Type>,
+ platform: Option<Ref<Platform>>,
+ escaping: TokenEscapingType,
+ ) -> Option<String>;
+
+ /// In a single-line text representation of a type, generate the string that
+ /// should be printed after the type's name. Returns a string representing
+ /// the type
+ ///
+ /// * `type_` - Type to print
+ /// * `platform` - Platform responsible for this type
+ /// * `escaping` - Style of escaping literals which may not be parsable
+ fn get_type_string_after_name(
+ &self,
+ type_: Ref<Type>,
+ platform: Option<Ref<Platform>>,
+ escaping: TokenEscapingType,
+ ) -> Option<String>;
+
+ /// Generate a multi-line representation of a type. Returns a list of type
+ /// definition lines
+ ///
+ /// * `type_` - Type to print
+ /// * `types` - Type Container containing the type and dependencies
+ /// * `name` - Name of the type
+ /// * `padding_cols` - Maximum number of bytes represented by each padding line
+ /// * `collapsed` - Whether to collapse structure/enum blocks
+ /// * `escaping` - Style of escaping literals which may not be parsable
+ fn get_type_lines<T: Into<QualifiedName>>(
+ &self,
+ type_: Ref<Type>,
+ types: &TypeContainer,
+ name: T,
+ padding_cols: isize,
+ collapsed: bool,
+ escaping: TokenEscapingType,
+ ) -> Option<Vec<TypeDefinitionLine>>;
+
+ /// Print all types to a single big string, including headers, sections,
+ /// etc.
+ ///
+ /// * `types` - All types to print
+ /// * `data` - Binary View in which all the types are defined
+ /// * `padding_cols` - Maximum number of bytes represented by each padding line
+ /// * `escaping` - Style of escaping literals which may not be parsable
+ fn print_all_types(
+ &self,
+ names: Vec<QualifiedName>,
+ types: Vec<Ref<Type>>,
+ data: Ref<BinaryView>,
+ padding_cols: isize,
+ escaping: TokenEscapingType,
+ ) -> Option<String>;
+}
+
+// TODO: This needs an extreme amount of documentation...
+#[derive(Clone)]
+pub struct TypeDefinitionLine {
+ pub line_type: TypeDefinitionLineType,
+ pub tokens: Vec<InstructionTextToken>,
+ pub ty: Ref<Type>,
+ pub parent_type: Option<Ref<Type>>,
+ // TODO: Document what the root type is.
+ pub root_type: Option<Ref<Type>>,
+ pub root_type_name: Option<String>,
+ // TODO: Document the base type, and why its a ntr instead of type + name like root type
+ pub base_type: Option<Ref<NamedTypeReference>>,
+ // TODO: These can also be optional?
+ pub base_offset: u64,
+ pub offset: u64,
+ pub field_index: usize,
+}
+
+impl TypeDefinitionLine {
+ pub(crate) fn from_raw(value: &BNTypeDefinitionLine) -> Self {
+ Self {
+ line_type: value.lineType,
+ tokens: {
+ let raw_tokens = unsafe { std::slice::from_raw_parts(value.tokens, value.count) };
+ raw_tokens
+ .iter()
+ .map(InstructionTextToken::from_raw)
+ .collect()
+ },
+ ty: unsafe { Type::from_raw(value.type_).to_owned() },
+ parent_type: match value.parentType.is_null() {
+ false => Some(unsafe { Type::from_raw(value.parentType).to_owned() }),
+ true => None,
+ },
+ root_type: match value.rootType.is_null() {
+ false => Some(unsafe { Type::from_raw(value.rootType).to_owned() }),
+ true => None,
+ },
+ root_type_name: match value.rootTypeName.is_null() {
+ false => Some(raw_to_string(value.rootTypeName).unwrap()),
+ true => None,
+ },
+ base_type: match value.baseType.is_null() {
+ false => Some(unsafe { NamedTypeReference::from_raw(value.baseType).to_owned() }),
+ true => None,
+ },
+ base_offset: value.baseOffset,
+ offset: value.offset,
+ field_index: value.fieldIndex,
+ }
+ }
+
+ /// The raw value must have been allocated by rust. See [`Self::free_owned_raw`] for details.
+ pub(crate) fn from_owned_raw(value: BNTypeDefinitionLine) -> Self {
+ let owned = Self::from_raw(&value);
+ Self::free_owned_raw(value);
+ owned
+ }
+
+ pub(crate) fn into_raw(value: Self) -> BNTypeDefinitionLine {
+ // NOTE: This is leaking [BNInstructionTextToken::text], [BNInstructionTextToken::typeNames].
+ let tokens: Box<[BNInstructionTextToken]> = value
+ .tokens
+ .into_iter()
+ .map(InstructionTextToken::into_raw)
+ .collect();
+ BNTypeDefinitionLine {
+ lineType: value.line_type,
+ count: tokens.len(),
+ // NOTE: This is leaking tokens. Must free with `cb_free_lines`.
+ tokens: Box::leak(tokens).as_mut_ptr(),
+ // NOTE: This is leaking a ref to ty. Must free with `cb_free_lines`.
+ type_: unsafe { Ref::into_raw(value.ty) }.handle,
+ // NOTE: This is leaking a ref to parent_type. Must free with `cb_free_lines`.
+ parentType: value
+ .parent_type
+ .map(|t| unsafe { Ref::into_raw(t) }.handle)
+ .unwrap_or(std::ptr::null_mut()),
+ // NOTE: This is leaking a ref to root_type. Must free with `cb_free_lines`.
+ rootType: value
+ .root_type
+ .map(|t| unsafe { Ref::into_raw(t) }.handle)
+ .unwrap_or(std::ptr::null_mut()),
+ // NOTE: This is leaking root_type_name. Must free with `cb_free_lines`.
+ rootTypeName: value
+ .root_type_name
+ .map(|s| BnString::into_raw(BnString::new(s)))
+ .unwrap_or(std::ptr::null_mut()),
+ // NOTE: This is leaking a ref to base_type. Must free with `cb_free_lines`.
+ baseType: value
+ .base_type
+ .map(|t| unsafe { Ref::into_raw(t) }.handle)
+ .unwrap_or(std::ptr::null_mut()),
+ baseOffset: value.base_offset,
+ offset: value.offset,
+ fieldIndex: value.field_index,
+ }
+ }
+
+ /// This is unique from the typical `from_raw` as the allocation of InstructionTextToken requires it be from rust, hence the "owned" free.
+ pub(crate) fn free_owned_raw(raw: BNTypeDefinitionLine) {
+ if !raw.tokens.is_null() {
+ let tokens = std::ptr::slice_from_raw_parts_mut(raw.tokens, raw.count);
+ // SAFETY: raw.tokens must have been allocated by rust.
+ let boxed_tokens = unsafe { Box::from_raw(tokens) };
+ for token in boxed_tokens {
+ InstructionTextToken::free_raw(token);
+ }
+ }
+ if !raw.type_.is_null() {
+ // SAFETY: raw.type_ must have been ref incremented in conjunction with this free
+ let _ = unsafe { Type::ref_from_raw(raw.type_) };
+ }
+ if !raw.parentType.is_null() {
+ // SAFETY: raw.parentType must have been ref incremented in conjunction with this free
+ let _ = unsafe { Type::ref_from_raw(raw.parentType) };
+ }
+ if !raw.rootType.is_null() {
+ // SAFETY: raw.rootType must have been ref incremented in conjunction with this free
+ let _ = unsafe { Type::ref_from_raw(raw.rootType) };
+ }
+ if !raw.rootTypeName.is_null() {
+ // SAFETY: raw.rootTypeName must have been ref incremented in conjunction with this free
+ let _ = unsafe { BnString::from_raw(raw.rootTypeName) };
+ }
+ if !raw.baseType.is_null() {
+ // SAFETY: raw.baseType must have been ref incremented in conjunction with this free
+ let _ = unsafe { NamedTypeReference::ref_from_raw(raw.baseType) };
+ }
+ }
+}
+
+impl CoreArrayProvider for TypeDefinitionLine {
+ type Raw = BNTypeDefinitionLine;
+ type Context = ();
+ type Wrapped<'a> = Self;
+}
+
+unsafe impl CoreArrayProviderInner for TypeDefinitionLine {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ unsafe { BNFreeTypeDefinitionLineList(raw, count) };
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ Self::from_raw(raw)
+ }
+}
+
+unsafe extern "C" fn cb_get_type_tokens<T: TypePrinter>(
+ ctxt: *mut ::std::os::raw::c_void,
+ type_: *mut BNType,
+ platform: *mut BNPlatform,
+ name: *mut BNQualifiedName,
+ base_confidence: u8,
+ escaping: BNTokenEscapingType,
+ result: *mut *mut BNInstructionTextToken,
+ result_count: *mut usize,
+) -> bool {
+ let ctxt: &mut T = &mut *(ctxt as *mut T);
+ // NOTE: The caller is responsible for freeing name.
+ let qualified_name = QualifiedName::from_raw(&*name);
+ let inner_result = ctxt.get_type_tokens(
+ unsafe { Type::ref_from_raw(type_) },
+ match platform.is_null() {
+ false => Some(Platform::ref_from_raw(platform)),
+ true => None,
+ },
+ qualified_name,
+ base_confidence,
+ escaping,
+ );
+ if let Some(inner_result) = inner_result {
+ let raw_text_tokens: Box<[BNInstructionTextToken]> = inner_result
+ .into_iter()
+ .map(InstructionTextToken::into_raw)
+ .collect();
+ *result_count = raw_text_tokens.len();
+ // NOTE: Dropped by the cb_free_tokens
+ *result = Box::leak(raw_text_tokens).as_mut_ptr();
+ true
+ } else {
+ *result = std::ptr::null_mut();
+ *result_count = 0;
+ false
+ }
+}
+
+unsafe extern "C" fn cb_get_type_tokens_before_name<T: TypePrinter>(
+ ctxt: *mut ::std::os::raw::c_void,
+ type_: *mut BNType,
+ platform: *mut BNPlatform,
+ base_confidence: u8,
+ parent_type: *mut BNType,
+ escaping: BNTokenEscapingType,
+ result: *mut *mut BNInstructionTextToken,
+ result_count: *mut usize,
+) -> bool {
+ let ctxt: &mut T = &mut *(ctxt as *mut T);
+ let inner_result = ctxt.get_type_tokens_before_name(
+ Type::ref_from_raw(type_),
+ match platform.is_null() {
+ false => Some(Platform::ref_from_raw(platform)),
+ true => None,
+ },
+ base_confidence,
+ match parent_type.is_null() {
+ false => Some(Type::ref_from_raw(parent_type)),
+ true => None,
+ },
+ escaping,
+ );
+ if let Some(inner_result) = inner_result {
+ let raw_text_tokens: Box<[BNInstructionTextToken]> = inner_result
+ .into_iter()
+ .map(InstructionTextToken::into_raw)
+ .collect();
+ *result_count = raw_text_tokens.len();
+ // NOTE: Dropped by the cb_free_tokens
+ *result = Box::leak(raw_text_tokens).as_mut_ptr();
+ true
+ } else {
+ *result = std::ptr::null_mut();
+ *result_count = 0;
+ false
+ }
+}
+
+unsafe extern "C" fn cb_get_type_tokens_after_name<T: TypePrinter>(
+ ctxt: *mut ::std::os::raw::c_void,
+ type_: *mut BNType,
+ platform: *mut BNPlatform,
+ base_confidence: u8,
+ parent_type: *mut BNType,
+ escaping: BNTokenEscapingType,
+ result: *mut *mut BNInstructionTextToken,
+ result_count: *mut usize,
+) -> bool {
+ let ctxt: &mut T = &mut *(ctxt as *mut T);
+ let inner_result = ctxt.get_type_tokens_after_name(
+ Type::ref_from_raw(type_),
+ match platform.is_null() {
+ false => Some(Platform::ref_from_raw(platform)),
+ true => None,
+ },
+ base_confidence,
+ match parent_type.is_null() {
+ false => Some(Type::ref_from_raw(parent_type)),
+ true => None,
+ },
+ escaping,
+ );
+ if let Some(inner_result) = inner_result {
+ let raw_text_tokens: Box<[BNInstructionTextToken]> = inner_result
+ .into_iter()
+ .map(InstructionTextToken::into_raw)
+ .collect();
+ *result_count = raw_text_tokens.len();
+ // NOTE: Dropped by the cb_free_tokens
+ *result = Box::leak(raw_text_tokens).as_mut_ptr();
+ true
+ } else {
+ *result = std::ptr::null_mut();
+ *result_count = 0;
+ false
+ }
+}
+
+unsafe extern "C" fn cb_get_type_string<T: TypePrinter>(
+ ctxt: *mut ::std::os::raw::c_void,
+ type_: *mut BNType,
+ platform: *mut BNPlatform,
+ name: *mut BNQualifiedName,
+ escaping: BNTokenEscapingType,
+ result: *mut *mut ::std::os::raw::c_char,
+) -> bool {
+ let ctxt: &mut T = &mut *(ctxt as *mut T);
+ // NOTE: The caller is responsible for freeing name.
+ let qualified_name = QualifiedName::from_raw(&*name);
+ let inner_result = ctxt.get_type_string(
+ Type::ref_from_raw(type_),
+ match platform.is_null() {
+ false => Some(Platform::ref_from_raw(platform)),
+ true => None,
+ },
+ qualified_name,
+ escaping,
+ );
+ if let Some(inner_result) = inner_result {
+ let raw_string = BnString::new(inner_result);
+ // NOTE: Dropped by `cb_free_string`
+ *result = BnString::into_raw(raw_string);
+ true
+ } else {
+ *result = std::ptr::null_mut();
+ false
+ }
+}
+
+unsafe extern "C" fn cb_get_type_string_before_name<T: TypePrinter>(
+ ctxt: *mut ::std::os::raw::c_void,
+ type_: *mut BNType,
+ platform: *mut BNPlatform,
+ escaping: BNTokenEscapingType,
+ result: *mut *mut ::std::os::raw::c_char,
+) -> bool {
+ let ctxt: &mut T = &mut *(ctxt as *mut T);
+ let inner_result = ctxt.get_type_string_before_name(
+ Type::ref_from_raw(type_),
+ match platform.is_null() {
+ false => Some(Platform::ref_from_raw(platform)),
+ true => None,
+ },
+ escaping,
+ );
+ if let Some(inner_result) = inner_result {
+ // NOTE: Dropped by `cb_free_string`
+ let raw_string = BnString::new(inner_result);
+ *result = BnString::into_raw(raw_string);
+ true
+ } else {
+ *result = std::ptr::null_mut();
+ false
+ }
+}
+
+unsafe extern "C" fn cb_get_type_string_after_name<T: TypePrinter>(
+ ctxt: *mut ::std::os::raw::c_void,
+ type_: *mut BNType,
+ platform: *mut BNPlatform,
+ escaping: BNTokenEscapingType,
+ result: *mut *mut ::std::os::raw::c_char,
+) -> bool {
+ let ctxt: &mut T = &mut *(ctxt as *mut T);
+ let inner_result = ctxt.get_type_string_after_name(
+ Type::ref_from_raw(type_),
+ match platform.is_null() {
+ false => Some(Platform::ref_from_raw(platform)),
+ true => None,
+ },
+ escaping,
+ );
+ if let Some(inner_result) = inner_result {
+ let raw_string = BnString::new(inner_result);
+ // NOTE: Dropped by `cb_free_string`
+ *result = BnString::into_raw(raw_string);
+ true
+ } else {
+ *result = std::ptr::null_mut();
+ false
+ }
+}
+
+unsafe extern "C" fn cb_get_type_lines<T: TypePrinter>(
+ ctxt: *mut ::std::os::raw::c_void,
+ type_: *mut BNType,
+ types: *mut BNTypeContainer,
+ name: *mut BNQualifiedName,
+ padding_cols: ::std::os::raw::c_int,
+ collapsed: bool,
+ escaping: BNTokenEscapingType,
+ result: *mut *mut BNTypeDefinitionLine,
+ result_count: *mut usize,
+) -> bool {
+ let ctxt: &mut T = &mut *(ctxt as *mut T);
+ // NOTE: The caller is responsible for freeing name.
+ let qualified_name = QualifiedName::from_raw(&*name);
+ let types_ptr = NonNull::new(types).unwrap();
+ let types = TypeContainer::from_raw(types_ptr);
+ let inner_result = ctxt.get_type_lines(
+ Type::ref_from_raw(type_),
+ &types,
+ qualified_name,
+ padding_cols as isize,
+ collapsed,
+ escaping,
+ );
+ if let Some(inner_result) = inner_result {
+ let boxed_raw_lines: Box<[_]> = inner_result
+ .into_iter()
+ .map(TypeDefinitionLine::into_raw)
+ .collect();
+ *result_count = boxed_raw_lines.len();
+ // NOTE: Dropped by `cb_free_lines`
+ *result = Box::leak(boxed_raw_lines).as_mut_ptr();
+ true
+ } else {
+ *result = std::ptr::null_mut();
+ *result_count = 0;
+ false
+ }
+}
+
+unsafe extern "C" fn cb_print_all_types<T: TypePrinter>(
+ ctxt: *mut ::std::os::raw::c_void,
+ names: *mut BNQualifiedName,
+ types: *mut *mut BNType,
+ type_count: usize,
+ data: *mut BNBinaryView,
+ padding_cols: ::std::os::raw::c_int,
+ escaping: BNTokenEscapingType,
+ result: *mut *mut ::std::os::raw::c_char,
+) -> bool {
+ let ctxt: &mut T = &mut *(ctxt as *mut T);
+ let raw_names = std::slice::from_raw_parts(names, type_count);
+ // NOTE: The caller is responsible for freeing raw_names.
+ let names: Vec<_> = raw_names.iter().map(QualifiedName::from_raw).collect();
+ let raw_types = std::slice::from_raw_parts(types, type_count);
+ // NOTE: The caller is responsible for freeing raw_types.
+ let types: Vec<_> = raw_types.iter().map(|&t| Type::ref_from_raw(t)).collect();
+ let inner_result = ctxt.print_all_types(
+ names,
+ types,
+ BinaryView::ref_from_raw(data),
+ padding_cols as isize,
+ escaping,
+ );
+ if let Some(inner_result) = inner_result {
+ let raw_string = BnString::new(inner_result);
+ // NOTE: Dropped by `cb_free_string`
+ *result = BnString::into_raw(raw_string);
+ true
+ } else {
+ *result = std::ptr::null_mut();
+ false
+ }
+}
+
+unsafe extern "C" fn cb_free_string(_ctxt: *mut c_void, string: *mut c_char) {
+ // SAFETY: The returned string is just BnString
+ let _ = BnString::from_raw(string);
+}
+
+unsafe extern "C" fn cb_free_tokens(
+ _ctxt: *mut ::std::os::raw::c_void,
+ tokens: *mut BNInstructionTextToken,
+ count: usize,
+) {
+ let tokens = std::ptr::slice_from_raw_parts_mut(tokens, count);
+ // SAFETY: tokens must have been allocated by rust.
+ let boxed_tokens = Box::from_raw(tokens);
+ for token in boxed_tokens {
+ InstructionTextToken::free_raw(token);
+ }
+}
+
+unsafe extern "C" fn cb_free_lines(
+ _ctxt: *mut ::std::os::raw::c_void,
+ lines: *mut BNTypeDefinitionLine,
+ count: usize,
+) {
+ let lines = std::ptr::slice_from_raw_parts_mut(lines, count);
+ // SAFETY: lines must have been allocated by rust.
+ let boxes_lines = Box::from_raw(lines);
+ for line in boxes_lines {
+ TypeDefinitionLine::free_owned_raw(line);
+ }
+}
diff --git a/rust/src/typearchive.rs b/rust/src/typearchive.rs
deleted file mode 100644
index 71a7058e..00000000
--- a/rust/src/typearchive.rs
+++ /dev/null
@@ -1,947 +0,0 @@
-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 },
- )
-}
diff --git a/rust/src/types.rs b/rust/src/types.rs
index c0156454..bd184b18 100644
--- a/rust/src/types.rs
+++ b/rust/src/types.rs
@@ -11,6 +11,7 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
+#![allow(unused)]
// TODO : More widely enforce the use of ref_from_raw vs just from_raw to simplify internal binding usage? Perhaps remove from_raw functions?
// TODO : Add documentation and fix examples
@@ -20,305 +21,33 @@ use binaryninjacore_sys::*;
use crate::{
architecture::{Architecture, CoreArchitecture},
- binaryview::{BinaryView, BinaryViewExt},
- callingconvention::CallingConvention,
- filemetadata::FileMetadata,
- function::Function,
- mlil::MediumLevelILFunction,
+ binary_view::{BinaryView, BinaryViewExt},
+ calling_convention::CoreCallingConvention,
rc::*,
- string::{raw_to_string, BnStrCompatible, BnString},
- symbol::Symbol,
+ string::{BnStrCompatible, BnString},
};
-use lazy_static::lazy_static;
-use std::ptr::null_mut;
+use crate::confidence::{Conf, MAX_CONFIDENCE, MIN_CONFIDENCE};
+use crate::string::{raw_to_string, strings_to_string_list};
+use crate::variable::{Variable, VariableSourceType};
+use std::borrow::Cow;
+use std::num::NonZeroUsize;
+use std::ops::{Index, IndexMut};
use std::{
- borrow::{Borrow, Cow},
- collections::{HashMap, HashSet},
+ collections::HashSet,
ffi::CStr,
- fmt::{self, Debug, Display, Formatter},
+ fmt::{Debug, Display, Formatter},
hash::{Hash, Hasher},
- iter::{zip, IntoIterator},
- mem::{self, ManuallyDrop},
- ops::Range,
- os::raw::c_char,
- ptr, result, slice,
- sync::Mutex,
+ iter::IntoIterator,
};
-pub type Result<R> = result::Result<R, ()>;
-
+pub type StructureType = BNStructureVariant;
pub type ReferenceType = BNReferenceType;
pub type TypeClass = BNTypeClass;
pub type NamedTypeReferenceClass = BNNamedTypeReferenceClass;
pub type MemberAccess = BNMemberAccess;
pub type MemberScope = BNMemberScope;
-pub type ILBranchDependence = BNILBranchDependence;
-pub type DataFlowQueryOption = BNDataFlowQueryOption;
-pub type VariableSourceType = BNVariableSourceType;
-
-////////////////
-// Confidence
-
-/// Compatible with the `BNType*WithConfidence` types
-pub struct Conf<T> {
- pub contents: T,
- pub confidence: u8,
-}
-
-pub trait ConfMergable<T, O> {
- type Result;
- /// Merge two confidence types' values depending on whichever has higher confidence
- /// In the event of a tie, the LHS (caller's) value is used.
- fn merge(self, other: O) -> Self::Result;
-}
-
-impl<T> Conf<T> {
- pub fn new(contents: T, confidence: u8) -> Self {
- Self {
- contents,
- confidence,
- }
- }
-
- pub fn map<U, F>(self, f: F) -> Conf<U>
- where
- F: FnOnce(T) -> U,
- {
- Conf::new(f(self.contents), self.confidence)
- }
-
- pub fn as_ref<U>(&self) -> Conf<&U>
- where
- T: AsRef<U>,
- {
- Conf::new(self.contents.as_ref(), self.confidence)
- }
-}
-
-/// Returns best value or LHS on tie
-///
-/// `Conf<T>` + `Conf<T>` → `Conf<T>`
-impl<T> ConfMergable<T, Conf<T>> for Conf<T> {
- type Result = Conf<T>;
- fn merge(self, other: Conf<T>) -> Conf<T> {
- if other.confidence > self.confidence {
- other
- } else {
- self
- }
- }
-}
-
-/// Returns LHS if RHS is None
-///
-/// `Conf<T>` + `Option<Conf<T>>` → `Conf<T>`
-impl<T> ConfMergable<T, Option<Conf<T>>> for Conf<T> {
- type Result = Conf<T>;
- fn merge(self, other: Option<Conf<T>>) -> Conf<T> {
- match other {
- Some(c @ Conf { confidence, .. }) if confidence > self.confidence => c,
- _ => self,
- }
- }
-}
-
-/// Returns RHS if LHS is None
-///
-/// `Option<Conf<T>>` + `Conf<T>` → `Conf<T>`
-impl<T> ConfMergable<T, Conf<T>> for Option<Conf<T>> {
- type Result = Conf<T>;
- fn merge(self, other: Conf<T>) -> Conf<T> {
- match self {
- Some(c @ Conf { confidence, .. }) if confidence >= other.confidence => c,
- _ => other,
- }
- }
-}
-
-/// Returns best non-None value or None
-///
-/// `Option<Conf<T>>` + `Option<Conf<T>>` → `Option<Conf<T>>`
-impl<T> ConfMergable<T, Option<Conf<T>>> for Option<Conf<T>> {
- type Result = Option<Conf<T>>;
- fn merge(self, other: Option<Conf<T>>) -> Option<Conf<T>> {
- match (self, other) {
- (
- Some(
- this @ Conf {
- confidence: this_confidence,
- ..
- },
- ),
- Some(
- other @ Conf {
- confidence: other_confidence,
- ..
- },
- ),
- ) => {
- if this_confidence >= other_confidence {
- Some(this)
- } else {
- Some(other)
- }
- }
- (None, Some(c)) => Some(c),
- (Some(c), None) => Some(c),
- (None, None) => None,
- }
- }
-}
-
-impl<T: Debug> Debug for Conf<T> {
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- write!(f, "{:?} ({} confidence)", self.contents, self.confidence)
- }
-}
-
-impl<T: Display> Display for Conf<T> {
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- write!(f, "{} ({} confidence)", self.contents, self.confidence)
- }
-}
-
-impl<T: PartialEq> PartialEq for Conf<T> {
- fn eq(&self, other: &Self) -> bool {
- self.contents.eq(&other.contents)
- }
-}
-
-impl<T: Eq> Eq for Conf<T> {}
-
-impl<T: Hash> Hash for Conf<T> {
- fn hash<H: Hasher>(&self, state: &mut H) {
- self.contents.hash(state);
- }
-}
-
-impl<'a, T> From<&'a Conf<T>> for Conf<&'a T> {
- fn from(c: &'a Conf<T>) -> Self {
- Conf::new(&c.contents, c.confidence)
- }
-}
-
-impl<'a, T: RefCountable> From<&'a Conf<Ref<T>>> for Conf<&'a T> {
- fn from(c: &'a Conf<Ref<T>>) -> Self {
- Conf::new(c.contents.as_ref(), c.confidence)
- }
-}
-
-impl<'a, T: RefCountable> From<&'a Ref<T>> for Conf<&'a T> {
- fn from(r: &'a Ref<T>) -> Self {
- r.as_ref().into()
- }
-}
-
-#[inline]
-pub fn min_confidence() -> u8 {
- u8::MIN
-}
-
-#[inline]
-pub fn max_confidence() -> u8 {
- u8::MAX
-}
-
-impl<T: Clone> Clone for Conf<T> {
- fn clone(&self) -> Self {
- Self {
- contents: self.contents.clone(),
- confidence: self.confidence,
- }
- }
-}
-
-impl<T: Copy> Copy for Conf<T> {}
-
-impl<T> From<T> for Conf<T> {
- fn from(contents: T) -> Self {
- Self::new(contents, max_confidence())
- }
-}
-
-impl From<BNTypeWithConfidence> for Conf<Ref<Type>> {
- fn from(type_with_confidence: BNTypeWithConfidence) -> Self {
- Self::new(
- unsafe { Type::ref_from_raw(type_with_confidence.type_) },
- type_with_confidence.confidence,
- )
- }
-}
-
-impl From<BNBoolWithConfidence> for Conf<bool> {
- fn from(bool_with_confidence: BNBoolWithConfidence) -> Self {
- Self::new(bool_with_confidence.value, bool_with_confidence.confidence)
- }
-}
-
-impl From<BNCallingConventionWithConfidence> for Conf<Ref<CallingConvention<CoreArchitecture>>> {
- fn from(cc_with_confidence: BNCallingConventionWithConfidence) -> Self {
- Self::new(
- unsafe {
- CallingConvention::ref_from_raw(
- cc_with_confidence.convention,
- CoreArchitecture::from_raw(BNGetCallingConventionArchitecture(
- cc_with_confidence.convention,
- )),
- )
- },
- cc_with_confidence.confidence,
- )
- }
-}
-
-impl From<BNOffsetWithConfidence> for Conf<i64> {
- fn from(offset_with_confidence: BNOffsetWithConfidence) -> Self {
- Self::new(
- offset_with_confidence.value,
- offset_with_confidence.confidence,
- )
- }
-}
-
-impl From<Conf<&Type>> for BNTypeWithConfidence {
- fn from(conf: Conf<&Type>) -> Self {
- Self {
- type_: conf.contents.handle,
- confidence: conf.confidence,
- }
- }
-}
-
-impl From<Conf<bool>> for BNBoolWithConfidence {
- fn from(conf: Conf<bool>) -> Self {
- Self {
- value: conf.contents,
- confidence: conf.confidence,
- }
- }
-}
-
-impl<A: Architecture> From<Conf<&CallingConvention<A>>> for BNCallingConventionWithConfidence {
- fn from(conf: Conf<&CallingConvention<A>>) -> Self {
- Self {
- convention: conf.contents.handle,
- confidence: conf.confidence,
- }
- }
-}
-
-impl From<Conf<i64>> for BNOffsetWithConfidence {
- fn from(conf: Conf<i64>) -> Self {
- Self {
- value: conf.contents,
- confidence: conf.confidence,
- }
- }
-}
-
-//////////////////
-// Type Builder
+pub type IntegerDisplayType = BNIntegerDisplayType;
#[derive(PartialEq, Eq, Hash)]
pub struct TypeBuilder {
@@ -396,59 +125,54 @@ impl TypeBuilder {
unsafe { BNIsTypeBuilderFloatingPoint(self.handle) }
}
- pub fn target(&self) -> Result<Conf<Ref<Type>>> {
+ pub fn child_type(&self) -> Option<Conf<Ref<Type>>> {
let raw_target = unsafe { BNGetTypeBuilderChildType(self.handle) };
- if raw_target.type_.is_null() {
- Err(())
- } else {
- Ok(raw_target.into())
+ match raw_target.type_.is_null() {
+ false => Some(Conf::<Ref<Type>>::from_owned_raw(raw_target)),
+ true => None,
}
}
- pub fn element_type(&self) -> Result<Conf<Ref<Type>>> {
- let raw_target = unsafe { BNGetTypeBuilderChildType(self.handle) };
- if raw_target.type_.is_null() {
- Err(())
- } else {
- Ok(raw_target.into())
- }
+ /// This is an alias for [`Self::child_type`].
+ pub fn target(&self) -> Option<Conf<Ref<Type>>> {
+ self.child_type()
}
- pub fn return_value(&self) -> Result<Conf<Ref<Type>>> {
- let raw_target = unsafe { BNGetTypeBuilderChildType(self.handle) };
- if raw_target.type_.is_null() {
- Err(())
- } else {
- Ok(raw_target.into())
- }
+ /// This is an alias for [`Self::child_type`].
+ pub fn element_type(&self) -> Option<Conf<Ref<Type>>> {
+ self.child_type()
}
- pub fn calling_convention(&self) -> Result<Conf<Ref<CallingConvention<CoreArchitecture>>>> {
- let convention_confidence = unsafe { BNGetTypeBuilderCallingConvention(self.handle) };
- if convention_confidence.convention.is_null() {
- Err(())
- } else {
- Ok(convention_confidence.into())
+ /// This is an alias for [`Self::child_type`].
+ pub fn return_value(&self) -> Option<Conf<Ref<Type>>> {
+ self.child_type()
+ }
+
+ pub fn calling_convention(&self) -> Option<Conf<Ref<CoreCallingConvention>>> {
+ let raw_convention_confidence = unsafe { BNGetTypeBuilderCallingConvention(self.handle) };
+ match raw_convention_confidence.convention.is_null() {
+ false => Some(Conf::<Ref<CoreCallingConvention>>::from_owned_raw(
+ raw_convention_confidence,
+ )),
+ true => None,
}
}
- pub fn parameters(&self) -> Result<Vec<FunctionParameter>> {
+ pub fn parameters(&self) -> Option<Vec<FunctionParameter>> {
unsafe {
let mut count = 0;
- let parameters_raw = BNGetTypeBuilderParameters(self.handle, &mut count);
- if parameters_raw.is_null() {
- Err(())
- } else {
- let parameters: &[BNFunctionParameter] =
- slice::from_raw_parts(parameters_raw, count);
-
- let result = (0..count)
- .map(|i| FunctionParameter::from_raw(parameters[i]))
- .collect();
-
- BNFreeTypeParameterList(parameters_raw, count);
-
- Ok(result)
+ let raw_parameters_ptr = BNGetTypeBuilderParameters(self.handle, &mut count);
+ match raw_parameters_ptr.is_null() {
+ false => {
+ let raw_parameters = std::slice::from_raw_parts(raw_parameters_ptr, count);
+ let parameters = raw_parameters
+ .iter()
+ .map(FunctionParameter::from_raw)
+ .collect();
+ BNFreeTypeParameterList(raw_parameters_ptr, count);
+ Some(parameters)
+ }
+ true => None,
}
}
}
@@ -465,30 +189,33 @@ impl TypeBuilder {
unsafe { BNIsTypeBuilderPure(self.handle).into() }
}
- pub fn get_structure(&self) -> Result<Ref<Structure>> {
- let result = unsafe { BNGetTypeBuilderStructure(self.handle) };
- if result.is_null() {
- Err(())
- } else {
- Ok(unsafe { Structure::ref_from_raw(result) })
+ // TODO: This naming is problematic... rename to `as_structure`?
+ // TODO: We wouldn't need these sort of functions if we destructured `Type`...
+ pub fn get_structure(&self) -> Option<Ref<Structure>> {
+ let raw_struct_ptr = unsafe { BNGetTypeBuilderStructure(self.handle) };
+ match raw_struct_ptr.is_null() {
+ false => Some(unsafe { Structure::ref_from_raw(raw_struct_ptr) }),
+ true => None,
}
}
- pub fn get_enumeration(&self) -> Result<Ref<Enumeration>> {
- let result = unsafe { BNGetTypeBuilderEnumeration(self.handle) };
- if result.is_null() {
- Err(())
- } else {
- Ok(unsafe { Enumeration::ref_from_raw(result) })
+ // TODO: This naming is problematic... rename to `as_enumeration`?
+ // TODO: We wouldn't need these sort of functions if we destructured `Type`...
+ pub fn get_enumeration(&self) -> Option<Ref<Enumeration>> {
+ let raw_enum_ptr = unsafe { BNGetTypeBuilderEnumeration(self.handle) };
+ match raw_enum_ptr.is_null() {
+ false => Some(unsafe { Enumeration::ref_from_raw(raw_enum_ptr) }),
+ true => None,
}
}
- pub fn get_named_type_reference(&self) -> Result<Ref<NamedTypeReference>> {
- let result = unsafe { BNGetTypeBuilderNamedTypeReference(self.handle) };
- if result.is_null() {
- Err(())
- } else {
- Ok(unsafe { NamedTypeReference::ref_from_raw(result) })
+ // TODO: This naming is problematic... rename to `as_named_type_reference`?
+ // TODO: We wouldn't need these sort of functions if we destructured `Type`...
+ pub fn get_named_type_reference(&self) -> Option<Ref<NamedTypeReference>> {
+ let raw_type_ref_ptr = unsafe { BNGetTypeBuilderNamedTypeReference(self.handle) };
+ match raw_type_ref_ptr.is_null() {
+ false => Some(unsafe { NamedTypeReference::ref_from_raw(raw_type_ref_ptr) }),
+ true => None,
}
}
@@ -520,7 +247,7 @@ impl TypeBuilder {
}
pub fn int(width: usize, is_signed: bool) -> Self {
- let mut is_signed = Conf::new(is_signed, max_confidence()).into();
+ let mut is_signed = Conf::new(is_signed, MAX_CONFIDENCE).into();
unsafe {
Self::from_raw(BNCreateIntegerTypeBuilder(
@@ -532,7 +259,7 @@ impl TypeBuilder {
}
pub fn named_int<S: BnStrCompatible>(width: usize, is_signed: bool, alt_name: S) -> Self {
- let mut is_signed = Conf::new(is_signed, max_confidence()).into();
+ let mut is_signed = Conf::new(is_signed, MAX_CONFIDENCE).into();
// let alt_name = BnString::new(alt_name);
let alt_name = alt_name.into_bytes_with_nul(); // This segfaulted once, so the above version is there if we need to change to it, but in theory this is copied into a `const string&` on the C++ side; I'm just not 100% confident that a constant reference copies data
@@ -566,24 +293,27 @@ impl TypeBuilder {
}
}
- pub fn array<'a, T: Into<Conf<&'a Type>>>(t: T, count: u64) -> Self {
- unsafe { Self::from_raw(BNCreateArrayTypeBuilder(&t.into().into(), count)) }
+ pub fn array<'a, T: Into<Conf<&'a Type>>>(ty: T, count: u64) -> Self {
+ let owned_raw_ty = Conf::<&Type>::into_raw(ty.into());
+ unsafe { Self::from_raw(BNCreateArrayTypeBuilder(&owned_raw_ty, count)) }
}
- /// The C/C++ APIs require an associated architecture, but in the core we only query the default_int_size if the given width is 0
- /// For simplicity's sake, that convention isn't followed and you can query the default_int_size from an arch, if you have it, if you need to
+ /// ## NOTE
+ ///
+ /// The C/C++ APIs require an associated architecture, but in the core we only query the default_int_size if the given width is 0.
+ ///
+ /// For simplicity's sake, that convention isn't followed, and you can query [`Architecture::default_integer_size`] if you need to.
pub fn enumeration<T: Into<Conf<bool>>>(
enumeration: &Enumeration,
- width: usize,
+ width: NonZeroUsize,
is_signed: T,
) -> Self {
unsafe {
- // TODO : This is _extremely fragile_, we should change the internals of BNCreateEnumerationTypeBuilder instead of doing this
- let mut fake_arch: BNArchitecture = mem::zeroed();
Self::from_raw(BNCreateEnumerationTypeBuilder(
- &mut fake_arch,
+ // TODO: We pass nullptr arch, really we should not even be passing arch.
+ std::ptr::null_mut(),
enumeration.handle,
- width,
+ width.get(),
&mut is_signed.into().into(),
))
}
@@ -594,8 +324,8 @@ impl TypeBuilder {
}
pub fn named_type(type_reference: NamedTypeReference) -> Self {
- let mut is_const = Conf::new(false, min_confidence()).into();
- let mut is_volatile = Conf::new(false, min_confidence()).into();
+ let mut is_const = Conf::new(false, MIN_CONFIDENCE).into();
+ let mut is_volatile = Conf::new(false, MIN_CONFIDENCE).into();
unsafe {
Self::from_raw(BNCreateNamedTypeReferenceBuilder(
type_reference.handle,
@@ -607,28 +337,31 @@ impl TypeBuilder {
}
}
- pub fn named_type_from_type<S: BnStrCompatible>(name: S, t: &Type) -> Self {
- let mut name = QualifiedName::from(name);
+ pub fn named_type_from_type<T: Into<QualifiedName>>(name: T, t: &Type) -> Self {
+ let mut raw_name = QualifiedName::into_raw(name.into());
+ let id = c"";
- unsafe {
+ let result = unsafe {
Self::from_raw(BNCreateNamedTypeReferenceBuilderFromTypeAndId(
- BnString::new("").as_ptr() as *mut _,
- &mut name.0,
+ id.as_ptr() as *mut _,
+ &mut raw_name,
t.handle,
))
- }
+ };
+ QualifiedName::free_raw(raw_name);
+ result
}
// TODO : BNCreateFunctionTypeBuilder
- pub fn pointer<'a, A: Architecture, T: Into<Conf<&'a Type>>>(arch: &A, t: T) -> Self {
- let mut is_const = Conf::new(false, min_confidence()).into();
- let mut is_volatile = Conf::new(false, min_confidence()).into();
-
+ pub fn pointer<'a, A: Architecture, T: Into<Conf<&'a Type>>>(arch: &A, ty: T) -> Self {
+ let mut is_const = Conf::new(false, MIN_CONFIDENCE).into();
+ let mut is_volatile = Conf::new(false, MIN_CONFIDENCE).into();
+ let owned_raw_ty = Conf::<&Type>::into_raw(ty.into());
unsafe {
Self::from_raw(BNCreatePointerTypeBuilder(
- arch.as_ref().0,
- &t.into().into(),
+ arch.as_ref().handle,
+ &owned_raw_ty,
&mut is_const,
&mut is_volatile,
ReferenceType::PointerReferenceType,
@@ -636,14 +369,14 @@ impl TypeBuilder {
}
}
- pub fn const_pointer<'a, A: Architecture, T: Into<Conf<&'a Type>>>(arch: &A, t: T) -> Self {
- let mut is_const = Conf::new(true, max_confidence()).into();
- let mut is_volatile = Conf::new(false, min_confidence()).into();
-
+ pub fn const_pointer<'a, A: Architecture, T: Into<Conf<&'a Type>>>(arch: &A, ty: T) -> Self {
+ let mut is_const = Conf::new(true, MAX_CONFIDENCE).into();
+ let mut is_volatile = Conf::new(false, MIN_CONFIDENCE).into();
+ let owned_raw_ty = Conf::<&Type>::into_raw(ty.into());
unsafe {
Self::from_raw(BNCreatePointerTypeBuilder(
- arch.as_ref().0,
- &t.into().into(),
+ arch.as_ref().handle,
+ &owned_raw_ty,
&mut is_const,
&mut is_volatile,
ReferenceType::PointerReferenceType,
@@ -652,19 +385,19 @@ impl TypeBuilder {
}
pub fn pointer_of_width<'a, T: Into<Conf<&'a Type>>>(
- t: T,
+ ty: T,
size: usize,
is_const: bool,
is_volatile: bool,
ref_type: Option<ReferenceType>,
) -> Self {
- let mut is_const = Conf::new(is_const, max_confidence()).into();
- let mut is_volatile = Conf::new(is_volatile, max_confidence()).into();
-
+ let mut is_const = Conf::new(is_const, MAX_CONFIDENCE).into();
+ let mut is_volatile = Conf::new(is_volatile, MAX_CONFIDENCE).into();
+ let owned_raw_ty = Conf::<&Type>::into_raw(ty.into());
unsafe {
Self::from_raw(BNCreatePointerTypeBuilderOfWidth(
size,
- &t.into().into(),
+ &owned_raw_ty,
&mut is_const,
&mut is_volatile,
ref_type.unwrap_or(ReferenceType::PointerReferenceType),
@@ -674,17 +407,18 @@ impl TypeBuilder {
pub fn pointer_with_options<'a, A: Architecture, T: Into<Conf<&'a Type>>>(
arch: &A,
- t: T,
+ ty: T,
is_const: bool,
is_volatile: bool,
ref_type: Option<ReferenceType>,
) -> Self {
- let mut is_const = Conf::new(is_const, max_confidence()).into();
- let mut is_volatile = Conf::new(is_volatile, max_confidence()).into();
+ let mut is_const = Conf::new(is_const, MAX_CONFIDENCE).into();
+ let mut is_volatile = Conf::new(is_volatile, MAX_CONFIDENCE).into();
+ let owned_raw_ty = Conf::<&Type>::into_raw(ty.into());
unsafe {
Self::from_raw(BNCreatePointerTypeBuilder(
- arch.as_ref().0,
- &t.into().into(),
+ arch.as_ref().handle,
+ &owned_raw_ty,
&mut is_const,
&mut is_volatile,
ref_type.unwrap_or(ReferenceType::PointerReferenceType),
@@ -693,10 +427,10 @@ impl TypeBuilder {
}
}
-impl fmt::Display for TypeBuilder {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+impl Display for TypeBuilder {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", unsafe {
- BnString::from_raw(BNGetTypeBuilderString(self.handle, ptr::null_mut()))
+ BnString::from_raw(BNGetTypeBuilderString(self.handle, std::ptr::null_mut()))
})
}
}
@@ -707,16 +441,13 @@ impl Drop for TypeBuilder {
}
}
-//////////
-// Type
-
#[repr(transparent)]
pub struct Type {
pub(crate) handle: *mut BNType,
}
/// ```no_run
-/// # use crate::binaryninja::binaryview::BinaryViewExt;
+/// # use crate::binaryninja::binary_view::BinaryViewExt;
/// # use binaryninja::types::Type;
/// let bv = binaryninja::load("example.bin").unwrap();
/// let my_custom_type_1 = Type::named_int(5, false, "my_w");
@@ -725,7 +456,7 @@ pub struct Type {
/// bv.define_user_type("int_2", &my_custom_type_2);
/// ```
impl Type {
- unsafe fn from_raw(handle: *mut BNType) -> Self {
+ pub(crate) unsafe fn from_raw(handle: *mut BNType) -> Self {
debug_assert!(!handle.is_null());
Self { handle }
}
@@ -739,12 +470,12 @@ impl Type {
TypeBuilder::new(self)
}
- // Readable properties
-
pub fn type_class(&self) -> TypeClass {
unsafe { BNGetTypeClass(self.handle) }
}
+ // TODO: We need to decide on a public type to represent type width.
+ // TODO: The api uses both `u64` and `usize`, pick one or a new type!
pub fn width(&self) -> u64 {
unsafe { BNGetTypeWidth(self.handle) }
}
@@ -769,60 +500,54 @@ impl Type {
unsafe { BNIsTypeFloatingPoint(self.handle) }
}
- pub fn target(&self) -> Result<Conf<Ref<Type>>> {
+ pub fn child_type(&self) -> Option<Conf<Ref<Type>>> {
let raw_target = unsafe { BNGetChildType(self.handle) };
- if raw_target.type_.is_null() {
- Err(())
- } else {
- Ok(raw_target.into())
+ match raw_target.type_.is_null() {
+ false => Some(Conf::<Ref<Type>>::from_owned_raw(raw_target)),
+ true => None,
}
}
- pub fn element_type(&self) -> Result<Conf<Ref<Type>>> {
- let raw_target = unsafe { BNGetChildType(self.handle) };
- if raw_target.type_.is_null() {
- Err(())
- } else {
- Ok(raw_target.into())
- }
+ /// This is an alias for [`Self::child_type`].
+ pub fn target(&self) -> Option<Conf<Ref<Type>>> {
+ self.child_type()
}
- pub fn return_value(&self) -> Result<Conf<Ref<Type>>> {
- let raw_target = unsafe { BNGetChildType(self.handle) };
- if raw_target.type_.is_null() {
- Err(())
- } else {
- Ok(raw_target.into())
- }
+ /// This is an alias for [`Self::child_type`].
+ pub fn element_type(&self) -> Option<Conf<Ref<Type>>> {
+ self.child_type()
+ }
+
+ /// This is an alias for [`Self::child_type`].
+ pub fn return_value(&self) -> Option<Conf<Ref<Type>>> {
+ self.child_type()
}
- pub fn calling_convention(&self) -> Result<Conf<Ref<CallingConvention<CoreArchitecture>>>> {
+ pub fn calling_convention(&self) -> Option<Conf<Ref<CoreCallingConvention>>> {
let convention_confidence = unsafe { BNGetTypeCallingConvention(self.handle) };
- if convention_confidence.convention.is_null() {
- Err(())
- } else {
- Ok(convention_confidence.into())
+ match convention_confidence.convention.is_null() {
+ false => Some(Conf::<Ref<CoreCallingConvention>>::from_owned_raw(
+ convention_confidence,
+ )),
+ true => None,
}
}
- pub fn parameters(&self) -> Result<Vec<FunctionParameter>> {
+ pub fn parameters(&self) -> Option<Vec<FunctionParameter>> {
unsafe {
let mut count = 0;
- let parameters_raw: *mut BNFunctionParameter =
- BNGetTypeParameters(self.handle, &mut count);
- if parameters_raw.is_null() {
- Err(())
- } else {
- let parameters: &[BNFunctionParameter] =
- slice::from_raw_parts(parameters_raw, count);
-
- let result = (0..count)
- .map(|i| FunctionParameter::from_raw(parameters[i]))
- .collect();
-
- BNFreeTypeParameterList(parameters_raw, count);
-
- Ok(result)
+ let raw_parameters_ptr = BNGetTypeParameters(self.handle, &mut count);
+ match raw_parameters_ptr.is_null() {
+ false => {
+ let raw_parameters = std::slice::from_raw_parts(raw_parameters_ptr, count);
+ let parameters = raw_parameters
+ .iter()
+ .map(FunctionParameter::from_raw)
+ .collect();
+ BNFreeTypeParameterList(raw_parameters_ptr, count);
+ Some(parameters)
+ }
+ true => None,
}
}
}
@@ -839,30 +564,33 @@ impl Type {
unsafe { BNIsTypePure(self.handle).into() }
}
- pub fn get_structure(&self) -> Result<Ref<Structure>> {
- let result = unsafe { BNGetTypeStructure(self.handle) };
- if result.is_null() {
- Err(())
- } else {
- Ok(unsafe { Structure::ref_from_raw(result) })
+ // TODO: This naming is problematic... rename to `as_structure`?
+ // TODO: We wouldn't need these sort of functions if we destructured `Type`...
+ pub fn get_structure(&self) -> Option<Ref<Structure>> {
+ let raw_struct_ptr = unsafe { BNGetTypeStructure(self.handle) };
+ match raw_struct_ptr.is_null() {
+ false => Some(unsafe { Structure::ref_from_raw(raw_struct_ptr) }),
+ true => None,
}
}
- pub fn get_enumeration(&self) -> Result<Ref<Enumeration>> {
- let result = unsafe { BNGetTypeEnumeration(self.handle) };
- if result.is_null() {
- Err(())
- } else {
- Ok(unsafe { Enumeration::ref_from_raw(result) })
+ // TODO: This naming is problematic... rename to `as_enumeration`?
+ // TODO: We wouldn't need these sort of functions if we destructured `Type`...
+ pub fn get_enumeration(&self) -> Option<Ref<Enumeration>> {
+ let raw_enum_ptr = unsafe { BNGetTypeEnumeration(self.handle) };
+ match raw_enum_ptr.is_null() {
+ false => Some(unsafe { Enumeration::ref_from_raw(raw_enum_ptr) }),
+ true => None,
}
}
- pub fn get_named_type_reference(&self) -> Result<Ref<NamedTypeReference>> {
- let result = unsafe { BNGetTypeNamedTypeReference(self.handle) };
- if result.is_null() {
- Err(())
- } else {
- Ok(unsafe { NamedTypeReference::ref_from_raw(result) })
+ // TODO: This naming is problematic... rename to `as_named_type_reference`?
+ // TODO: We wouldn't need these sort of functions if we destructured `Type`...
+ pub fn get_named_type_reference(&self) -> Option<Ref<NamedTypeReference>> {
+ let raw_type_ref_ptr = unsafe { BNGetTypeNamedTypeReference(self.handle) };
+ match raw_type_ref_ptr.is_null() {
+ false => Some(unsafe { NamedTypeReference::ref_from_raw(raw_type_ref_ptr) }),
+ true => None,
}
}
@@ -878,12 +606,11 @@ impl Type {
unsafe { BNGetTypeStackAdjustment(self.handle).into() }
}
- pub fn registered_name(&self) -> Result<Ref<NamedTypeReference>> {
- let result = unsafe { BNGetRegisteredTypeName(self.handle) };
- if result.is_null() {
- Err(())
- } else {
- Ok(unsafe { NamedTypeReference::ref_from_raw(result) })
+ pub fn registered_name(&self) -> Option<Ref<NamedTypeReference>> {
+ let raw_type_ref_ptr = unsafe { BNGetRegisteredTypeName(self.handle) };
+ match raw_type_ref_ptr.is_null() {
+ false => Some(unsafe { NamedTypeReference::ref_from_raw(raw_type_ref_ptr) }),
+ true => None,
}
}
@@ -912,7 +639,7 @@ impl Type {
}
pub fn int(width: usize, is_signed: bool) -> Ref<Self> {
- let mut is_signed = Conf::new(is_signed, max_confidence()).into();
+ let mut is_signed = Conf::new(is_signed, MAX_CONFIDENCE).into();
unsafe {
Self::ref_from_raw(BNCreateIntegerType(
width,
@@ -923,7 +650,7 @@ impl Type {
}
pub fn named_int<S: BnStrCompatible>(width: usize, is_signed: bool, alt_name: S) -> Ref<Self> {
- let mut is_signed = Conf::new(is_signed, max_confidence()).into();
+ let mut is_signed = Conf::new(is_signed, MAX_CONFIDENCE).into();
// let alt_name = BnString::new(alt_name);
let alt_name = alt_name.into_bytes_with_nul(); // This segfaulted once, so the above version is there if we need to change to it, but in theory this is copied into a `const string&` on the C++ side; I'm just not 100% confident that a constant reference copies data
@@ -952,25 +679,27 @@ impl Type {
unsafe { Self::ref_from_raw(BNCreateFloatType(width, alt_name.as_ref().as_ptr() as _)) }
}
- pub fn array<'a, T: Into<Conf<&'a Type>>>(t: T, count: u64) -> Ref<Self> {
- unsafe { Self::ref_from_raw(BNCreateArrayType(&t.into().into(), count)) }
+ pub fn array<'a, T: Into<Conf<&'a Type>>>(ty: T, count: u64) -> Ref<Self> {
+ let owned_raw_ty = Conf::<&Type>::into_raw(ty.into());
+ unsafe { Self::ref_from_raw(BNCreateArrayType(&owned_raw_ty, count)) }
}
- /// The C/C++ APIs require an associated architecture, but in the core we only query the default_int_size if the given width is 0
+ /// ## NOTE
///
- /// For simplicity's sake, that convention isn't followed and you can query the default_int_size from an arch, if you have it, if you need to
+ /// The C/C++ APIs require an associated architecture, but in the core we only query the default_int_size if the given width is 0.
+ ///
+ /// For simplicity's sake, that convention isn't followed, and you can query [`Architecture::default_integer_size`] if you need to.
pub fn enumeration<T: Into<Conf<bool>>>(
enumeration: &Enumeration,
- width: usize,
+ width: NonZeroUsize,
is_signed: T,
) -> Ref<Self> {
unsafe {
- // TODO : This is _extremely fragile_, we should change the internals of BNCreateEnumerationType instead of doing this
- let mut fake_arch: BNArchitecture = mem::zeroed();
Self::ref_from_raw(BNCreateEnumerationType(
- &mut fake_arch,
+ // TODO: We pass nullptr arch, really we should not even be passing arch.
+ std::ptr::null_mut(),
enumeration.handle,
- width,
+ width.get(),
&mut is_signed.into().into(),
))
}
@@ -981,8 +710,8 @@ impl Type {
}
pub fn named_type(type_reference: &NamedTypeReference) -> Ref<Self> {
- let mut is_const = Conf::new(false, min_confidence()).into();
- let mut is_volatile = Conf::new(false, min_confidence()).into();
+ let mut is_const = Conf::new(false, MIN_CONFIDENCE).into();
+ let mut is_volatile = Conf::new(false, MIN_CONFIDENCE).into();
unsafe {
Self::ref_from_raw(BNCreateNamedTypeReference(
type_reference.handle,
@@ -994,65 +723,56 @@ impl Type {
}
}
- pub fn named_type_from_type<S: BnStrCompatible>(name: S, t: &Type) -> Ref<Self> {
- let mut name = QualifiedName::from(name);
+ pub fn named_type_from_type<T: Into<QualifiedName>>(name: T, t: &Type) -> Ref<Self> {
+ let mut raw_name = QualifiedName::into_raw(name.into());
+ // TODO: No id is present for this call?
+ let id = c"";
- unsafe {
+ let result = unsafe {
Self::ref_from_raw(BNCreateNamedTypeReferenceFromTypeAndId(
- BnString::new("").as_ptr() as *mut _,
- &mut name.0,
+ id.as_ptr(),
+ &mut raw_name,
t.handle,
))
- }
+ };
+ QualifiedName::free_raw(raw_name);
+ result
}
+ // TODO: FunctionBuilder
pub fn function<'a, T: Into<Conf<&'a Type>>>(
return_type: T,
- parameters: &[FunctionParameter],
+ parameters: Vec<FunctionParameter>,
variable_arguments: bool,
) -> Ref<Self> {
- let mut return_type = return_type.into().into();
- let mut variable_arguments = Conf::new(variable_arguments, max_confidence()).into();
- let mut can_return = Conf::new(true, min_confidence()).into();
- let mut pure = Conf::new(false, min_confidence()).into();
+ let mut owned_raw_return_type = Conf::<&Type>::into_raw(return_type.into());
+ let mut variable_arguments = Conf::new(variable_arguments, MAX_CONFIDENCE).into();
+ let mut can_return = Conf::new(true, MIN_CONFIDENCE).into();
+ let mut pure = Conf::new(false, MIN_CONFIDENCE).into();
let mut raw_calling_convention: BNCallingConventionWithConfidence =
BNCallingConventionWithConfidence {
- convention: ptr::null_mut(),
- confidence: min_confidence(),
- };
-
- let mut stack_adjust = Conf::<i64>::new(0, min_confidence()).into();
- let mut raw_parameters = Vec::<BNFunctionParameter>::with_capacity(parameters.len());
- let mut parameter_name_references = Vec::with_capacity(parameters.len());
- for parameter in parameters {
- let raw_name = parameter.name.as_str().into_bytes_with_nul();
- let location = match &parameter.location {
- Some(location) => location.raw(),
- None => unsafe { mem::zeroed() },
+ convention: std::ptr::null_mut(),
+ confidence: MIN_CONFIDENCE,
};
- raw_parameters.push(BNFunctionParameter {
- name: raw_name.as_slice().as_ptr() as *mut _,
- type_: parameter.t.contents.handle,
- typeConfidence: parameter.t.confidence,
- defaultLocation: parameter.location.is_none(),
- location,
- });
- parameter_name_references.push(raw_name);
- }
- let reg_stack_adjust_regs = ptr::null_mut();
- let reg_stack_adjust_values = ptr::null_mut();
+ let mut stack_adjust = Conf::new(0, MIN_CONFIDENCE).into();
+ let mut raw_parameters = parameters
+ .into_iter()
+ .map(FunctionParameter::into_raw)
+ .collect::<Vec<_>>();
+ let reg_stack_adjust_regs = std::ptr::null_mut();
+ let reg_stack_adjust_values = std::ptr::null_mut();
let mut return_regs: BNRegisterSetWithConfidence = BNRegisterSetWithConfidence {
- regs: ptr::null_mut(),
+ regs: std::ptr::null_mut(),
count: 0,
confidence: 0,
};
- unsafe {
+ let result = unsafe {
Self::ref_from_raw(BNNewTypeReference(BNCreateFunctionType(
- &mut return_type,
+ &mut owned_raw_return_type,
&mut raw_calling_convention,
raw_parameters.as_mut_ptr(),
raw_parameters.len(),
@@ -1066,14 +786,20 @@ impl Type {
BNNameType::NoNameType,
&mut pure,
)))
+ };
+
+ for raw_param in raw_parameters {
+ FunctionParameter::free_raw(raw_param);
}
+
+ result
}
- pub fn function_with_options<
+ // TODO: FunctionBuilder
+ pub fn function_with_opts<
'a,
- A: Architecture,
T: Into<Conf<&'a Type>>,
- C: Into<Conf<&'a CallingConvention<A>>>,
+ C: Into<Conf<Ref<CoreCallingConvention>>>,
>(
return_type: T,
parameters: &[FunctionParameter],
@@ -1081,52 +807,35 @@ impl Type {
calling_convention: C,
stack_adjust: Conf<i64>,
) -> Ref<Self> {
- let mut return_type = return_type.into().into();
- let mut variable_arguments = Conf::new(variable_arguments, max_confidence()).into();
- let mut can_return = Conf::new(true, min_confidence()).into();
- let mut pure = Conf::new(false, min_confidence()).into();
- let mut raw_calling_convention: BNCallingConventionWithConfidence =
- calling_convention.into().into();
- let mut stack_adjust = stack_adjust.into();
-
- let mut raw_parameters = Vec::<BNFunctionParameter>::with_capacity(parameters.len());
- let mut parameter_name_references = Vec::with_capacity(parameters.len());
- let mut name_ptrs = vec![];
- for parameter in parameters {
- name_ptrs.push(parameter.name.clone());
- }
+ let mut owned_raw_return_type = Conf::<&Type>::into_raw(return_type.into());
+ let mut variable_arguments = Conf::new(variable_arguments, MAX_CONFIDENCE).into();
+ let mut can_return = Conf::new(true, MIN_CONFIDENCE).into();
+ let mut pure = Conf::new(false, MIN_CONFIDENCE).into();
- for (name, parameter) in zip(name_ptrs, parameters) {
- let raw_name = name.as_str().into_bytes_with_nul();
- let location = match &parameter.location {
- Some(location) => location.raw(),
- None => unsafe { mem::zeroed() },
- };
+ let mut owned_raw_calling_convention =
+ Conf::<Ref<CoreCallingConvention>>::into_owned_raw(&calling_convention.into());
- raw_parameters.push(BNFunctionParameter {
- name: raw_name.as_slice().as_ptr() as *mut _,
- type_: parameter.t.contents.handle,
- typeConfidence: parameter.t.confidence,
- defaultLocation: parameter.location.is_none(),
- location,
- });
- parameter_name_references.push(raw_name);
- }
+ let mut stack_adjust = stack_adjust.into();
+ let mut raw_parameters = parameters
+ .iter()
+ .cloned()
+ .map(FunctionParameter::into_raw)
+ .collect::<Vec<_>>();
// TODO: Update type signature and include these (will be a breaking change)
- let reg_stack_adjust_regs = ptr::null_mut();
- let reg_stack_adjust_values = ptr::null_mut();
+ let reg_stack_adjust_regs = std::ptr::null_mut();
+ let reg_stack_adjust_values = std::ptr::null_mut();
let mut return_regs: BNRegisterSetWithConfidence = BNRegisterSetWithConfidence {
- regs: ptr::null_mut(),
+ regs: std::ptr::null_mut(),
count: 0,
confidence: 0,
};
- unsafe {
+ let result = unsafe {
Self::ref_from_raw(BNCreateFunctionType(
- &mut return_type,
- &mut raw_calling_convention,
+ &mut owned_raw_return_type,
+ &mut owned_raw_calling_convention,
raw_parameters.as_mut_ptr(),
raw_parameters.len(),
&mut variable_arguments,
@@ -1139,16 +848,23 @@ impl Type {
BNNameType::NoNameType,
&mut pure,
))
+ };
+
+ for raw_param in raw_parameters {
+ FunctionParameter::free_raw(raw_param);
}
+
+ result
}
- pub fn pointer<'a, A: Architecture, T: Into<Conf<&'a Type>>>(arch: &A, t: T) -> Ref<Self> {
- let mut is_const = Conf::new(false, min_confidence()).into();
- let mut is_volatile = Conf::new(false, min_confidence()).into();
+ pub fn pointer<'a, A: Architecture, T: Into<Conf<&'a Type>>>(arch: &A, ty: T) -> Ref<Self> {
+ let mut is_const = Conf::new(false, MIN_CONFIDENCE).into();
+ let mut is_volatile = Conf::new(false, MIN_CONFIDENCE).into();
+ let owned_raw_ty = Conf::<&Type>::into_raw(ty.into());
unsafe {
Self::ref_from_raw(BNCreatePointerType(
- arch.as_ref().0,
- &t.into().into(),
+ arch.as_ref().handle,
+ &owned_raw_ty,
&mut is_const,
&mut is_volatile,
ReferenceType::PointerReferenceType,
@@ -1158,14 +874,15 @@ impl Type {
pub fn const_pointer<'a, A: Architecture, T: Into<Conf<&'a Type>>>(
arch: &A,
- t: T,
+ ty: T,
) -> Ref<Self> {
- let mut is_const = Conf::new(true, max_confidence()).into();
- let mut is_volatile = Conf::new(false, min_confidence()).into();
+ let mut is_const = Conf::new(true, MAX_CONFIDENCE).into();
+ let mut is_volatile = Conf::new(false, MIN_CONFIDENCE).into();
+ let owned_raw_ty = Conf::<&Type>::into_raw(ty.into());
unsafe {
Self::ref_from_raw(BNCreatePointerType(
- arch.as_ref().0,
- &t.into().into(),
+ arch.as_ref().handle,
+ &owned_raw_ty,
&mut is_const,
&mut is_volatile,
ReferenceType::PointerReferenceType,
@@ -1174,18 +891,19 @@ impl Type {
}
pub fn pointer_of_width<'a, T: Into<Conf<&'a Type>>>(
- t: T,
+ ty: T,
size: usize,
is_const: bool,
is_volatile: bool,
ref_type: Option<ReferenceType>,
) -> Ref<Self> {
- let mut is_const = Conf::new(is_const, max_confidence()).into();
- let mut is_volatile = Conf::new(is_volatile, max_confidence()).into();
+ let mut is_const = Conf::new(is_const, MAX_CONFIDENCE).into();
+ let mut is_volatile = Conf::new(is_volatile, MAX_CONFIDENCE).into();
+ let owned_raw_ty = Conf::<&Type>::into_raw(ty.into());
unsafe {
Self::ref_from_raw(BNCreatePointerTypeOfWidth(
size,
- &t.into().into(),
+ &owned_raw_ty,
&mut is_const,
&mut is_volatile,
ref_type.unwrap_or(ReferenceType::PointerReferenceType),
@@ -1195,17 +913,18 @@ impl Type {
pub fn pointer_with_options<'a, A: Architecture, T: Into<Conf<&'a Type>>>(
arch: &A,
- t: T,
+ ty: T,
is_const: bool,
is_volatile: bool,
ref_type: Option<ReferenceType>,
) -> Ref<Self> {
- let mut is_const = Conf::new(is_const, max_confidence()).into();
- let mut is_volatile = Conf::new(is_volatile, max_confidence()).into();
+ let mut is_const = Conf::new(is_const, MAX_CONFIDENCE).into();
+ let mut is_volatile = Conf::new(is_volatile, MAX_CONFIDENCE).into();
+ let owned_raw_ty = Conf::<&Type>::into_raw(ty.into());
unsafe {
Self::ref_from_raw(BNCreatePointerType(
- arch.as_ref().0,
- &t.into().into(),
+ arch.as_ref().handle,
+ &owned_raw_ty,
&mut is_const,
&mut is_volatile,
ref_type.unwrap_or(ReferenceType::PointerReferenceType),
@@ -1213,95 +932,54 @@ impl Type {
}
}
- pub fn generate_auto_demangled_type_id<S: BnStrCompatible>(name: S) -> BnString {
- let mut name = QualifiedName::from(name);
- unsafe { BnString::from_raw(BNGenerateAutoDemangledTypeId(&mut name.0)) }
+ pub fn generate_auto_demangled_type_id<T: Into<QualifiedName>>(name: T) -> BnString {
+ let mut raw_name = QualifiedName::into_raw(name.into());
+ let type_id = unsafe { BnString::from_raw(BNGenerateAutoDemangledTypeId(&mut raw_name)) };
+ QualifiedName::free_raw(raw_name);
+ type_id
}
}
-impl fmt::Display for Type {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+impl Display for Type {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", unsafe {
BnString::from_raw(BNGetTypeString(
self.handle,
- ptr::null_mut(),
+ std::ptr::null_mut(),
BNTokenEscapingType::NoTokenEscapingType,
))
})
}
}
-lazy_static! {
- static ref TYPE_DEBUG_BV: Mutex<Option<Ref<BinaryView>>> =
- Mutex::new(BinaryView::from_data(&FileMetadata::new(), &[]).ok());
-}
-
-impl fmt::Debug for Type {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- if let Ok(lock) = TYPE_DEBUG_BV.lock() {
- if let Some(bv) = &*lock {
- let container = unsafe { BNGetAnalysisTypeContainer(bv.handle) };
-
- let printer = if f.alternate() {
- unsafe { BNGetTypePrinterByName(c"_DebugTypePrinter".as_ptr()) }
- } else {
- unsafe { BNGetTypePrinterByName(c"CoreTypePrinter".as_ptr()) }
- };
- if printer.is_null() {
- return Err(fmt::Error);
- }
-
- let mut name = QualifiedName::from("");
-
- let mut lines: *mut BNTypeDefinitionLine = null_mut();
- let mut count: usize = 0;
-
- unsafe {
- BNGetTypePrinterTypeLines(
- printer,
- self.handle,
- container,
- &mut name.0,
- 64,
- false,
- BNTokenEscapingType::NoTokenEscapingType,
- &mut lines,
- &mut count,
- )
- };
- unsafe {
- BNFreeTypeContainer(container);
- }
-
- if lines.is_null() {
- return Err(fmt::Error);
- }
-
- let line_slice: &[BNTypeDefinitionLine] =
- unsafe { slice::from_raw_parts(lines, count) };
-
- for (i, line) in line_slice.iter().enumerate() {
- if i > 0 {
- writeln!(f)?;
- }
-
- let tokens: &[BNInstructionTextToken] =
- unsafe { slice::from_raw_parts(line.tokens, line.count) };
-
- for token in tokens {
- let text: *const c_char = token.text;
- let str = unsafe { CStr::from_ptr(text) };
- write!(f, "{}", str.to_string_lossy())?;
- }
- }
-
- unsafe {
- BNFreeTypeDefinitionLineList(lines, count);
- }
- return Ok(());
- }
- }
- Err(fmt::Error)
+impl Debug for Type {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ // You might be tempted to rip this atrocity out and make this more "sensible". READ BELOW!
+ // Type is a one-size fits all structure, these are actually its fields! If we wanted to
+ // omit some fields for different type classes what you really want to do is implement your
+ // own formatter. This is supposed to represent the structure entirely, it's not supposed to be pretty!
+ f.debug_struct("Type")
+ .field("type_class", &self.type_class())
+ .field("width", &self.width())
+ .field("alignment", &self.alignment())
+ .field("is_signed", &self.is_signed())
+ .field("is_const", &self.is_const())
+ .field("is_volatile", &self.is_volatile())
+ .field("is_floating_point", &self.is_floating_point())
+ .field("child_type", &self.child_type())
+ .field("calling_convention", &self.calling_convention())
+ .field("parameters", &self.parameters())
+ .field("has_variable_arguments", &self.has_variable_arguments())
+ .field("can_return", &self.can_return())
+ .field("pure", &self.pure())
+ .field("get_structure", &self.get_structure())
+ .field("get_enumeration", &self.get_enumeration())
+ .field("get_named_type_reference", &self.get_named_type_reference())
+ .field("count", &self.count())
+ .field("offset", &self.offset())
+ .field("stack_adjustment", &self.stack_adjustment())
+ .field("registered_name", &self.registered_name())
+ .finish()
}
}
@@ -1340,286 +1018,181 @@ impl ToOwned for Type {
}
}
-pub struct ComponentReferencedTypes;
-impl CoreArrayProvider for ComponentReferencedTypes {
+impl CoreArrayProvider for Type {
+ type Raw = *mut BNType;
+ type Context = ();
+ type Wrapped<'a> = &'a Self;
+}
+
+unsafe impl CoreArrayProviderInner for Type {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeTypeList(raw, count)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ // TODO: This is assuming &'a Type is &*mut BNType
+ std::mem::transmute(raw)
+ }
+}
+
+// TODO: Remove this struct, or make it not a ZST with a terrible array provider.
+/// ZST used only for `Array<ComponentReferencedType>`.
+pub struct ComponentReferencedType;
+
+impl CoreArrayProvider for ComponentReferencedType {
type Raw = *mut BNType;
type Context = ();
type Wrapped<'a> = &'a Type;
}
-unsafe impl CoreArrayProviderInner for ComponentReferencedTypes {
+unsafe impl CoreArrayProviderInner for ComponentReferencedType {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNComponentFreeReferencedTypes(raw, count)
}
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
- // SAFETY: BNType and Type are trasparent
- core::mem::transmute(raw)
+ // SAFETY: &*mut BNType == &Type (*mut BNType == Type)
+ std::mem::transmute(raw)
}
}
-///////////////////////
-// FunctionParameter
-
-#[derive(Clone, Debug)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct FunctionParameter {
- pub t: Conf<Ref<Type>>,
+ pub ty: Conf<Ref<Type>>,
pub name: String,
pub location: Option<Variable>,
}
impl FunctionParameter {
- pub fn new<T: Into<Conf<Ref<Type>>>>(t: T, name: String, location: Option<Variable>) -> Self {
- Self {
- t: t.into(),
- name,
- location,
- }
- }
-
- pub(crate) fn from_raw(member: BNFunctionParameter) -> Self {
- let name = if member.name.is_null() {
- if member.location.type_ == VariableSourceType::RegisterVariableSourceType {
- format!("reg_{}", member.location.storage)
- } else if member.location.type_ == VariableSourceType::StackVariableSourceType {
- format!("arg_{}", member.location.storage)
+ pub(crate) fn from_raw(value: &BNFunctionParameter) -> Self {
+ // TODO: I copied this from the original `from_raw` function.
+ // TODO: So this actually needs to be audited later.
+ let name = if value.name.is_null() {
+ if value.location.type_ == VariableSourceType::RegisterVariableSourceType {
+ format!("reg_{}", value.location.storage)
+ } else if value.location.type_ == VariableSourceType::StackVariableSourceType {
+ format!("arg_{}", value.location.storage)
} else {
String::new()
}
} else {
- unsafe { CStr::from_ptr(member.name) }
- .to_str()
- .unwrap()
- .to_owned()
+ raw_to_string(value.name as *const _).unwrap()
};
Self {
- t: Conf::new(
- unsafe { Type::ref_from_raw(BNNewTypeReference(member.type_)) },
- member.typeConfidence,
+ ty: Conf::new(
+ unsafe { Type::from_raw(value.type_).to_owned() },
+ value.typeConfidence,
),
name,
- location: if member.defaultLocation {
- None
- } else {
- Some(unsafe { Variable::from_raw(member.location) })
+ location: match value.defaultLocation {
+ false => Some(Variable::from(value.location)),
+ true => None,
},
}
}
-}
-
-//////////////
-// Variable
-#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
-pub struct Variable {
- pub t: VariableSourceType,
- pub index: u32,
- pub storage: i64,
-}
-
-impl Variable {
- pub fn new(t: VariableSourceType, index: u32, storage: i64) -> Self {
- Self { t, index, storage }
+ pub(crate) fn from_owned_raw(value: BNFunctionParameter) -> Self {
+ let owned = Self::from_raw(&value);
+ Self::free_raw(value);
+ owned
}
- pub(crate) unsafe fn from_raw(var: BNVariable) -> Self {
- Self {
- t: var.type_,
- index: var.index,
- storage: var.storage,
+ pub(crate) fn into_raw(value: Self) -> BNFunctionParameter {
+ let bn_name = BnString::new(value.name);
+ BNFunctionParameter {
+ name: BnString::into_raw(bn_name),
+ type_: unsafe { Ref::into_raw(value.ty.contents) }.handle,
+ typeConfidence: value.ty.confidence,
+ defaultLocation: value.location.is_none(),
+ location: value.location.map(Into::into).unwrap_or_default(),
}
}
- pub(crate) unsafe fn from_identifier(var: u64) -> Self {
- Self::from_raw(unsafe { BNFromVariableIdentifier(var) })
- }
- pub(crate) fn raw(&self) -> BNVariable {
- BNVariable {
- type_: self.t,
- index: self.index,
- storage: self.storage,
- }
+ pub(crate) fn free_raw(value: BNFunctionParameter) {
+ let _ = unsafe { BnString::from_raw(value.name) };
+ let _ = unsafe { Type::ref_from_raw(value.type_) };
}
-}
-
-impl CoreArrayProvider for Variable {
- type Raw = BNVariable;
- type Context = ();
- type Wrapped<'a> = Self;
-}
-unsafe impl CoreArrayProviderInner for Variable {
- unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
- BNFreeVariableList(raw)
- }
- unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
- Variable::from_raw(*raw)
+ pub fn new<T: Into<Conf<Ref<Type>>>>(ty: T, name: String, location: Option<Variable>) -> Self {
+ Self {
+ ty: ty.into(),
+ name,
+ location,
+ }
}
}
+// TODO: We need to delete this...
// Name, Variable and Type
impl CoreArrayProvider for (&str, Variable, &Type) {
type Raw = BNVariableNameAndType;
type Context = ();
- type Wrapped<'a> = (&'a str, Variable, &'a Type) where Self: 'a;
+ type Wrapped<'a>
+ = (&'a str, Variable, &'a Type)
+ where
+ Self: 'a;
}
+// TODO: This needs to go!
unsafe impl CoreArrayProviderInner for (&str, Variable, &Type) {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeVariableNameAndTypeList(raw, count)
}
+
unsafe fn wrap_raw<'a>(
raw: &'a Self::Raw,
_context: &'a Self::Context,
) -> (&'a str, Variable, &'a Type) {
let name = CStr::from_ptr(raw.name).to_str().unwrap();
- let var = Variable::from_raw(raw.var);
- let var_type = core::mem::transmute(&raw.type_);
+ let var = Variable::from(raw.var);
+ let var_type = &*(raw.type_ as *mut Type);
(name, var, var_type)
}
}
-//////////////
-// SSAVariable
-
-#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
-pub struct SSAVariable {
- pub variable: Variable,
- pub version: usize,
-}
-
-impl SSAVariable {
- pub fn new(variable: Variable, version: usize) -> Self {
- Self { variable, version }
- }
-}
-
-impl CoreArrayProvider for SSAVariable {
- type Raw = usize;
- type Context = Variable;
- type Wrapped<'a> = Self;
-}
-
-unsafe impl CoreArrayProviderInner for SSAVariable {
- 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> {
- SSAVariable::new(*context, *raw)
- }
-}
-
-impl CoreArrayProvider for Array<SSAVariable> {
- type Raw = BNVariable;
- type Context = Ref<MediumLevelILFunction>;
- type Wrapped<'a> = Self;
-}
-
-unsafe impl CoreArrayProviderInner for Array<SSAVariable> {
- unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
- BNFreeVariableList(raw)
- }
-
- unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
- let mut count = 0;
- let versions =
- unsafe { BNGetMediumLevelILVariableSSAVersions(context.handle, raw, &mut count) };
- Array::new(versions, count, Variable::from_raw(*raw))
- }
-}
-
-///////////////
-// NamedVariable
-
-#[derive(Clone, Debug, Hash, Eq, PartialEq)]
-pub struct NamedTypedVariable {
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
+pub struct EnumerationMember {
pub name: String,
- pub ty: Conf<Ref<Type>>,
- pub var: Variable,
- pub auto_defined: bool,
+ /// The associated constant value for the member.
+ pub value: u64,
+ /// Whether this is the default member for the associated [`Enumeration`].
+ pub default: bool,
}
-impl NamedTypedVariable {
- pub fn new(var: Variable, name: String, ty: Conf<Ref<Type>>, auto_defined: bool) -> Self {
- Self {
- name,
- ty,
- var,
- auto_defined,
- }
- }
-
- pub(crate) unsafe fn from_raw(var: &BNVariableNameAndType) -> Self {
+impl EnumerationMember {
+ pub(crate) fn from_raw(value: &BNEnumerationMember) -> Self {
Self {
- var: Variable::from_raw(var.var),
- auto_defined: var.autoDefined,
- name: CStr::from_ptr(var.name).to_str().unwrap().to_string(),
- ty: Conf::new(Type::ref_from_raw(var.type_), var.typeConfidence),
+ name: raw_to_string(value.name).unwrap(),
+ value: value.value,
+ default: value.isDefault,
}
}
- pub fn name(&self) -> &str {
- &self.name
- }
-
- pub fn var(&self) -> Variable {
- self.var
- }
-
- pub fn auto_defined(&self) -> bool {
- self.auto_defined
+ pub(crate) fn from_owned_raw(value: BNEnumerationMember) -> Self {
+ let owned = Self::from_raw(&value);
+ Self::free_raw(value);
+ owned
}
- pub fn type_confidence(&self) -> u8 {
- self.ty.confidence
- }
-
- pub fn var_type(&self) -> Ref<Type> {
- self.ty.contents.clone()
+ pub(crate) fn into_raw(value: Self) -> BNEnumerationMember {
+ let bn_name = BnString::new(value.name);
+ BNEnumerationMember {
+ name: BnString::into_raw(bn_name),
+ value: value.value,
+ isDefault: value.default,
+ }
}
-}
-
-impl CoreArrayProvider for NamedTypedVariable {
- type Raw = BNVariableNameAndType;
- type Context = ();
- type Wrapped<'a> = Guard<'a, NamedTypedVariable>;
-}
-unsafe impl CoreArrayProviderInner for NamedTypedVariable {
- unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
- BNFreeVariableNameAndTypeList(raw, count)
+ pub(crate) fn free_raw(value: BNEnumerationMember) {
+ let _ = unsafe { BnString::from_raw(value.name) };
}
- unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
- unsafe { Guard::new(NamedTypedVariable::from_raw(raw), raw) }
- }
-}
-////////////////////////
-// EnumerationBuilder
-
-#[derive(Debug, Clone)]
-pub struct EnumerationMember {
- pub name: String,
- pub value: u64,
- pub is_default: bool,
-}
-
-impl EnumerationMember {
- pub fn new(name: String, value: u64, is_default: bool) -> Self {
+ pub fn new(name: String, value: u64, default: bool) -> Self {
Self {
name,
value,
- is_default,
- }
- }
-
- pub(crate) unsafe fn from_raw(member: BNEnumerationMember) -> Self {
- Self {
- name: raw_to_string(member.name).unwrap(),
- value: member.value,
- is_default: member.isDefault,
+ default,
}
}
}
@@ -1679,16 +1252,15 @@ impl EnumerationBuilder {
pub fn members(&self) -> Vec<EnumerationMember> {
unsafe {
let mut count = 0;
- let members_raw = BNGetEnumerationBuilderMembers(self.handle, &mut count);
- let members: &[BNEnumerationMember] = slice::from_raw_parts(members_raw, count);
-
- let result = (0..count)
- .map(|i| EnumerationMember::from_raw(members[i]))
+ let members_raw_ptr = BNGetEnumerationBuilderMembers(self.handle, &mut count);
+ let members_raw: &[BNEnumerationMember] =
+ std::slice::from_raw_parts(members_raw_ptr, count);
+ let members = members_raw
+ .iter()
+ .map(EnumerationMember::from_raw)
.collect();
-
- BNFreeEnumerationMemberList(members_raw, count);
-
- result
+ BNFreeEnumerationMemberList(members_raw_ptr, count);
+ members
}
}
}
@@ -1715,9 +1287,6 @@ impl Drop for EnumerationBuilder {
}
}
-/////////////////
-// Enumeration
-
#[derive(PartialEq, Eq, Hash)]
pub struct Enumeration {
pub(crate) handle: *mut BNEnumeration,
@@ -1736,20 +1305,28 @@ impl Enumeration {
pub fn members(&self) -> Vec<EnumerationMember> {
unsafe {
let mut count = 0;
- let members_raw = BNGetEnumerationMembers(self.handle, &mut count);
- let members: &[BNEnumerationMember] = slice::from_raw_parts(members_raw, count);
-
- let result = (0..count)
- .map(|i| EnumerationMember::from_raw(members[i]))
+ let members_raw_ptr = BNGetEnumerationMembers(self.handle, &mut count);
+ debug_assert!(!members_raw_ptr.is_null());
+ let members_raw: &[BNEnumerationMember] =
+ std::slice::from_raw_parts(members_raw_ptr, count);
+ let members = members_raw
+ .iter()
+ .map(EnumerationMember::from_raw)
.collect();
-
- BNFreeEnumerationMemberList(members_raw, count);
-
- result
+ BNFreeEnumerationMemberList(members_raw_ptr, count);
+ members
}
}
}
+impl Debug for Enumeration {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("Enumeration")
+ .field("members", &self.members())
+ .finish()
+ }
+}
+
unsafe impl RefCountable for Enumeration {
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
Self::ref_from_raw(BNNewEnumerationReference(handle.handle))
@@ -1768,11 +1345,6 @@ impl ToOwned for Enumeration {
}
}
-//////////////////////
-// StructureBuilder
-
-pub type StructureType = BNStructureVariant;
-
#[derive(PartialEq, Eq, Hash)]
pub struct StructureBuilder {
pub(crate) handle: *mut BNStructureBuilder,
@@ -1780,20 +1352,47 @@ pub struct StructureBuilder {
/// ```no_run
/// // Includes
-/// # use binaryninja::binaryview::BinaryViewExt;
-/// use binaryninja::types::{Structure, StructureBuilder, Type, MemberAccess, MemberScope};
+/// # use binaryninja::binary_view::BinaryViewExt;
+/// use binaryninja::types::{MemberAccess, MemberScope, Structure, StructureBuilder, Type};
///
-/// // Define struct, set size (in bytes)
-/// let mut my_custom_struct = StructureBuilder::new();
-/// let field_1 = Type::named_int(5, false, "my_weird_int_type");
-/// let field_2 = Type::int(4, false);
-/// let field_3 = Type::int(8, false);
+/// // Types to use in the members
+/// let field_1_ty = Type::named_int(5, false, "my_weird_int_type");
+/// let field_2_ty = Type::int(4, false);
+/// let field_3_ty = Type::int(8, false);
///
/// // Assign those fields
-/// my_custom_struct.insert(&field_1, "field_1", 0, false, MemberAccess::PublicAccess, MemberScope::NoScope);
-/// my_custom_struct.insert(&field_2, "field_2", 5, false, MemberAccess::PublicAccess, MemberScope::NoScope);
-/// my_custom_struct.insert(&field_3, "field_3", 9, false, MemberAccess::PublicAccess, MemberScope::NoScope);
-/// my_custom_struct.append(&field_1, "field_4", MemberAccess::PublicAccess, MemberScope::NoScope);
+/// let mut my_custom_struct = StructureBuilder::new();
+/// my_custom_struct
+/// .insert(
+/// &field_1_ty,
+/// "field_1",
+/// 0,
+/// false,
+/// MemberAccess::PublicAccess,
+/// MemberScope::NoScope,
+/// )
+/// .insert(
+/// &field_2_ty,
+/// "field_2",
+/// 5,
+/// false,
+/// MemberAccess::PublicAccess,
+/// MemberScope::NoScope,
+/// )
+/// .insert(
+/// &field_3_ty,
+/// "field_3",
+/// 9,
+/// false,
+/// MemberAccess::PublicAccess,
+/// MemberScope::NoScope,
+/// )
+/// .append(
+/// &field_1_ty,
+/// "field_4",
+/// MemberAccess::PublicAccess,
+/// MemberScope::NoScope,
+/// );
///
/// // Convert structure to type
/// let my_custom_structure_type = Type::structure(&my_custom_struct.finalize());
@@ -1814,99 +1413,98 @@ impl StructureBuilder {
Self { handle }
}
- // Chainable terminal
- pub fn finalize(&self) -> Ref<Structure> {
- unsafe { Structure::ref_from_raw(BNFinalizeStructureBuilder(self.handle)) }
+ // TODO: Document the width adjustment with alignment.
+ pub fn finalize(&mut self) -> Ref<Structure> {
+ let raw_struct_ptr = unsafe { BNFinalizeStructureBuilder(self.handle) };
+ unsafe { Structure::ref_from_raw(raw_struct_ptr) }
}
- // Chainable builders/setters
-
- pub fn set_width(&self, width: u64) -> &Self {
+ /// Sets the width of the [`StructureBuilder`] to the new width.
+ ///
+ /// This will remove all previously inserted members outside the new width. This is done by computing
+ /// the member access range (member offset + member width) and if it is larger than the new width
+ /// it will be removed.
+ pub fn width(&mut self, width: u64) -> &mut Self {
unsafe {
BNSetStructureBuilderWidth(self.handle, width);
}
-
self
}
- pub fn set_alignment(&self, alignment: usize) -> &Self {
+ pub fn alignment(&mut self, alignment: usize) -> &mut Self {
unsafe {
BNSetStructureBuilderAlignment(self.handle, alignment);
}
-
self
}
- pub fn set_packed(&self, packed: bool) -> &Self {
+ /// Sets whether the [`StructureBuilder`] is packed.
+ ///
+ /// If set the alignment of the structure will be `1`. You do not need to set the alignment to `1`.
+ pub fn packed(&mut self, packed: bool) -> &mut Self {
unsafe {
BNSetStructureBuilderPacked(self.handle, packed);
}
-
self
}
- pub fn set_structure_type(&self, t: StructureType) -> &Self {
+ pub fn structure_type(&mut self, t: StructureType) -> &mut Self {
unsafe { BNSetStructureBuilderType(self.handle, t) };
self
}
- pub fn set_pointer_offset(&self, offset: i64) -> &Self {
+ pub fn pointer_offset(&mut self, offset: i64) -> &mut Self {
unsafe { BNSetStructureBuilderPointerOffset(self.handle, offset) };
self
}
- pub fn set_propagates_data_var_refs(&self, does: bool) -> &Self {
- unsafe { BNSetStructureBuilderPropagatesDataVariableReferences(self.handle, does) };
+ pub fn propagates_data_var_refs(&mut self, propagates: bool) -> &mut Self {
+ unsafe { BNSetStructureBuilderPropagatesDataVariableReferences(self.handle, propagates) };
self
}
- pub fn set_base_structures(&self, bases: Vec<BaseStructure>) -> &Self {
- let mut bases_api = vec![];
- for base in &bases {
- bases_api.push(BNBaseStructure {
- type_: base.ty.handle,
- offset: base.offset,
- width: base.width,
- });
- }
-
+ pub fn base_structures(&mut self, bases: &[BaseStructure]) -> &mut Self {
+ let raw_base_structs: Vec<BNBaseStructure> =
+ bases.iter().map(BaseStructure::into_owned_raw).collect();
unsafe {
BNSetBaseStructuresForStructureBuilder(
self.handle,
- bases_api.as_mut_ptr(),
- bases_api.len(),
+ raw_base_structs.as_ptr() as *mut _,
+ raw_base_structs.len(),
)
};
-
self
}
pub fn append<'a, S: BnStrCompatible, T: Into<Conf<&'a Type>>>(
- &self,
- t: T,
+ &mut self,
+ ty: T,
name: S,
access: MemberAccess,
scope: MemberScope,
- ) -> &Self {
+ ) -> &mut Self {
let name = name.into_bytes_with_nul();
+ let owned_raw_ty = Conf::<&Type>::into_raw(ty.into());
unsafe {
BNAddStructureBuilderMember(
self.handle,
- &t.into().into(),
+ &owned_raw_ty,
name.as_ref().as_ptr() as _,
access,
scope,
);
}
-
self
}
- pub fn insert_member(&self, member: &StructureMember, overwrite_existing: bool) -> &Self {
- let ty = member.ty.clone();
+ pub fn insert_member(
+ &mut self,
+ member: StructureMember,
+ overwrite_existing: bool,
+ ) -> &mut Self {
self.insert(
- ty.as_ref(),
- member.name.clone(),
+ &member.ty,
+ member.name,
member.offset,
overwrite_existing,
member.access,
@@ -1916,19 +1514,20 @@ impl StructureBuilder {
}
pub fn insert<'a, S: BnStrCompatible, T: Into<Conf<&'a Type>>>(
- &self,
- t: T,
+ &mut self,
+ ty: T,
name: S,
offset: u64,
overwrite_existing: bool,
access: MemberAccess,
scope: MemberScope,
- ) -> &Self {
+ ) -> &mut Self {
let name = name.into_bytes_with_nul();
+ let owned_raw_ty = Conf::<&Type>::into_raw(ty.into());
unsafe {
BNAddStructureBuilderMemberAtOffset(
self.handle,
- &t.into().into(),
+ &owned_raw_ty,
name.as_ref().as_ptr() as _,
offset,
overwrite_existing,
@@ -1936,118 +1535,33 @@ impl StructureBuilder {
scope,
);
}
-
self
}
- pub fn with_members<'a, S: BnStrCompatible, T: Into<Conf<&'a Type>>>(
- &self,
- members: impl IntoIterator<Item = (T, S)>,
- ) -> &Self {
- for (t, name) in members {
- self.append(t, name, MemberAccess::NoAccess, MemberScope::NoScope);
+ pub fn replace<'a, S: BnStrCompatible, T: Into<Conf<&'a Type>>>(
+ &mut self,
+ index: usize,
+ ty: T,
+ name: S,
+ overwrite_existing: bool,
+ ) -> &mut Self {
+ let name = name.into_bytes_with_nul();
+ let owned_raw_ty = Conf::<&Type>::into_raw(ty.into());
+ unsafe {
+ BNReplaceStructureBuilderMember(
+ self.handle,
+ index,
+ &owned_raw_ty,
+ name.as_ref().as_ptr() as _,
+ overwrite_existing,
+ )
}
self
}
- // Getters
-
- pub fn width(&self) -> u64 {
- unsafe { BNGetStructureBuilderWidth(self.handle) }
- }
-
- pub fn alignment(&self) -> usize {
- unsafe { BNGetStructureBuilderAlignment(self.handle) }
- }
-
- pub fn packed(&self) -> bool {
- unsafe { BNIsStructureBuilderPacked(self.handle) }
- }
-
- pub fn structure_type(&self) -> StructureType {
- unsafe { BNGetStructureBuilderType(self.handle) }
- }
-
- pub fn pointer_offset(&self) -> i64 {
- unsafe { BNGetStructureBuilderPointerOffset(self.handle) }
- }
-
- pub fn propagates_data_var_refs(&self) -> bool {
- unsafe { BNStructureBuilderPropagatesDataVariableReferences(self.handle) }
- }
-
- pub fn base_structures(&self) -> Result<Vec<BaseStructure>> {
- let mut count = 0usize;
- let bases = unsafe { BNGetBaseStructuresForStructureBuilder(self.handle, &mut count) };
- if bases.is_null() {
- Err(())
- } else {
- let bases_slice = unsafe { slice::from_raw_parts_mut(bases, count) };
-
- let result = bases_slice
- .iter()
- .map(|base| unsafe { BaseStructure::from_raw(*base) })
- .collect::<Vec<_>>();
-
- unsafe {
- BNFreeBaseStructureList(bases, count);
- }
-
- Ok(result)
- }
- }
-
- pub fn members(&self) -> Array<StructureMember> {
- let mut count = 0;
- let members_raw = unsafe { BNGetStructureBuilderMembers(self.handle, &mut count) };
- unsafe { Array::new(members_raw, count, ()) }
- }
-
- pub fn index_by_name(&self, name: &str) -> Option<usize> {
- self.members().iter().position(|member| member.name == name)
- }
-
- pub fn index_by_offset(&self, offset: u64) -> Option<usize> {
- self.members()
- .iter()
- .position(|member| member.offset == offset)
- }
-
- // Setters
-
- pub fn clear_members(&self) {
- let len = self.members().len();
- for idx in (0..len).rev() {
- self.remove(idx)
- }
- }
-
- pub fn add_members<'a>(&self, members: impl IntoIterator<Item = &'a StructureMember>) {
- for member in members {
- self.append(&member.ty, &member.name, member.access, member.scope);
- }
- }
-
- pub fn set_members<'a>(&self, members: impl IntoIterator<Item = &'a StructureMember>) {
- self.clear_members();
- self.add_members(members);
- }
-
- pub fn remove(&self, index: usize) {
- unsafe { BNRemoveStructureBuilderMember(self.handle, index) }
- }
-
- pub fn replace(&self, index: usize, type_: Conf<&Type>, name: &str, overwrite: bool) {
- let name = name.into_bytes_with_nul();
- let name_ptr = name.as_ptr() as *const _;
-
- let raw_type_ = BNTypeWithConfidence {
- type_: type_.contents as *const Type as *mut _,
- confidence: type_.confidence,
- };
- unsafe {
- BNReplaceStructureBuilderMember(self.handle, index, &raw_type_, name_ptr, overwrite)
- }
+ pub fn remove(&mut self, index: usize) -> &mut Self {
+ unsafe { BNRemoveStructureBuilderMember(self.handle, index) };
+ self
}
}
@@ -2059,20 +1573,14 @@ impl From<&Structure> for StructureBuilder {
impl From<Vec<StructureMember>> for StructureBuilder {
fn from(members: Vec<StructureMember>) -> StructureBuilder {
- let builder = StructureBuilder::new();
- for m in members {
- builder.insert_member(&m, false);
+ let mut builder = StructureBuilder::new();
+ for member in members {
+ builder.insert_member(member, false);
}
builder
}
}
-impl Debug for StructureBuilder {
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- write!(f, "StructureBuilder {{ ... }}")
- }
-}
-
impl Drop for StructureBuilder {
fn drop(&mut self) {
unsafe { BNFreeStructureBuilder(self.handle) };
@@ -2085,20 +1593,12 @@ impl Default for StructureBuilder {
}
}
-///////////////
-// Structure
-
#[derive(PartialEq, Eq, Hash)]
pub struct Structure {
pub(crate) handle: *mut BNStructure,
}
impl Structure {
- unsafe fn from_raw(handle: *mut BNStructure) -> Self {
- debug_assert!(!handle.is_null());
- Self { handle }
- }
-
pub(crate) unsafe fn ref_from_raw(handle: *mut BNStructure) -> Ref<Self> {
debug_assert!(!handle.is_null());
Ref::new(Self { handle })
@@ -2116,65 +1616,46 @@ impl Structure {
unsafe { BNGetStructureType(self.handle) }
}
- pub fn members(&self) -> Result<Vec<StructureMember>> {
+ pub fn members(&self) -> Vec<StructureMember> {
unsafe {
let mut count = 0;
- let members_raw: *mut BNStructureMember =
+ let members_raw_ptr: *mut BNStructureMember =
BNGetStructureMembers(self.handle, &mut count);
- if members_raw.is_null() {
- return Err(());
- }
- let members = slice::from_raw_parts(members_raw, count);
-
- let result = (0..count)
- .map(|i| StructureMember::from_raw(members[i]))
- .collect();
-
- BNFreeStructureMemberList(members_raw, count);
-
- Ok(result)
+ debug_assert!(!members_raw_ptr.is_null());
+ let members_raw = std::slice::from_raw_parts(members_raw_ptr, count);
+ let members = members_raw.iter().map(StructureMember::from_raw).collect();
+ BNFreeStructureMemberList(members_raw_ptr, count);
+ members
}
}
- pub fn base_structures(&self) -> Result<Vec<BaseStructure>> {
- let mut count = 0usize;
- let bases = unsafe { BNGetBaseStructuresForStructure(self.handle, &mut count) };
- if bases.is_null() {
- Err(())
- } else {
- let bases_slice = unsafe { slice::from_raw_parts_mut(bases, count) };
-
- let result = bases_slice
- .iter()
- .map(|base| unsafe { BaseStructure::from_raw(*base) })
- .collect::<Vec<_>>();
-
- unsafe {
- BNFreeBaseStructureList(bases, count);
- }
-
- Ok(result)
- }
+ pub fn base_structures(&self) -> Vec<BaseStructure> {
+ let mut count = 0;
+ let bases_raw_ptr = unsafe { BNGetBaseStructuresForStructure(self.handle, &mut count) };
+ debug_assert!(!bases_raw_ptr.is_null());
+ let bases_raw = unsafe { std::slice::from_raw_parts(bases_raw_ptr, count) };
+ let bases = bases_raw.iter().map(BaseStructure::from_raw).collect();
+ unsafe { BNFreeBaseStructureList(bases_raw_ptr, count) };
+ bases
}
// TODO : The other methods in the python version (alignment, packed, type, members, remove, replace, etc)
}
impl Debug for Structure {
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- write!(f, "Structure {{")?;
- if let Ok(members) = self.members() {
- for member in members {
- write!(f, " {:?}", member)?;
- }
- }
- write!(f, "}}")
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ f.debug_struct("Structure")
+ .field("width", &self.width())
+ .field("structure_type", &self.structure_type())
+ .field("base_structures", &self.base_structures())
+ .field("members", &self.members())
+ .finish()
}
}
unsafe impl RefCountable for Structure {
unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
- Ref::new(Self::from_raw(BNNewStructureReference(handle.handle)))
+ Self::ref_from_raw(BNNewStructureReference(handle.handle))
}
unsafe fn dec_ref(handle: &Self) {
@@ -2190,9 +1671,10 @@ impl ToOwned for Structure {
}
}
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct StructureMember {
pub ty: Conf<Ref<Type>>,
+ // TODO: Shouldnt this be a QualifiedName? The ffi says no...
pub name: String,
pub offset: u64,
pub access: MemberAccess,
@@ -2200,6 +1682,43 @@ pub struct StructureMember {
}
impl StructureMember {
+ pub(crate) fn from_raw(value: &BNStructureMember) -> Self {
+ Self {
+ ty: Conf::new(
+ unsafe { Type::from_raw(value.type_) }.to_owned(),
+ value.typeConfidence,
+ ),
+ // TODO: I dislike using this function here.
+ name: raw_to_string(value.name as *mut _).unwrap(),
+ offset: value.offset,
+ access: value.access,
+ scope: value.scope,
+ }
+ }
+
+ pub(crate) fn from_owned_raw(value: BNStructureMember) -> Self {
+ let owned = Self::from_raw(&value);
+ Self::free_raw(value);
+ owned
+ }
+
+ pub(crate) fn into_raw(value: Self) -> BNStructureMember {
+ let bn_name = BnString::new(value.name);
+ BNStructureMember {
+ type_: unsafe { Ref::into_raw(value.ty.contents) }.handle,
+ name: BnString::into_raw(bn_name),
+ offset: value.offset,
+ typeConfidence: value.ty.confidence,
+ access: value.access,
+ scope: value.scope,
+ }
+ }
+
+ pub(crate) fn free_raw(value: BNStructureMember) {
+ let _ = unsafe { Type::ref_from_raw(value.type_) };
+ let _ = unsafe { BnString::from_raw(value.name) };
+ }
+
pub fn new(
ty: Conf<Ref<Type>>,
name: String,
@@ -2215,37 +1734,25 @@ impl StructureMember {
scope,
}
}
-
- pub(crate) unsafe fn from_raw(handle: BNStructureMember) -> Self {
- Self {
- ty: Conf::new(
- RefCountable::inc_ref(&Type::from_raw(handle.type_)),
- handle.typeConfidence,
- ),
- name: CStr::from_ptr(handle.name).to_string_lossy().to_string(),
- offset: handle.offset,
- access: handle.access,
- scope: handle.scope,
- }
- }
}
impl CoreArrayProvider for StructureMember {
type Raw = BNStructureMember;
type Context = ();
- type Wrapped<'a> = Guard<'a, StructureMember>;
+ type Wrapped<'a> = Self;
}
unsafe impl CoreArrayProviderInner for StructureMember {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
BNFreeStructureMemberList(raw, count)
}
+
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
- Guard::new(StructureMember::from_raw(*raw), &())
+ Self::from_raw(raw)
}
}
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct InheritedStructureMember {
pub base: Ref<NamedTypeReference>,
pub base_offset: u64,
@@ -2267,18 +1774,9 @@ impl InheritedStructureMember {
member_index,
}
}
-
- // pub(crate) unsafe fn from_raw(handle: BNInheritedStructureMember) -> Self {
- // Self {
- // base: RefCountable::inc_ref(&NamedTypeReference::from_raw(handle.base)),
- // base_offset: handle.baseOffset,
- // member: StructureMember::from_raw(handle.member),
- // member_index: handle.memberIndex,
- // }
- // }
}
-#[derive(Debug, Clone)]
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
pub struct BaseStructure {
pub ty: Ref<NamedTypeReference>,
pub offset: u64,
@@ -2286,21 +1784,44 @@ pub struct BaseStructure {
}
impl BaseStructure {
- pub fn new(ty: Ref<NamedTypeReference>, offset: u64, width: u64) -> Self {
- Self { ty, offset, width }
+ pub(crate) fn from_raw(value: &BNBaseStructure) -> Self {
+ Self {
+ ty: unsafe { NamedTypeReference::from_raw(value.type_) }.to_owned(),
+ offset: value.offset,
+ width: value.width,
+ }
}
- pub(crate) unsafe fn from_raw(handle: BNBaseStructure) -> Self {
- Self {
- ty: RefCountable::inc_ref(&NamedTypeReference::from_raw(handle.type_)),
- offset: handle.offset,
- width: handle.width,
+ pub(crate) fn from_owned_raw(value: BNBaseStructure) -> Self {
+ let owned = Self::from_raw(&value);
+ Self::free_raw(value);
+ owned
+ }
+
+ pub(crate) fn into_raw(value: Self) -> BNBaseStructure {
+ BNBaseStructure {
+ type_: unsafe { Ref::into_raw(value.ty) }.handle,
+ offset: value.offset,
+ width: value.width,
+ }
+ }
+
+ pub(crate) fn into_owned_raw(value: &Self) -> BNBaseStructure {
+ BNBaseStructure {
+ type_: value.ty.handle,
+ offset: value.offset,
+ width: value.width,
}
}
-}
-////////////////////////
-// NamedTypeReference
+ pub(crate) fn free_raw(value: BNBaseStructure) {
+ let _ = unsafe { NamedTypeReference::ref_from_raw(value.type_) };
+ }
+
+ pub fn new(ty: Ref<NamedTypeReference>, offset: u64, width: u64) -> Self {
+ Self { ty, offset, width }
+ }
+}
#[derive(PartialEq, Eq, Hash)]
pub struct NamedTypeReference {
@@ -2310,7 +1831,6 @@ pub struct NamedTypeReference {
impl NamedTypeReference {
pub(crate) unsafe fn from_raw(handle: *mut BNNamedTypeReference) -> Self {
debug_assert!(!handle.is_null());
-
Self { handle }
}
@@ -2324,10 +1844,17 @@ impl NamedTypeReference {
/// You should not assign type ids yourself, that is the responsibility of the BinaryView
/// implementation after your types have been added. Just make sure the names match up and
/// the core will do the id stuff for you.
- pub fn new(type_class: NamedTypeReferenceClass, mut name: QualifiedName) -> Ref<Self> {
- unsafe {
- Self::ref_from_raw(BNCreateNamedType(type_class, ptr::null() as *const _, &mut name.0))
- }
+ pub fn new<T: Into<QualifiedName>>(type_class: NamedTypeReferenceClass, name: T) -> Ref<Self> {
+ let mut raw_name = QualifiedName::into_raw(name.into());
+ let result = unsafe {
+ Self::ref_from_raw(BNCreateNamedType(
+ type_class,
+ std::ptr::null(),
+ &mut raw_name,
+ ))
+ };
+ QualifiedName::free_raw(raw_name);
+ result
}
/// Create an NTR to a type with an existing type id, which generally means it came directly
@@ -2335,21 +1862,27 @@ impl NamedTypeReference {
/// You should not assign type ids yourself: if you use this to reference a type you are going
/// to create but have not yet created, you may run into problems when giving your types to
/// a BinaryView.
- pub fn new_with_id<S: BnStrCompatible>(
+ pub fn new_with_id<T: Into<QualifiedName>, S: BnStrCompatible>(
type_class: NamedTypeReferenceClass,
type_id: S,
- mut name: QualifiedName,
+ name: T,
) -> Ref<Self> {
let type_id = type_id.into_bytes_with_nul();
-
- unsafe {
- Self::ref_from_raw(BNCreateNamedType(type_class, type_id.as_ref().as_ptr() as _, &mut name.0))
- }
+ let mut raw_name = QualifiedName::into_raw(name.into());
+ let result = unsafe {
+ Self::ref_from_raw(BNCreateNamedType(
+ type_class,
+ type_id.as_ref().as_ptr() as _,
+ &mut raw_name,
+ ))
+ };
+ QualifiedName::free_raw(raw_name);
+ result
}
pub fn name(&self) -> QualifiedName {
- let named_ref: BNQualifiedName = unsafe { BNGetTypeReferenceName(self.handle) };
- QualifiedName(named_ref)
+ let raw_name = unsafe { BNGetTypeReferenceName(self.handle) };
+ QualifiedName::from_owned_raw(raw_name)
}
pub fn id(&self) -> BnString {
@@ -2361,28 +1894,26 @@ impl NamedTypeReference {
}
fn target_helper(&self, bv: &BinaryView, visited: &mut HashSet<BnString>) -> Option<Ref<Type>> {
- // TODO : This is a clippy bug (#10088, I think); remove after we upgrade past 2022-12-12
- #[allow(clippy::manual_filter)]
- if let Some(t) = bv.get_type_by_id(self.id()) {
- if t.type_class() != TypeClass::NamedTypeReferenceClass {
- Some(t)
- } else {
- let t = t.get_named_type_reference().unwrap();
- if visited.contains(&t.id()) {
- error!("Can't get target for recursively defined type!");
- None
- } else {
- visited.insert(t.id());
- t.target_helper(bv, visited)
+ let ty = bv.type_by_id(self.id())?;
+ match ty.type_class() {
+ TypeClass::NamedTypeReferenceClass => {
+ // Recurse into the NTR type until we get the target type.
+ let ntr = ty.get_named_type_reference().unwrap();
+ match visited.insert(ntr.id()) {
+ true => ntr.target_helper(bv, visited),
+ false => {
+ log::error!("Can't get target for recursively defined type!");
+ None
+ }
}
}
- } else {
- None
+ // Found target type
+ _ => Some(ty),
}
}
+ /// Type referenced by this [`NamedTypeReference`].
pub fn target(&self, bv: &BinaryView) -> Option<Ref<Type>> {
- //! Returns the type referenced by this named type reference
self.target_helper(bv, &mut HashSet::new())
}
}
@@ -2406,1375 +1937,420 @@ unsafe impl RefCountable for NamedTypeReference {
}
impl Debug for NamedTypeReference {
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{} (id: {})", self.name(), self.id())
}
}
-///////////////////
-// QualifiedName
-
-#[repr(transparent)]
-pub struct QualifiedName(pub(crate) BNQualifiedName);
+// TODO: Document usage, specifically how to make a qualified name and why it exists.
+#[derive(Default, Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
+pub struct QualifiedName {
+ // TODO: Make this Option<String> where default is "::".
+ pub separator: String,
+ pub items: Vec<String>,
+}
impl QualifiedName {
- // TODO : I think this is bad
- pub fn string(&self) -> String {
- unsafe {
- slice::from_raw_parts(self.0.name, self.0.nameCount)
- .iter()
- .map(|c| CStr::from_ptr(*c).to_string_lossy())
- .collect::<Vec<_>>()
- .join("::")
- }
+ pub(crate) fn from_raw(value: &BNQualifiedName) -> Self {
+ // TODO: This could be improved...
+ let raw_names = unsafe { std::slice::from_raw_parts(value.name, value.nameCount) };
+ let items = raw_names
+ .iter()
+ .filter_map(|&raw_name| raw_to_string(raw_name as *const _))
+ .collect();
+ let separator = raw_to_string(value.join).unwrap();
+ Self { items, separator }
}
- pub fn join(&self) -> Cow<str> {
- let join: *mut c_char = self.0.join;
- unsafe { CStr::from_ptr(join) }.to_string_lossy()
+ pub(crate) fn from_owned_raw(value: BNQualifiedName) -> Self {
+ let result = Self::from_raw(&value);
+ Self::free_raw(value);
+ result
}
- pub fn strings(&self) -> Vec<Cow<str>> {
- let names: *mut *mut c_char = self.0.name;
- unsafe {
- slice::from_raw_parts(names, self.0.nameCount)
- .iter()
- .map(|name| CStr::from_ptr(*name).to_string_lossy())
- .collect::<Vec<_>>()
+ pub fn into_raw(value: Self) -> BNQualifiedName {
+ let bn_join = BnString::new(&value.separator);
+ BNQualifiedName {
+ // NOTE: Leaking string list must be freed by core or us!
+ name: strings_to_string_list(&value.items),
+ // NOTE: Leaking string must be freed by core or us!
+ join: BnString::into_raw(bn_join),
+ nameCount: value.items.len(),
}
}
- pub fn len(&self) -> usize {
- self.0.nameCount
- }
-
- pub fn is_empty(&self) -> bool {
- self.0.nameCount == 0
- }
-}
-
-impl<S: BnStrCompatible> From<S> for QualifiedName {
- fn from(name: S) -> Self {
- let join = BnString::new("::");
- let name = name.into_bytes_with_nul();
- let mut list = vec![name.as_ref().as_ptr() as *const _];
-
- QualifiedName(BNQualifiedName {
- name: unsafe { BNAllocStringList(list.as_mut_ptr(), 1) },
- join: join.into_raw(),
- nameCount: 1,
- })
+ pub(crate) fn free_raw(value: BNQualifiedName) {
+ unsafe { BNFreeString(value.join) };
+ unsafe { BNFreeStringList(value.name, value.nameCount) };
}
-}
-impl<S: BnStrCompatible> From<Vec<S>> for QualifiedName {
- fn from(names: Vec<S>) -> Self {
- let join = BnString::new("::");
- let names = names
- .into_iter()
- .map(|n| n.into_bytes_with_nul())
- .collect::<Vec<_>>();
- let mut list = names
- .iter()
- .map(|n| n.as_ref().as_ptr() as *const _)
- .collect::<Vec<_>>();
-
- QualifiedName(BNQualifiedName {
- name: unsafe { BNAllocStringList(list.as_mut_ptr(), list.len()) },
- join: join.into_raw(),
- nameCount: list.len(),
- })
+ pub fn new(items: Vec<String>) -> Self {
+ Self::new_with_separator(items, "::".to_string())
}
-}
-impl Clone for QualifiedName {
- fn clone(&self) -> Self {
- let strings = self.strings();
- let name = Self::from(strings.iter().collect::<Vec<&Cow<str>>>());
- name
+ pub fn new_with_separator(items: Vec<String>, separator: String) -> Self {
+ Self { items, separator }
}
-}
-impl Hash for QualifiedName {
- fn hash<H: Hasher>(&self, state: &mut H) {
- self.join().hash(state);
- self.strings().hash(state);
+ pub fn with_item(&self, item: impl Into<String>) -> Self {
+ let mut items = self.items.clone();
+ items.push(item.into());
+ Self::new_with_separator(items, self.separator.clone())
}
-}
-impl Debug for QualifiedName {
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- write!(f, "{}", self.string())
+ pub fn push(&mut self, item: String) {
+ self.items.push(item);
}
-}
-
-impl Display for QualifiedName {
- fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
- write!(f, "{}", self.string())
- }
-}
-impl PartialEq for QualifiedName {
- fn eq(&self, other: &Self) -> bool {
- self.strings() == other.strings()
+ pub fn pop(&mut self) -> Option<String> {
+ self.items.pop()
}
-}
-impl Eq for QualifiedName {}
-
-impl Drop for QualifiedName {
- fn drop(&mut self) {
- unsafe {
- BNFreeQualifiedName(&mut self.0);
+ pub fn insert(&mut self, index: usize, item: String) {
+ if index <= self.items.len() {
+ self.items.insert(index, item);
}
}
-}
-impl CoreArrayProvider for QualifiedName {
- type Raw = BNQualifiedName;
- type Context = ();
- type Wrapped<'a> = &'a QualifiedName;
-}
-unsafe impl CoreArrayProviderInner for QualifiedName {
- unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
- BNFreeTypeNameList(raw, count);
- }
- unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
- mem::transmute(raw)
- }
-}
-
-//////////////////////////
-// QualifiedNameAndType
-
-#[repr(transparent)]
-pub struct QualifiedNameAndType(pub(crate) BNQualifiedNameAndType);
-
-impl QualifiedNameAndType {
- pub fn name(&self) -> &QualifiedName {
- unsafe { mem::transmute(&self.0.name) }
- }
-
- pub fn type_object(&self) -> Guard<Type> {
- unsafe { Guard::new(Type::from_raw(self.0.type_), self) }
+ pub fn split_last(&self) -> Option<(String, QualifiedName)> {
+ self.items.split_last().map(|(a, b)| {
+ (
+ a.to_owned(),
+ QualifiedName::new_with_separator(b.to_vec(), self.separator.clone()),
+ )
+ })
}
-}
-impl Drop for QualifiedNameAndType {
- fn drop(&mut self) {
- unsafe {
- BNFreeQualifiedNameAndType(&mut self.0);
+ /// Replaces all occurrences of a substring with another string in all items of the `QualifiedName`
+ /// and returns an owned version of the modified `QualifiedName`.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// use binaryninja::types::QualifiedName;
+ ///
+ /// let qualified_name =
+ /// QualifiedName::new(vec!["my::namespace".to_string(), "mytype".to_string()]);
+ /// let replaced = qualified_name.replace("my", "your");
+ /// assert_eq!(
+ /// replaced.items,
+ /// vec!["your::namespace".to_string(), "yourtype".to_string()]
+ /// );
+ /// ```
+ pub fn replace(&self, from: &str, to: &str) -> Self {
+ Self {
+ items: self
+ .items
+ .iter()
+ .map(|item| item.replace(from, to))
+ .collect(),
+ separator: self.separator.clone(),
}
}
-}
-impl CoreArrayProvider for QualifiedNameAndType {
- type Raw = BNQualifiedNameAndType;
- type Context = ();
- type Wrapped<'a> = &'a QualifiedNameAndType;
-}
-unsafe impl CoreArrayProviderInner for QualifiedNameAndType {
- unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
- BNFreeTypeAndNameList(raw, count);
+ /// Returns the last item, or `None` if it is empty.
+ pub fn last(&self) -> Option<&String> {
+ self.items.last()
}
- unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
- mem::transmute(raw)
- }
-}
-
-//////////////////////////
-// QualifiedNameTypeAndId
-#[repr(transparent)]
-pub struct QualifiedNameTypeAndId(pub(crate) BNQualifiedNameTypeAndId);
-
-impl QualifiedNameTypeAndId {
- pub fn name(&self) -> &QualifiedName {
- unsafe { mem::transmute(&self.0.name) }
+ /// Returns a mutable reference to the last item, or `None` if it is empty.
+ pub fn last_mut(&mut self) -> Option<&mut String> {
+ self.items.last_mut()
}
- pub fn id(&self) -> &str {
- unsafe { CStr::from_ptr(self.0.id).to_str().unwrap() }
+ pub fn len(&self) -> usize {
+ self.items.len()
}
- pub fn type_object(&self) -> Guard<Type> {
- unsafe { Guard::new(Type::from_raw(self.0.type_), self) }
+ /// A [`QualifiedName`] is empty if it has no items.
+ ///
+ /// If you want to know if the unqualified name is empty (i.e. no characters)
+ /// you must first convert the qualified name to unqualified via the `to_string` method.
+ pub fn is_empty(&self) -> bool {
+ self.items.is_empty()
}
}
-impl Drop for QualifiedNameTypeAndId {
- fn drop(&mut self) {
- unsafe {
- BNFreeQualifiedNameTypeAndId(&mut self.0);
+impl From<String> for QualifiedName {
+ fn from(value: String) -> Self {
+ Self {
+ items: vec![value],
+ // TODO: See comment in struct def.
+ separator: String::from("::"),
}
}
}
-impl CoreArrayProvider for QualifiedNameTypeAndId {
- type Raw = BNQualifiedNameTypeAndId;
- type Context = ();
- type Wrapped<'a> = &'a QualifiedNameTypeAndId;
-}
-unsafe impl CoreArrayProviderInner for QualifiedNameTypeAndId {
- unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
- BNFreeTypeIdList(raw, count);
- }
- unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
- mem::transmute(raw)
+impl From<&str> for QualifiedName {
+ fn from(value: &str) -> Self {
+ Self::from(value.to_string())
}
}
-//////////////////////////
-// NameAndType
-
-pub struct NameAndType(pub(crate) BNNameAndType);
-
-impl NameAndType {
- pub(crate) unsafe fn from_raw(raw: &BNNameAndType) -> Self {
- Self(*raw)
+impl From<&String> for QualifiedName {
+ fn from(value: &String) -> Self {
+ Self::from(value.to_owned())
}
}
-impl NameAndType {
- pub fn new<S: BnStrCompatible>(name: S, t: &Type, confidence: u8) -> Ref<Self> {
- unsafe {
- Ref::new(Self(BNNameAndType {
- name: BNAllocString(name.into_bytes_with_nul().as_ref().as_ptr() as *mut _),
- type_: Ref::into_raw(t.to_owned()).handle,
- typeConfidence: confidence,
- }))
- }
- }
-
- pub fn name(&self) -> &str {
- let c_str = unsafe { CStr::from_ptr(self.0.name) };
- c_str.to_str().unwrap()
- }
-
- pub fn t(&self) -> &Type {
- unsafe { mem::transmute::<_, &Type>(&self.0.type_) }
- }
-
- pub fn type_with_confidence(&self) -> Conf<&Type> {
- Conf::new(self.t(), self.0.typeConfidence)
+impl From<Cow<'_, str>> for QualifiedName {
+ fn from(value: Cow<'_, str>) -> Self {
+ Self::from(value.to_string())
}
}
-impl ToOwned for NameAndType {
- type Owned = Ref<Self>;
-
- fn to_owned(&self) -> Self::Owned {
- unsafe { RefCountable::inc_ref(self) }
+impl From<Vec<String>> for QualifiedName {
+ fn from(value: Vec<String>) -> Self {
+ Self::new(value)
}
}
-unsafe impl RefCountable for NameAndType {
- unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
- Self::new(
- CStr::from_ptr(handle.0.name),
- handle.t(),
- handle.0.typeConfidence,
- )
- }
-
- unsafe fn dec_ref(handle: &Self) {
- unsafe {
- BNFreeString(handle.0.name);
- RefCountable::dec_ref(handle.t());
- }
+impl From<Vec<&str>> for QualifiedName {
+ fn from(value: Vec<&str>) -> Self {
+ value
+ .iter()
+ .map(ToString::to_string)
+ .collect::<Vec<_>>()
+ .into()
}
}
-impl CoreArrayProvider for NameAndType {
- type Raw = BNNameAndType;
- type Context = ();
- type Wrapped<'a> = Guard<'a, NameAndType>;
-}
-
-unsafe impl CoreArrayProviderInner for NameAndType {
- unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
- BNFreeNameAndTypeList(raw, count);
- }
- unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
- unsafe { Guard::new(NameAndType::from_raw(raw), raw) }
+impl From<QualifiedName> for String {
+ fn from(value: QualifiedName) -> Self {
+ value.to_string()
}
}
-//////////////////
-// DataVariable
-
-#[repr(transparent)]
-pub struct DataVariable(pub(crate) BNDataVariable);
-
-// impl DataVariable {
-// pub(crate) fn from_raw(var: &BNDataVariable) -> Self {
-// let var = DataVariable(*var);
-// Self(BNDataVariable {
-// type_: unsafe { Ref::into_raw(var.t().to_owned()).handle },
-// ..var.0
-// })
-// }
-// }
-
-impl DataVariable {
- pub fn address(&self) -> u64 {
- self.0.address
- }
-
- pub fn auto_discovered(&self) -> bool {
- self.0.autoDiscovered
- }
-
- pub fn t(&self) -> &Type {
- unsafe { mem::transmute(&self.0.type_) }
- }
-
- pub fn type_with_confidence(&self) -> Conf<&Type> {
- Conf::new(self.t(), self.0.typeConfidence)
- }
+impl Index<usize> for QualifiedName {
+ type Output = String;
- pub fn symbol(&self, bv: &BinaryView) -> Option<Ref<Symbol>> {
- bv.symbol_by_address(self.0.address).ok()
+ fn index(&self, index: usize) -> &Self::Output {
+ &self.items[index]
}
}
-impl ToOwned for DataVariable {
- type Owned = Ref<Self>;
-
- fn to_owned(&self) -> Self::Owned {
- unsafe { RefCountable::inc_ref(self) }
+impl IndexMut<usize> for QualifiedName {
+ fn index_mut(&mut self, index: usize) -> &mut Self::Output {
+ &mut self.items[index]
}
}
-unsafe impl RefCountable for DataVariable {
- unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
- unsafe {
- Ref::new(Self(BNDataVariable {
- type_: Ref::into_raw(handle.t().to_owned()).handle,
- ..handle.0
- }))
- }
- }
-
- unsafe fn dec_ref(handle: &Self) {
- unsafe { BNFreeType(handle.0.type_) }
+impl Display for QualifiedName {
+ fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self.items.join(&self.separator))
}
}
-impl CoreArrayProvider for DataVariable {
- type Raw = BNDataVariable;
+impl CoreArrayProvider for QualifiedName {
+ type Raw = BNQualifiedName;
type Context = ();
- type Wrapped<'a> = &'a DataVariable;
+ type Wrapped<'a> = Self;
}
-unsafe impl CoreArrayProviderInner for DataVariable {
+
+unsafe impl CoreArrayProviderInner for QualifiedName {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
- BNFreeDataVariables(raw, count);
+ BNFreeTypeNameList(raw, count);
}
+
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
- mem::transmute(raw)
+ QualifiedName::from_raw(raw)
}
}
-/////////////////////////
-// DataVariableAndName
-
-pub struct DataVariableAndName<S: BnStrCompatible> {
- pub address: u64,
- pub t: Conf<Ref<Type>>,
- pub auto_discovered: bool,
- pub name: S,
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
+pub struct QualifiedNameAndType {
+ pub name: QualifiedName,
+ pub ty: Ref<Type>,
}
-impl DataVariableAndName<String> {
- pub(crate) fn from_raw(var: &BNDataVariableAndName) -> Self {
- Self {
- address: var.address,
- t: Conf::new(unsafe { Type::ref_from_raw(var.type_) }, var.typeConfidence),
- auto_discovered: var.autoDiscovered,
- name: raw_to_string(var.name).unwrap(),
- }
- }
-}
-
-impl<S: BnStrCompatible> DataVariableAndName<S> {
- pub fn new(address: u64, t: Conf<Ref<Type>>, auto_discovered: bool, name: S) -> Self {
+impl QualifiedNameAndType {
+ pub(crate) fn from_raw(value: &BNQualifiedNameAndType) -> Self {
Self {
- address,
- t,
- auto_discovered,
- name,
+ name: QualifiedName::from_raw(&value.name),
+ ty: unsafe { Type::from_raw(value.type_).to_owned() },
}
}
- pub fn type_with_confidence(&self) -> Conf<Ref<Type>> {
- Conf::new(self.t.contents.clone(), self.t.confidence)
- }
-}
-
-/////////////////////////
-// RegisterValueType
-
-#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
-pub enum RegisterValueType {
- UndeterminedValue,
- EntryValue,
- ConstantValue,
- ConstantPointerValue,
- ExternalPointerValue,
- StackFrameOffset,
- ReturnAddressValue,
- ImportedAddressValue,
- SignedRangeValue,
- UnsignedRangeValue,
- LookupTableValue,
- InSetOfValues,
- NotInSetOfValues,
- ConstantDataValue,
- ConstantDataZeroExtendValue,
- ConstantDataSignExtendValue,
- ConstantDataAggregateValue,
-}
-
-impl RegisterValueType {
- pub(crate) fn from_raw_value(value: u32) -> Option<Self> {
- use BNRegisterValueType::*;
- Some(match value {
- x if x == UndeterminedValue as u32 => Self::UndeterminedValue,
- x if x == EntryValue as u32 => Self::EntryValue,
- x if x == ConstantValue as u32 => Self::ConstantValue,
- x if x == ConstantPointerValue as u32 => Self::ConstantPointerValue,
- x if x == ExternalPointerValue as u32 => Self::ExternalPointerValue,
- x if x == StackFrameOffset as u32 => Self::StackFrameOffset,
- x if x == ReturnAddressValue as u32 => Self::ReturnAddressValue,
- x if x == ImportedAddressValue as u32 => Self::ImportedAddressValue,
- x if x == SignedRangeValue as u32 => Self::SignedRangeValue,
- x if x == UnsignedRangeValue as u32 => Self::UnsignedRangeValue,
- x if x == LookupTableValue as u32 => Self::LookupTableValue,
- x if x == InSetOfValues as u32 => Self::InSetOfValues,
- x if x == NotInSetOfValues as u32 => Self::NotInSetOfValues,
- x if x == ConstantDataValue as u32 => Self::ConstantDataValue,
- x if x == ConstantDataZeroExtendValue as u32 => Self::ConstantDataZeroExtendValue,
- x if x == ConstantDataSignExtendValue as u32 => Self::ConstantDataSignExtendValue,
- x if x == ConstantDataAggregateValue as u32 => Self::ConstantDataAggregateValue,
- _ => return None,
- })
+ pub(crate) fn from_owned_raw(value: BNQualifiedNameAndType) -> Self {
+ let owned = Self::from_raw(&value);
+ Self::free_raw(value);
+ owned
}
- pub(crate) fn into_raw_value(self) -> BNRegisterValueType {
- use BNRegisterValueType::*;
- match self {
- Self::UndeterminedValue => UndeterminedValue,
- Self::EntryValue => EntryValue,
- Self::ConstantValue => ConstantValue,
- Self::ConstantPointerValue => ConstantPointerValue,
- Self::ExternalPointerValue => ExternalPointerValue,
- Self::StackFrameOffset => StackFrameOffset,
- Self::ReturnAddressValue => ReturnAddressValue,
- Self::ImportedAddressValue => ImportedAddressValue,
- Self::SignedRangeValue => SignedRangeValue,
- Self::UnsignedRangeValue => UnsignedRangeValue,
- Self::LookupTableValue => LookupTableValue,
- Self::InSetOfValues => InSetOfValues,
- Self::NotInSetOfValues => NotInSetOfValues,
- Self::ConstantDataValue => ConstantDataValue,
- Self::ConstantDataZeroExtendValue => ConstantDataZeroExtendValue,
- Self::ConstantDataSignExtendValue => ConstantDataSignExtendValue,
- Self::ConstantDataAggregateValue => ConstantDataAggregateValue,
+ pub(crate) fn into_raw(value: Self) -> BNQualifiedNameAndType {
+ BNQualifiedNameAndType {
+ name: QualifiedName::into_raw(value.name),
+ type_: unsafe { Ref::into_raw(value.ty).handle },
}
}
-}
-/////////////////////////
-// RegisterValue
-
-#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
-pub struct RegisterValue {
- pub(crate) state: RegisterValueType,
- pub(crate) value: i64,
- pub(crate) offset: i64,
- pub(crate) size: usize,
-}
-
-impl RegisterValue {
- pub fn new(state: RegisterValueType, value: i64, offset: i64, size: usize) -> Self {
- Self {
- state,
- value,
- offset,
- size,
- }
+ pub(crate) fn free_raw(value: BNQualifiedNameAndType) {
+ QualifiedName::free_raw(value.name);
+ let _ = unsafe { Type::ref_from_raw(value.type_) };
}
-}
-impl From<BNRegisterValue> for RegisterValue {
- fn from(value: BNRegisterValue) -> Self {
- Self {
- state: RegisterValueType::from_raw_value(value.state as u32).unwrap(),
- value: value.value,
- offset: value.offset,
- size: value.size,
- }
+ pub fn new(name: QualifiedName, ty: Ref<Type>) -> Self {
+ Self { name, ty }
}
}
-impl From<RegisterValue> for BNRegisterValue {
- fn from(value: RegisterValue) -> Self {
+impl<T> From<(T, Ref<Type>)> for QualifiedNameAndType
+where
+ T: Into<QualifiedName>,
+{
+ fn from(value: (T, Ref<Type>)) -> Self {
Self {
- state: value.state.into_raw_value(),
- value: value.value,
- offset: value.offset,
- size: value.size,
+ name: value.0.into(),
+ ty: value.1,
}
}
}
-/////////////////////////
-// ConstantData
-
-#[derive(Clone, Debug, PartialEq, Hash)]
-pub struct ConstantData {
- function: Ref<Function>,
- value: RegisterValue,
-}
-
-impl ConstantData {
- pub(crate) fn new(function: Ref<Function>, value: RegisterValue) -> Self {
- Self { function, value }
- }
-}
-
-// unsafe impl<S: BnStrCompatible> CoreArrayProvider for DataVariableAndName<S> {
-// type Raw = BNDataVariableAndName;
-// type Context = ();
-// }
-
-// unsafe impl<S: BnStrCompatible> CoreOwnedArrayProvider for DataVariableAndName<S> {
-// unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
-// BNFreeDataVariablesAndName(raw, count);
-// }
-// }
-
-// unsafe impl<'a, S: 'a + BnStrCompatible> CoreArrayWrapper<'a> for DataVariableAndName<S> {
-// type Wrapped = &'a DataVariableAndName<S>;
-// }
-
-// unsafe impl<'a, S: 'a + BnStrCompatible> CoreArrayWrapper<'a> for DataVariableAndName<S> {
-// unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
-// mem::transmute(raw)
-// }
-// }
-
-/////////////////////////
-// ValueRange
-
-#[repr(transparent)]
-#[derive(Copy, Clone, Debug)]
-pub struct ValueRange<T> {
- raw: BNValueRange,
- _t: core::marker::PhantomData<T>,
-}
-
-impl<T> ValueRange<T> {
- fn from_raw(value: BNValueRange) -> Self {
+impl<T> From<(T, &Type)> for QualifiedNameAndType
+where
+ T: Into<QualifiedName>,
+{
+ fn from(value: (T, &Type)) -> Self {
+ let ty = value.1.to_owned();
Self {
- raw: value,
- _t: core::marker::PhantomData,
+ name: value.0.into(),
+ ty,
}
}
- fn into_raw(self) -> BNValueRange {
- self.raw
- }
-}
-
-impl IntoIterator for ValueRange<u64> {
- type Item = u64;
- type IntoIter = core::iter::StepBy<Range<u64>>;
-
- fn into_iter(self) -> Self::IntoIter {
- (self.raw.start..self.raw.end).step_by(self.raw.step.try_into().unwrap())
- }
}
-impl IntoIterator for ValueRange<i64> {
- type Item = i64;
- type IntoIter = core::iter::StepBy<Range<i64>>;
- fn into_iter(self) -> Self::IntoIter {
- (self.raw.start as i64..self.raw.end as i64).step_by(self.raw.step.try_into().unwrap())
- }
-}
-
-/////////////////////////
-// PossibleValueSet
-
-#[derive(Clone, Debug)]
-pub enum PossibleValueSet {
- UndeterminedValue,
- EntryValue {
- reg: i64,
- },
- ConstantValue {
- value: i64,
- },
- ConstantPointerValue {
- value: i64,
- },
- ExternalPointerValue,
- StackFrameOffset {
- offset: i64,
- },
- ReturnAddressValue,
- ImportedAddressValue,
- SignedRangeValue {
- offset: i64,
- ranges: Vec<ValueRange<i64>>,
- },
- UnsignedRangeValue {
- offset: i64,
- ranges: Vec<ValueRange<u64>>,
- },
- LookupTableValue {
- tables: Vec<LookupTableEntry>,
- },
- InSetOfValues {
- values: HashSet<i64>,
- },
- NotInSetOfValues {
- values: HashSet<i64>,
- },
- ConstantDataValue {
- value_type: ConstantDataType,
- value: i64,
- },
-}
-
-#[derive(Copy, Clone, Debug)]
-pub enum ConstantDataType {
- Value,
- ZeroExtend,
- SignExtend,
- Aggregate,
+impl CoreArrayProvider for QualifiedNameAndType {
+ type Raw = BNQualifiedNameAndType;
+ type Context = ();
+ type Wrapped<'a> = Self;
}
-impl PossibleValueSet {
- pub(crate) unsafe fn from_raw(value: BNPossibleValueSet) -> Self {
- unsafe fn from_range<T>(value: BNPossibleValueSet) -> Vec<ValueRange<T>> {
- core::slice::from_raw_parts(value.ranges, value.count)
- .iter()
- .copied()
- .map(|range| ValueRange::from_raw(range))
- .collect()
- }
- let from_sets = |value: BNPossibleValueSet| {
- unsafe { core::slice::from_raw_parts(value.valueSet, value.count) }
- .iter()
- .copied()
- .collect()
- };
- use BNRegisterValueType::*;
- match value.state {
- UndeterminedValue => Self::UndeterminedValue,
- EntryValue => Self::EntryValue { reg: value.value },
- ConstantValue => Self::ConstantValue { value: value.value },
- ConstantPointerValue => Self::ConstantPointerValue { value: value.value },
- StackFrameOffset => Self::StackFrameOffset {
- offset: value.value,
- },
- ConstantDataValue => Self::ConstantDataValue {
- value_type: ConstantDataType::Value,
- value: value.value,
- },
- ConstantDataZeroExtendValue => Self::ConstantDataValue {
- value_type: ConstantDataType::ZeroExtend,
- value: value.value,
- },
- ConstantDataSignExtendValue => Self::ConstantDataValue {
- value_type: ConstantDataType::SignExtend,
- value: value.value,
- },
- ConstantDataAggregateValue => Self::ConstantDataValue {
- value_type: ConstantDataType::Aggregate,
- value: value.value,
- },
- SignedRangeValue => Self::SignedRangeValue {
- offset: value.value,
- ranges: from_range(value),
- },
- UnsignedRangeValue => Self::UnsignedRangeValue {
- offset: value.value,
- ranges: from_range(value),
- },
- LookupTableValue => {
- let raw_tables = unsafe { core::slice::from_raw_parts(value.table, value.count) };
- let raw_from_tables = |i: &BNLookupTableEntry| unsafe {
- core::slice::from_raw_parts(i.fromValues, i.fromCount)
- };
- let tables = raw_tables
- .iter()
- .map(|table| LookupTableEntry {
- from_values: raw_from_tables(table).to_vec(),
- to_value: table.toValue,
- })
- .collect();
- Self::LookupTableValue { tables }
- }
- NotInSetOfValues => Self::NotInSetOfValues {
- values: from_sets(value),
- },
- InSetOfValues => Self::InSetOfValues {
- values: from_sets(value),
- },
- ImportedAddressValue => Self::ImportedAddressValue,
- ReturnAddressValue => Self::ReturnAddressValue,
- ExternalPointerValue => Self::ExternalPointerValue,
- }
- }
- pub(crate) fn into_raw(self) -> PossibleValueSetRaw {
- let mut raw: BNPossibleValueSet = unsafe { core::mem::zeroed() };
- // set the state field
- raw.state = self.value_type().into_raw_value();
- // set all other fields
- match self {
- PossibleValueSet::UndeterminedValue
- | PossibleValueSet::ExternalPointerValue
- | PossibleValueSet::ReturnAddressValue
- | PossibleValueSet::ImportedAddressValue => {}
- PossibleValueSet::EntryValue { reg: value }
- | PossibleValueSet::ConstantValue { value }
- | PossibleValueSet::ConstantPointerValue { value }
- | PossibleValueSet::ConstantDataValue { value, .. }
- | PossibleValueSet::StackFrameOffset { offset: value } => raw.value = value,
- PossibleValueSet::NotInSetOfValues { values }
- | PossibleValueSet::InSetOfValues { values } => {
- let values = Box::leak(values.into_iter().collect());
- raw.valueSet = values.as_mut_ptr();
- raw.count = values.len();
- }
- PossibleValueSet::SignedRangeValue { offset, ranges } => {
- let ranges = Box::leak(ranges.into_iter().map(|x| x.into_raw()).collect());
- raw.value = offset;
- raw.ranges = ranges.as_mut_ptr();
- raw.count = ranges.len();
- }
- PossibleValueSet::UnsignedRangeValue { offset, ranges } => {
- let ranges = Box::leak(ranges.into_iter().map(|x| x.into_raw()).collect());
- raw.value = offset;
- raw.ranges = ranges.as_mut_ptr();
- raw.count = ranges.len();
- }
- PossibleValueSet::LookupTableValue { tables } => {
- let tables = Box::leak(tables.into_iter().map(|table| table.into_raw()).collect());
- // SAFETY: BNLookupTableEntry and LookupTableEntryRaw are transparent
- raw.table = tables.as_mut_ptr() as *mut BNLookupTableEntry;
- raw.count = tables.len();
- }
- }
- PossibleValueSetRaw(raw)
+unsafe impl CoreArrayProviderInner for QualifiedNameAndType {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeTypeAndNameList(raw, count);
}
- pub fn value_type(&self) -> RegisterValueType {
- use RegisterValueType::*;
- match self {
- PossibleValueSet::UndeterminedValue => UndeterminedValue,
- PossibleValueSet::EntryValue { .. } => EntryValue,
- PossibleValueSet::ConstantValue { .. } => ConstantValue,
- PossibleValueSet::ConstantPointerValue { .. } => ConstantPointerValue,
- PossibleValueSet::ExternalPointerValue => ExternalPointerValue,
- PossibleValueSet::StackFrameOffset { .. } => StackFrameOffset,
- PossibleValueSet::ReturnAddressValue => ReturnAddressValue,
- PossibleValueSet::ImportedAddressValue => ImportedAddressValue,
- PossibleValueSet::SignedRangeValue { .. } => SignedRangeValue,
- PossibleValueSet::UnsignedRangeValue { .. } => UnsignedRangeValue,
- PossibleValueSet::LookupTableValue { .. } => LookupTableValue,
- PossibleValueSet::InSetOfValues { .. } => InSetOfValues,
- PossibleValueSet::NotInSetOfValues { .. } => NotInSetOfValues,
- PossibleValueSet::ConstantDataValue {
- value_type: ConstantDataType::Value,
- ..
- } => ConstantDataValue,
- PossibleValueSet::ConstantDataValue {
- value_type: ConstantDataType::ZeroExtend,
- ..
- } => ConstantDataZeroExtendValue,
- PossibleValueSet::ConstantDataValue {
- value_type: ConstantDataType::SignExtend,
- ..
- } => ConstantDataSignExtendValue,
- PossibleValueSet::ConstantDataValue {
- value_type: ConstantDataType::Aggregate,
- ..
- } => ConstantDataAggregateValue,
- }
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ QualifiedNameAndType::from_raw(raw)
}
}
-/// The owned version of the BNPossibleValueSet
-#[repr(transparent)]
-pub(crate) struct PossibleValueSetRaw(BNPossibleValueSet);
-
-impl PossibleValueSetRaw {
- pub fn as_ffi(&self) -> &BNPossibleValueSet {
- &self.0
- }
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
+pub struct QualifiedNameTypeAndId {
+ pub name: QualifiedName,
+ pub ty: Ref<Type>,
+ pub id: String,
}
-impl Drop for PossibleValueSetRaw {
- fn drop(&mut self) {
- use BNRegisterValueType::*;
- match self.0.state {
- UndeterminedValue
- | ExternalPointerValue
- | ReturnAddressValue
- | ImportedAddressValue
- | EntryValue
- | ConstantValue
- | ConstantPointerValue
- | StackFrameOffset
- | ConstantDataValue
- | ConstantDataZeroExtendValue
- | ConstantDataSignExtendValue
- | ConstantDataAggregateValue => {}
- InSetOfValues | NotInSetOfValues => {
- let _values: Box<[i64]> = unsafe {
- Box::from_raw(ptr::slice_from_raw_parts_mut(self.0.valueSet, self.0.count))
- };
- }
- SignedRangeValue | UnsignedRangeValue => {
- let _ranges: Box<[BNValueRange]> = unsafe {
- Box::from_raw(ptr::slice_from_raw_parts_mut(self.0.ranges, self.0.count))
- };
- }
- LookupTableValue => {
- // SAFETY: LookupTableEntryRaw and BNLookupTableEntry can be safely transmuted
- let table_ptr = self.0.table as *mut LookupTableEntryRaw;
- let _table: Box<[LookupTableEntryRaw]> = unsafe {
- Box::from_raw(ptr::slice_from_raw_parts_mut(table_ptr, self.0.count))
- };
- }
+impl QualifiedNameTypeAndId {
+ pub(crate) fn from_raw(value: &BNQualifiedNameTypeAndId) -> Self {
+ Self {
+ name: QualifiedName::from_raw(&value.name),
+ ty: unsafe { Type::from_raw(value.type_) }.to_owned(),
+ id: raw_to_string(value.id).unwrap(),
}
}
-}
-
-/////////////////////////
-// LookupTableEntry
-#[derive(Clone, Debug)]
-pub struct LookupTableEntry {
- pub from_values: Vec<i64>,
- pub to_value: i64,
-}
-
-impl LookupTableEntry {
- fn into_raw(self) -> LookupTableEntryRaw {
- let from_value = Box::leak(self.from_values.into_boxed_slice());
- LookupTableEntryRaw(BNLookupTableEntry {
- toValue: self.to_value,
- fromValues: from_value.as_mut_ptr(),
- fromCount: from_value.len(),
- })
+ pub(crate) fn from_owned_raw(value: BNQualifiedNameTypeAndId) -> Self {
+ let owned = Self::from_raw(&value);
+ Self::free_raw(value);
+ owned
}
-}
-/// The owned version of the BNLookupTableEntry
-#[repr(transparent)]
-struct LookupTableEntryRaw(BNLookupTableEntry);
-impl Drop for LookupTableEntryRaw {
- fn drop(&mut self) {
- let _from_value: Box<[i64]> = unsafe {
- Box::from_raw(ptr::slice_from_raw_parts_mut(
- self.0.fromValues,
- self.0.fromCount,
- ))
- };
- }
-}
-
-/////////////////////////
-// ArchAndAddr
-
-#[derive(Copy, Clone, Eq, Hash, PartialEq)]
-pub struct ArchAndAddr {
- pub arch: CoreArchitecture,
- pub address: u64,
-}
-
-/////////////////////////
-// UserVariableValues
-
-pub struct UserVariableValues {
- pub(crate) vars: *const [BNUserVariableValue],
-}
-
-impl UserVariableValues {
- pub fn into_hashmap(self) -> HashMap<Variable, HashMap<ArchAndAddr, PossibleValueSet>> {
- let mut result: HashMap<Variable, HashMap<ArchAndAddr, PossibleValueSet>> = HashMap::new();
- for (var, def_site, possible_val) in self.all() {
- result
- .entry(var)
- .or_default()
- .entry(def_site)
- .or_insert(possible_val);
+ pub(crate) fn into_raw(value: Self) -> BNQualifiedNameTypeAndId {
+ let bn_id = BnString::new(value.id);
+ BNQualifiedNameTypeAndId {
+ name: QualifiedName::into_raw(value.name),
+ id: BnString::into_raw(bn_id),
+ type_: unsafe { Ref::into_raw(value.ty) }.handle,
}
- result
- }
- pub fn all(&self) -> impl Iterator<Item = (Variable, ArchAndAddr, PossibleValueSet)> {
- unsafe { &*self.vars }.iter().map(|var_val| {
- let var = unsafe { Variable::from_raw(var_val.var) };
- let def_site = ArchAndAddr {
- arch: unsafe { CoreArchitecture::from_raw(var_val.defSite.arch) },
- address: var_val.defSite.address,
- };
- let possible_val = unsafe { PossibleValueSet::from_raw(var_val.value) };
- (var, def_site, possible_val)
- })
- }
- pub fn values_from_variable(
- &self,
- var: Variable,
- ) -> impl Iterator<Item = (ArchAndAddr, PossibleValueSet)> {
- self.all()
- .filter(move |(t_var, _, _)| t_var == &var)
- .map(|(_var, def_site, possible_val)| (def_site, possible_val))
- }
-}
-
-impl Drop for UserVariableValues {
- fn drop(&mut self) {
- unsafe { BNFreeUserVariableValues(self.vars as *mut BNUserVariableValue) };
}
-}
-
-/////////////////////////
-// ConstantReference
-#[derive(Copy, Clone, Eq, Hash, PartialEq)]
-pub struct ConstantReference {
- pub value: i64,
- pub size: usize,
- pub pointer: bool,
- pub intermediate: bool,
-}
-
-impl ConstantReference {
- pub fn from_raw(value: BNConstantReference) -> Self {
- Self {
- value: value.value,
- size: value.size,
- pointer: value.pointer,
- intermediate: value.intermediate,
- }
- }
- pub fn into_raw(self) -> BNConstantReference {
- BNConstantReference {
- value: self.value,
- size: self.size,
- pointer: self.pointer,
- intermediate: self.intermediate,
- }
+ pub(crate) fn free_raw(value: BNQualifiedNameTypeAndId) {
+ QualifiedName::free_raw(value.name);
+ let _ = unsafe { Type::ref_from_raw(value.type_) };
+ let _ = unsafe { BnString::from_raw(value.id) };
}
}
-impl CoreArrayProvider for ConstantReference {
- type Raw = BNConstantReference;
+impl CoreArrayProvider for QualifiedNameTypeAndId {
+ type Raw = BNQualifiedNameTypeAndId;
type Context = ();
- type Wrapped<'a> = Self;
+ type Wrapped<'a> = QualifiedNameTypeAndId;
}
-unsafe impl CoreArrayProviderInner for ConstantReference {
- unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
- BNFreeConstantReferenceList(raw)
+unsafe impl CoreArrayProviderInner for QualifiedNameTypeAndId {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeTypeIdList(raw, count);
}
+
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
- Self::from_raw(*raw)
+ QualifiedNameTypeAndId::from_raw(raw)
}
}
-/////////////////////////
-// IndirectBranchInfo
-
-pub struct IndirectBranchInfo {
- pub source_arch: CoreArchitecture,
- pub source_addr: u64,
- pub dest_arch: CoreArchitecture,
- pub dest_addr: u64,
- pub auto_defined: bool,
+// TODO: Document how this type is used for many different purposes. (this is literally (string, type))
+// TODO: Ex. the name might be the parser it came from
+// TODO: Ex. the name might be the param name for an intrinsic input
+// TODO: Should we make new types for each varying use case?
+#[derive(Debug, Clone, Eq, PartialEq, Hash)]
+pub struct NameAndType {
+ pub name: String,
+ pub ty: Conf<Ref<Type>>,
}
-impl IndirectBranchInfo {
- pub fn from_raw(value: BNIndirectBranchInfo) -> Self {
+impl NameAndType {
+ pub(crate) fn from_raw(value: &BNNameAndType) -> Self {
Self {
- source_arch: unsafe { CoreArchitecture::from_raw(value.sourceArch) },
- source_addr: value.sourceAddr,
- dest_arch: unsafe { CoreArchitecture::from_raw(value.destArch) },
- dest_addr: value.destAddr,
- auto_defined: value.autoDefined,
- }
- }
- pub fn into_raw(self) -> BNIndirectBranchInfo {
- BNIndirectBranchInfo {
- sourceArch: self.source_arch.0,
- sourceAddr: self.source_addr,
- destArch: self.dest_arch.0,
- destAddr: self.dest_addr,
- autoDefined: self.auto_defined,
+ // TODO: I dislike using this function here.
+ name: raw_to_string(value.name as *mut _).unwrap(),
+ ty: Conf::new(
+ unsafe { Type::from_raw(value.type_).to_owned() },
+ value.typeConfidence,
+ ),
}
}
-}
-impl CoreArrayProvider for IndirectBranchInfo {
- type Raw = BNIndirectBranchInfo;
- type Context = ();
- type Wrapped<'a> = Self;
-}
-
-unsafe impl CoreArrayProviderInner for IndirectBranchInfo {
- unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
- BNFreeIndirectBranchList(raw)
- }
- unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
- Self::from_raw(*raw)
+ pub(crate) fn from_owned_raw(value: BNNameAndType) -> Self {
+ let owned = Self::from_raw(&value);
+ Self::free_raw(value);
+ owned
}
-}
-
-/////////////////////////
-// HighlightStandardColor
-
-#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
-pub enum HighlightStandardColor {
- //NoHighlightColor,
- BlueHighlightColor,
- GreenHighlightColor,
- CyanHighlightColor,
- RedHighlightColor,
- MagentaHighlightColor,
- YellowHighlightColor,
- OrangeHighlightColor,
- WhiteHighlightColor,
- BlackHighlightColor,
-}
-impl HighlightStandardColor {
- pub fn from_raw(value: BNHighlightStandardColor) -> Option<Self> {
- Some(match value {
- BNHighlightStandardColor::NoHighlightColor => return None,
- BNHighlightStandardColor::BlueHighlightColor => Self::BlueHighlightColor,
- BNHighlightStandardColor::GreenHighlightColor => Self::GreenHighlightColor,
- BNHighlightStandardColor::CyanHighlightColor => Self::CyanHighlightColor,
- BNHighlightStandardColor::RedHighlightColor => Self::RedHighlightColor,
- BNHighlightStandardColor::MagentaHighlightColor => Self::MagentaHighlightColor,
- BNHighlightStandardColor::YellowHighlightColor => Self::YellowHighlightColor,
- BNHighlightStandardColor::OrangeHighlightColor => Self::OrangeHighlightColor,
- BNHighlightStandardColor::WhiteHighlightColor => Self::WhiteHighlightColor,
- BNHighlightStandardColor::BlackHighlightColor => Self::BlackHighlightColor,
- })
- }
- pub fn into_raw(self) -> BNHighlightStandardColor {
- match self {
- //Self::NoHighlightColor => BNHighlightStandardColor::NoHighlightColor,
- Self::BlueHighlightColor => BNHighlightStandardColor::BlueHighlightColor,
- Self::GreenHighlightColor => BNHighlightStandardColor::GreenHighlightColor,
- Self::CyanHighlightColor => BNHighlightStandardColor::CyanHighlightColor,
- Self::RedHighlightColor => BNHighlightStandardColor::RedHighlightColor,
- Self::MagentaHighlightColor => BNHighlightStandardColor::MagentaHighlightColor,
- Self::YellowHighlightColor => BNHighlightStandardColor::YellowHighlightColor,
- Self::OrangeHighlightColor => BNHighlightStandardColor::OrangeHighlightColor,
- Self::WhiteHighlightColor => BNHighlightStandardColor::WhiteHighlightColor,
- Self::BlackHighlightColor => BNHighlightStandardColor::BlackHighlightColor,
+ pub(crate) fn into_raw(value: Self) -> BNNameAndType {
+ let bn_name = BnString::new(value.name);
+ BNNameAndType {
+ name: BnString::into_raw(bn_name),
+ type_: unsafe { Ref::into_raw(value.ty.contents) }.handle,
+ typeConfidence: value.ty.confidence,
}
}
-}
-
-/////////////////////////
-// HighlightColor
-
-#[derive(Debug, Copy, Clone)]
-pub enum HighlightColor {
- NoHighlightColor {
- alpha: u8,
- },
- StandardHighlightColor {
- color: HighlightStandardColor,
- alpha: u8,
- },
- MixedHighlightColor {
- color: HighlightStandardColor,
- mix_color: HighlightStandardColor,
- mix: u8,
- alpha: u8,
- },
- CustomHighlightColor {
- r: u8,
- g: u8,
- b: u8,
- alpha: u8,
- },
-}
-impl HighlightColor {
- pub fn from_raw(raw: BNHighlightColor) -> Self {
- const HIGHLIGHT_COLOR: u32 = BNHighlightColorStyle::StandardHighlightColor as u32;
- const MIXED_HIGHLIGHT_COLOR: u32 = BNHighlightColorStyle::MixedHighlightColor as u32;
- const CUSTOM_HIGHLIHGT_COLOR: u32 = BNHighlightColorStyle::CustomHighlightColor as u32;
- match raw.style as u32 {
- HIGHLIGHT_COLOR => {
- let Some(color) = HighlightStandardColor::from_raw(raw.color) else {
- // StandardHighlightColor with NoHighlightColor, is no color
- return Self::NoHighlightColor { alpha: raw.alpha };
- };
- Self::StandardHighlightColor {
- color,
- alpha: raw.alpha,
- }
- }
- MIXED_HIGHLIGHT_COLOR => {
- let Some(color) = HighlightStandardColor::from_raw(raw.color) else {
- panic!("Highlight mixed color with no color");
- };
- let Some(mix_color) = HighlightStandardColor::from_raw(raw.mixColor) else {
- panic!("Highlight mixed color with no mix_color");
- };
- Self::MixedHighlightColor {
- color,
- mix_color,
- mix: raw.mix,
- alpha: raw.alpha,
- }
- }
- CUSTOM_HIGHLIHGT_COLOR => Self::CustomHighlightColor {
- r: raw.r,
- g: raw.g,
- b: raw.b,
- alpha: raw.alpha,
- },
- // other color style is just no color
- _ => Self::NoHighlightColor { alpha: u8::MAX },
- }
- }
-
- pub fn into_raw(self) -> BNHighlightColor {
- let zeroed: BNHighlightColor = unsafe { core::mem::zeroed() };
- match self {
- Self::NoHighlightColor { alpha } => BNHighlightColor {
- style: BNHighlightColorStyle::StandardHighlightColor,
- color: BNHighlightStandardColor::NoHighlightColor,
- alpha,
- ..zeroed
- },
- Self::StandardHighlightColor { color, alpha } => BNHighlightColor {
- style: BNHighlightColorStyle::StandardHighlightColor,
- color: color.into_raw(),
- alpha,
- ..zeroed
- },
- Self::MixedHighlightColor {
- color,
- mix_color,
- mix,
- alpha,
- } => BNHighlightColor {
- color: color.into_raw(),
- mixColor: mix_color.into_raw(),
- mix,
- alpha,
- ..zeroed
- },
- Self::CustomHighlightColor { r, g, b, alpha } => BNHighlightColor {
- r,
- g,
- b,
- alpha,
- ..zeroed
- },
- }
+ pub(crate) fn free_raw(value: BNNameAndType) {
+ let _ = unsafe { BnString::from_raw(value.name) };
+ let _ = unsafe { Type::ref_from_raw(value.type_) };
}
-}
-/////////////////////////
-// IntegerDisplayType
-
-pub type IntegerDisplayType = binaryninjacore_sys::BNIntegerDisplayType;
-
-/////////////////////////
-// StackVariableReference
-
-#[derive(Debug, Clone)]
-pub struct StackVariableReference {
- _source_operand: u32,
- var_type: Conf<Ref<Type>>,
- name: BnString,
- var: Variable,
- offset: i64,
- size: usize,
-}
-
-impl StackVariableReference {
- pub fn from_raw(value: BNStackVariableReference) -> Self {
- let var_type = Conf::new(
- unsafe { Type::ref_from_raw(value.type_) },
- value.typeConfidence,
- );
- let name = unsafe { BnString::from_raw(value.name) };
- let var = unsafe { Variable::from_identifier(value.varIdentifier) };
- let offset = value.referencedOffset;
- let size = value.size;
+ pub fn new(name: impl Into<String>, ty: Conf<Ref<Type>>) -> Self {
Self {
- _source_operand: value.sourceOperand,
- var_type,
- name,
- var,
- offset,
- size,
- }
- }
- pub fn variable(&self) -> &Variable {
- &self.var
- }
- pub fn variable_type(&self) -> Conf<&Type> {
- self.var_type.as_ref()
- }
- pub fn name(&self) -> &str {
- self.name.as_str()
- }
- pub fn offset(&self) -> i64 {
- self.offset
- }
- pub fn size(&self) -> usize {
- self.size
- }
-}
-
-impl CoreArrayProvider for StackVariableReference {
- type Raw = BNStackVariableReference;
- type Context = ();
- type Wrapped<'a> = Guard<'a, Self>;
-}
-
-unsafe impl CoreArrayProviderInner for StackVariableReference {
- unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
- BNFreeStackVariableReferenceList(raw, count)
- }
- unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
- Guard::new(Self::from_raw(*raw), context)
- }
-}
-
-/////////////////////////
-// RegisterStackAdjustment
-
-#[derive(Debug, Copy, Clone)]
-pub struct RegisterStackAdjustment<A: Architecture> {
- reg_id: u32,
- adjustment: Conf<i32>,
- arch: A::Handle,
-}
-
-impl<A: Architecture> RegisterStackAdjustment<A> {
- pub(crate) unsafe fn from_raw(value: BNRegisterStackAdjustment, arch: A::Handle) -> Self {
- RegisterStackAdjustment {
- reg_id: value.regStack,
- adjustment: Conf::new(value.adjustment, value.confidence),
- arch,
- }
- }
- pub(crate) fn into_raw(self) -> BNRegisterStackAdjustment {
- BNRegisterStackAdjustment {
- regStack: self.reg_id,
- adjustment: self.adjustment.contents,
- confidence: self.adjustment.confidence,
- }
- }
- pub fn new<I>(reg_id: u32, adjustment: I, arch_handle: A::Handle) -> Self
- where
- I: Into<Conf<i32>>,
- {
- Self {
- reg_id,
- adjustment: adjustment.into(),
- arch: arch_handle,
+ name: name.into(),
+ ty,
}
}
- pub const fn register_id(&self) -> u32 {
- self.reg_id
- }
- pub fn register(&self) -> A::Register {
- self.arch.borrow().register_from_id(self.reg_id).unwrap()
- }
-}
-
-impl<A: Architecture> CoreArrayProvider for RegisterStackAdjustment<A> {
- type Raw = BNRegisterStackAdjustment;
- type Context = A::Handle;
- type Wrapped<'a> = Self;
-}
-
-unsafe impl<A: Architecture> CoreArrayProviderInner for RegisterStackAdjustment<A> {
- unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
- BNFreeRegisterStackAdjustments(raw)
- }
- unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped<'a> {
- Self::from_raw(*raw, context.clone())
- }
-}
-
-/////////////////////////
-// RegisterStackAdjustment
-
-// NOTE only exists as part of an Array, never owned
-pub struct MergedVariable {
- target: Variable,
- // droped by the CoreArrayProviderInner::free
- sources: ManuallyDrop<Array<Variable>>,
-}
-
-impl MergedVariable {
- pub fn target(&self) -> Variable {
- self.target
- }
- pub fn sources(&self) -> &Array<Variable> {
- &self.sources
- }
}
-impl CoreArrayProvider for MergedVariable {
- type Raw = BNMergedVariable;
+impl CoreArrayProvider for NameAndType {
+ type Raw = BNNameAndType;
type Context = ();
type Wrapped<'a> = Self;
}
-unsafe impl CoreArrayProviderInner for MergedVariable {
+unsafe impl CoreArrayProviderInner for NameAndType {
unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
- BNFreeMergedVariableList(raw, count)
- }
- unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
- Self {
- target: Variable::from_raw(raw.target),
- sources: ManuallyDrop::new(Array::new(raw.sources, raw.sourceCount, ())),
- }
- }
-}
-
-/////////////////////////
-// UnresolvedIndirectBranches
-
-// NOTE only exists as part of an Array, never owned
-pub struct UnresolvedIndirectBranches(u64);
-
-impl UnresolvedIndirectBranches {
- pub fn address(&self) -> u64 {
- self.0
+ BNFreeNameAndTypeList(raw, count);
}
-}
-impl CoreArrayProvider for UnresolvedIndirectBranches {
- type Raw = u64;
- type Context = ();
- type Wrapped<'a> = Self;
-}
-
-unsafe impl CoreArrayProviderInner for UnresolvedIndirectBranches {
- unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
- BNFreeAddressList(raw)
- }
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
- Self(*raw)
+ NameAndType::from_raw(raw)
}
}
diff --git a/rust/src/update.rs b/rust/src/update.rs
index 31e02cd8..2a22c9c9 100644
--- a/rust/src/update.rs
+++ b/rust/src/update.rs
@@ -1,30 +1,88 @@
-use core::{ffi, mem, ptr};
+#![allow(dead_code)]
+use std::ffi::{c_char, c_void};
use std::time::{Duration, SystemTime, UNIX_EPOCH};
-use binaryninjacore_sys::*;
-
+use crate::progress::{NoProgressCallback, ProgressCallback};
use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner};
-use crate::string::BnString;
+use crate::string::{raw_to_string, BnString};
+use binaryninjacore_sys::*;
pub type UpdateResult = BNUpdateResult;
-#[repr(C)]
+pub fn auto_updates_enabled() -> bool {
+ unsafe { BNAreAutoUpdatesEnabled() }
+}
+
+pub fn set_auto_updates_enabled(enabled: bool) {
+ unsafe { BNSetAutoUpdatesEnabled(enabled) }
+}
+
+pub fn time_since_last_update_check() -> Duration {
+ Duration::from_secs(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 = std::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() }
+}
+
+#[derive(Clone, Debug)]
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<()>,
+ pub name: String,
+ pub description: String,
+ pub latest_version: String,
}
impl UpdateChannel {
- pub(crate) unsafe fn ref_from_raw(handle: &BNUpdateChannel) -> &Self {
- mem::transmute(handle)
+ pub(crate) fn from_raw(value: &BNUpdateChannel) -> Self {
+ Self {
+ name: raw_to_string(value.name as *mut _).unwrap(),
+ description: raw_to_string(value.description as *mut _).unwrap(),
+ latest_version: raw_to_string(value.latestVersion as *mut _).unwrap(),
+ }
+ }
+
+ pub(crate) fn from_owned_raw(value: BNUpdateChannel) -> Self {
+ let owned = Self::from_raw(&value);
+ Self::free_raw(value);
+ owned
+ }
+
+ pub(crate) fn into_raw(value: Self) -> BNUpdateChannel {
+ let bn_name = BnString::new(value.name);
+ let bn_description = BnString::new(value.description);
+ let bn_latest_version = BnString::new(value.latest_version);
+ BNUpdateChannel {
+ name: BnString::into_raw(bn_name),
+ description: BnString::into_raw(bn_description),
+ latestVersion: BnString::into_raw(bn_latest_version),
+ }
+ }
+
+ pub(crate) fn free_raw(value: BNUpdateChannel) {
+ let _ = unsafe { BnString::from_raw(value.name) };
+ let _ = unsafe { BnString::from_raw(value.description) };
+ let _ = unsafe { BnString::from_raw(value.latestVersion) };
}
pub fn all() -> Result<Array<UpdateChannel>, BnString> {
let mut count = 0;
- let mut errors = ptr::null_mut();
+ let mut errors = std::ptr::null_mut();
let result = unsafe { BNGetUpdateChannels(&mut count, &mut errors) };
if !errors.is_null() {
Err(unsafe { BnString::from_raw(errors) })
@@ -37,9 +95,10 @@ impl UpdateChannel {
/// 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) };
+ let mut errors = std::ptr::null_mut();
+ let result = unsafe {
+ BNGetUpdateChannelVersions(self.name.as_ptr() as *const c_char, &mut count, &mut errors)
+ };
if !errors.is_null() {
Err(unsafe { BnString::from_raw(errors) })
} else {
@@ -54,20 +113,20 @@ impl UpdateChannel {
let versions = self.versions()?;
for version in &versions {
if &version.version == last_version {
- return Ok(version.clone());
+ return Ok(version);
}
}
- panic!();
+ Err(BnString::new("Could not find latest version"))
}
/// Whether updates are available
pub fn updates_available(&self) -> Result<bool, BnString> {
- let mut errors = ptr::null_mut();
+ let mut errors = std::ptr::null_mut();
let result = unsafe {
BNAreUpdatesAvailable(
- self.name.as_ptr(),
- ptr::null_mut(),
- ptr::null_mut(),
+ self.name.as_ptr() as *const c_char,
+ std::ptr::null_mut(),
+ std::ptr::null_mut(),
&mut errors,
)
};
@@ -79,38 +138,24 @@ impl UpdateChannel {
}
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)
- }
+ self.update_to_latest_with_progress(NoProgressCallback)
}
- pub fn update_to_latest_with_progress<F>(
+ pub fn update_to_latest_with_progress<P: ProgressCallback>(
&self,
- mut progress: F,
- ) -> Result<UpdateResult, BnString>
- where
- F: FnMut(usize, usize) -> bool,
- {
- let mut errors = ptr::null_mut();
+ mut progress: P,
+ ) -> Result<UpdateResult, BnString> {
+ let mut errors = std::ptr::null_mut();
+
let result = unsafe {
BNUpdateToLatestVersion(
- self.name.as_ptr(),
+ self.name.as_ptr() as *const c_char,
&mut errors,
- Some(cb_progress::<F>),
- &mut progress as *mut _ as *mut ffi::c_void,
+ Some(P::cb_progress_callback),
+ &mut progress as *mut P as *mut c_void,
)
};
+
if !errors.is_null() {
Err(unsafe { BnString::from_raw(errors) })
} else {
@@ -119,41 +164,26 @@ impl UpdateChannel {
}
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)
- }
+ self.update_with_progress(version, NoProgressCallback)
}
- pub fn update_with_progress<F>(
+ pub fn update_with_progress<P: ProgressCallback>(
&self,
version: &UpdateVersion,
- mut progress: F,
- ) -> Result<UpdateResult, BnString>
- where
- F: FnMut(usize, usize) -> bool,
- {
- let mut errors = ptr::null_mut();
+ mut progress: P,
+ ) -> Result<UpdateResult, BnString> {
+ let mut errors = std::ptr::null_mut();
+
let result = unsafe {
BNUpdateToVersion(
- self.name.as_ptr(),
- version.version.as_ptr(),
+ self.name.as_ptr() as *const c_char,
+ version.version.as_ptr() as *const c_char,
&mut errors,
- Some(cb_progress::<F>),
- &mut progress as *mut _ as *mut ffi::c_void,
+ Some(P::cb_progress_callback),
+ &mut progress as *mut P as *mut c_void,
)
};
+
if !errors.is_null() {
Err(unsafe { BnString::from_raw(errors) })
} else {
@@ -165,7 +195,7 @@ impl UpdateChannel {
impl CoreArrayProvider for UpdateChannel {
type Raw = BNUpdateChannel;
type Context = ();
- type Wrapped<'a> = &'a Self;
+ type Wrapped<'a> = Self;
}
unsafe impl CoreArrayProviderInner for UpdateChannel {
@@ -174,39 +204,53 @@ unsafe impl CoreArrayProviderInner for UpdateChannel {
}
unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
- UpdateChannel::ref_from_raw(raw)
+ UpdateChannel::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<()>,
+ pub version: String,
+ pub notes: String,
+ pub time: SystemTime,
}
impl UpdateVersion {
- pub(crate) unsafe fn ref_from_raw(handle: &BNUpdateVersion) -> &Self {
- mem::transmute(handle)
+ pub(crate) fn from_raw(value: &BNUpdateVersion) -> Self {
+ Self {
+ version: raw_to_string(value.version as *mut _).unwrap(),
+ notes: raw_to_string(value.notes as *mut _).unwrap(),
+ time: UNIX_EPOCH + Duration::from_secs(value.time),
+ }
}
- pub fn time(&self) -> SystemTime {
- UNIX_EPOCH + Duration::from_secs(self.time)
+ pub(crate) fn from_owned_raw(value: BNUpdateVersion) -> Self {
+ let owned = Self::from_raw(&value);
+ Self::free_raw(value);
+ owned
+ }
+
+ pub(crate) fn into_raw(value: Self) -> BNUpdateVersion {
+ let bn_version = BnString::new(value.version);
+ let bn_notes = BnString::new(value.notes);
+ let epoch = value.time.duration_since(UNIX_EPOCH).unwrap();
+ BNUpdateVersion {
+ version: BnString::into_raw(bn_version),
+ notes: BnString::into_raw(bn_notes),
+ time: epoch.as_secs(),
+ }
}
- pub fn set_time(&mut self, time: SystemTime) {
- let epoch = time.duration_since(UNIX_EPOCH).unwrap();
- self.time = epoch.as_secs();
+ pub(crate) fn free_raw(value: BNUpdateVersion) {
+ let _ = unsafe { BnString::from_raw(value.version) };
+ let _ = unsafe { BnString::from_raw(value.notes) };
}
}
impl CoreArrayProvider for UpdateVersion {
type Raw = BNUpdateVersion;
type Context = ();
- type Wrapped<'a> = &'a Self;
+ type Wrapped<'a> = Self;
}
unsafe impl CoreArrayProviderInner for UpdateVersion {
@@ -215,58 +259,6 @@ unsafe impl CoreArrayProviderInner for UpdateVersion {
}
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(())
+ UpdateVersion::from_raw(raw)
}
}
-
-pub fn updates_checked() {
- unsafe { BNUpdatesChecked() }
-}
-
-unsafe extern "C" fn cb_progress_nop(
- _ctxt: *mut ::std::os::raw::c_void,
- _progress: usize,
- _total: usize,
-) -> bool {
- true
-}
-
-unsafe extern "C" fn cb_progress<F: FnMut(usize, usize) -> bool>(
- ctxt: *mut ::std::os::raw::c_void,
- progress: usize,
- total: usize,
-) -> bool {
- let ctxt: &mut F = &mut *(ctxt as *mut F);
- ctxt(progress, total)
-}
diff --git a/rust/src/variable.rs b/rust/src/variable.rs
new file mode 100644
index 00000000..260e12d8
--- /dev/null
+++ b/rust/src/variable.rs
@@ -0,0 +1,910 @@
+#![allow(unused)]
+
+use crate::confidence::Conf;
+use crate::function::{Function, Location};
+use crate::rc::{CoreArrayProvider, CoreArrayProviderInner, Ref};
+use crate::string::{raw_to_string, BnString};
+use crate::types::Type;
+use binaryninjacore_sys::{
+ BNDataVariable, BNDataVariableAndName, BNFreeDataVariableAndName, BNFreeDataVariables,
+ BNFreeILInstructionList, BNFreeIndirectBranchList, BNFreeMergedVariableList,
+ BNFreePossibleValueSet, BNFreeStackVariableReferenceList, BNFreeUserVariableValues,
+ BNFreeVariableList, BNFreeVariableNameAndTypeList, BNFromVariableIdentifier,
+ BNIndirectBranchInfo, BNLookupTableEntry, BNMergedVariable, BNPossibleValueSet,
+ BNRegisterValue, BNRegisterValueType, BNStackVariableReference, BNToVariableIdentifier,
+ BNUserVariableValue, BNValueRange, BNVariable, BNVariableNameAndType, BNVariableSourceType,
+};
+use std::collections::HashSet;
+
+pub type VariableSourceType = BNVariableSourceType;
+pub type RegisterValueType = BNRegisterValueType;
+
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
+pub struct DataVariable {
+ pub address: u64,
+ pub ty: Conf<Ref<Type>>,
+ pub auto_discovered: bool,
+}
+
+impl DataVariable {
+ pub(crate) fn from_raw(value: &BNDataVariable) -> Self {
+ Self {
+ address: value.address,
+ ty: Conf::new(
+ unsafe { Type::from_raw(value.type_).to_owned() },
+ value.typeConfidence,
+ ),
+ auto_discovered: value.autoDiscovered,
+ }
+ }
+
+ pub(crate) fn from_owned_raw(value: BNDataVariable) -> Self {
+ let owned = Self::from_raw(&value);
+ Self::free_raw(value);
+ owned
+ }
+
+ pub(crate) fn into_raw(value: Self) -> BNDataVariable {
+ BNDataVariable {
+ address: value.address,
+ type_: unsafe { Ref::into_raw(value.ty.contents) }.handle,
+ autoDiscovered: value.auto_discovered,
+ typeConfidence: value.ty.confidence,
+ }
+ }
+
+ pub(crate) fn into_owned_raw(value: &Self) -> BNDataVariable {
+ BNDataVariable {
+ address: value.address,
+ type_: value.ty.contents.handle,
+ autoDiscovered: value.auto_discovered,
+ typeConfidence: value.ty.confidence,
+ }
+ }
+
+ pub(crate) fn free_raw(value: BNDataVariable) {
+ let _ = unsafe { Type::ref_from_raw(value.type_) };
+ }
+
+ pub fn new(address: u64, ty: Conf<Ref<Type>>, auto_discovered: bool) -> Self {
+ Self {
+ address,
+ ty,
+ auto_discovered,
+ }
+ }
+}
+
+impl CoreArrayProvider for DataVariable {
+ type Raw = BNDataVariable;
+ type Context = ();
+ type Wrapped<'a> = Self;
+}
+
+unsafe impl CoreArrayProviderInner for DataVariable {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeDataVariables(raw, count);
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ DataVariable::from_raw(raw)
+ }
+}
+
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
+pub struct NamedDataVariableWithType {
+ pub address: u64,
+ pub ty: Conf<Ref<Type>>,
+ pub name: String,
+ pub auto_discovered: bool,
+}
+
+impl NamedDataVariableWithType {
+ pub(crate) fn from_raw(value: &BNDataVariableAndName) -> Self {
+ Self {
+ address: value.address,
+ ty: Conf::new(
+ unsafe { Type::from_raw(value.type_).to_owned() },
+ value.typeConfidence,
+ ),
+ // TODO: I dislike using this function here.
+ name: raw_to_string(value.name as *mut _).unwrap(),
+ auto_discovered: value.autoDiscovered,
+ }
+ }
+
+ pub(crate) fn from_owned_raw(value: BNDataVariableAndName) -> Self {
+ let owned = Self::from_raw(&value);
+ Self::free_raw(value);
+ owned
+ }
+
+ pub(crate) unsafe fn from_ref_raw(value: *mut BNDataVariableAndName) -> Self {
+ let owned = Self::from_raw(&*value);
+ Self::free_ref_raw(value);
+ owned
+ }
+
+ pub(crate) fn into_raw(value: Self) -> BNDataVariableAndName {
+ let bn_name = BnString::new(value.name);
+ BNDataVariableAndName {
+ address: value.address,
+ type_: unsafe { Ref::into_raw(value.ty.contents) }.handle,
+ name: BnString::into_raw(bn_name),
+ autoDiscovered: value.auto_discovered,
+ typeConfidence: value.ty.confidence,
+ }
+ }
+
+ pub(crate) fn free_ref_raw(value: *mut BNDataVariableAndName) {
+ unsafe { BNFreeDataVariableAndName(value) }
+ }
+
+ pub(crate) fn free_raw(value: BNDataVariableAndName) {
+ let _ = unsafe { Type::ref_from_raw(value.type_) };
+ let _ = unsafe { BnString::from_raw(value.name) };
+ }
+
+ pub fn new(address: u64, ty: Conf<Ref<Type>>, name: String, auto_discovered: bool) -> Self {
+ Self {
+ address,
+ ty,
+ name,
+ auto_discovered,
+ }
+ }
+}
+
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
+pub struct NamedVariableWithType {
+ pub variable: Variable,
+ pub ty: Conf<Ref<Type>>,
+ pub name: String,
+ pub auto_defined: bool,
+}
+
+impl NamedVariableWithType {
+ pub(crate) fn from_raw(value: &BNVariableNameAndType) -> Self {
+ Self {
+ variable: value.var.into(),
+ ty: Conf::new(
+ unsafe { Type::from_raw(value.type_) }.to_owned(),
+ value.typeConfidence,
+ ),
+ // TODO: I dislike using this function here.
+ name: raw_to_string(value.name as *mut _).unwrap(),
+ auto_defined: value.autoDefined,
+ }
+ }
+
+ pub(crate) fn from_owned_raw(value: BNVariableNameAndType) -> Self {
+ let owned = Self::from_raw(&value);
+ Self::free_raw(value);
+ owned
+ }
+
+ pub(crate) fn into_raw(value: Self) -> BNVariableNameAndType {
+ let bn_name = BnString::new(value.name);
+ BNVariableNameAndType {
+ var: value.variable.into(),
+ type_: unsafe { Ref::into_raw(value.ty.contents) }.handle,
+ name: BnString::into_raw(bn_name),
+ autoDefined: value.auto_defined,
+ typeConfidence: value.ty.confidence,
+ }
+ }
+
+ pub(crate) fn free_raw(value: BNVariableNameAndType) {
+ let _ = unsafe { Type::ref_from_raw(value.type_) };
+ let _ = unsafe { BnString::from_raw(value.name) };
+ }
+
+ pub fn new(variable: Variable, ty: Conf<Ref<Type>>, name: String, auto_defined: bool) -> Self {
+ Self {
+ variable,
+ ty,
+ name,
+ auto_defined,
+ }
+ }
+}
+
+impl CoreArrayProvider for NamedVariableWithType {
+ type Raw = BNVariableNameAndType;
+ type Context = ();
+ type Wrapped<'a> = NamedVariableWithType;
+}
+
+unsafe impl CoreArrayProviderInner for NamedVariableWithType {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeVariableNameAndTypeList(raw, count)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ Self::from_raw(raw)
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct UserVariableValue {
+ pub variable: Variable,
+ pub def_site: Location,
+ pub value: PossibleValueSet,
+}
+
+impl UserVariableValue {
+ pub(crate) fn from_raw(value: &BNUserVariableValue) -> Self {
+ Self {
+ variable: value.var.into(),
+ def_site: value.defSite.into(),
+ value: PossibleValueSet::from_raw(&value.value),
+ }
+ }
+
+ pub(crate) fn into_raw(value: Self) -> BNUserVariableValue {
+ BNUserVariableValue {
+ var: value.variable.into(),
+ defSite: value.def_site.into(),
+ value: PossibleValueSet::into_raw(value.value),
+ }
+ }
+}
+
+impl CoreArrayProvider for UserVariableValue {
+ type Raw = BNUserVariableValue;
+ type Context = ();
+ type Wrapped<'a> = Self;
+}
+
+unsafe impl CoreArrayProviderInner for UserVariableValue {
+ unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
+ BNFreeUserVariableValues(raw)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ UserVariableValue::from_raw(raw)
+ }
+}
+
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
+pub struct StackVariableReference {
+ source_operand: u32,
+ pub variable_type: Conf<Ref<Type>>,
+ pub name: String,
+ pub variable: Variable,
+ pub offset: i64,
+ pub size: usize,
+}
+
+impl StackVariableReference {
+ pub(crate) fn from_raw(value: &BNStackVariableReference) -> Self {
+ Self {
+ source_operand: value.sourceOperand,
+ variable_type: Conf::new(
+ unsafe { Type::from_raw(value.type_) }.to_owned(),
+ value.typeConfidence,
+ ),
+ // TODO: I dislike using this function here.
+ name: raw_to_string(value.name).unwrap(),
+ // TODO: It might be beneficial to newtype the identifier as VariableIdentifier.
+ variable: Variable::from_identifier(value.varIdentifier),
+ offset: value.referencedOffset,
+ size: value.size,
+ }
+ }
+
+ pub(crate) fn from_owned_raw(value: BNStackVariableReference) -> Self {
+ let owned = Self::from_raw(&value);
+ Self::free_raw(value);
+ owned
+ }
+
+ pub(crate) fn into_raw(value: Self) -> BNStackVariableReference {
+ let bn_name = BnString::new(value.name);
+ BNStackVariableReference {
+ sourceOperand: value.source_operand,
+ typeConfidence: value.variable_type.confidence,
+ type_: unsafe { Ref::into_raw(value.variable_type.contents) }.handle,
+ name: BnString::into_raw(bn_name),
+ varIdentifier: value.variable.to_identifier(),
+ referencedOffset: value.offset,
+ size: value.size,
+ }
+ }
+
+ pub(crate) fn free_raw(value: BNStackVariableReference) {
+ let _ = unsafe { Type::ref_from_raw(value.type_) };
+ let _ = unsafe { BnString::from_raw(value.name) };
+ }
+}
+
+impl CoreArrayProvider for StackVariableReference {
+ type Raw = BNStackVariableReference;
+ type Context = ();
+ type Wrapped<'a> = Self;
+}
+
+unsafe impl CoreArrayProviderInner for StackVariableReference {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeStackVariableReferenceList(raw, count)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ StackVariableReference::from_raw(raw)
+ }
+}
+
+#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
+pub struct SSAVariable {
+ pub variable: Variable,
+ pub version: usize,
+}
+
+impl SSAVariable {
+ pub fn new(variable: Variable, version: usize) -> Self {
+ Self { variable, version }
+ }
+}
+
+impl CoreArrayProvider for SSAVariable {
+ type Raw = usize;
+ type Context = Variable;
+ type Wrapped<'a> = Self;
+}
+
+unsafe impl CoreArrayProviderInner for SSAVariable {
+ 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> {
+ SSAVariable::new(*context, *raw)
+ }
+}
+
+/// Variables exist within functions at Medium Level IL or higher.
+///
+/// As such, they are to be used within the context of a [`Function`].
+/// See [`Function::variable_name`] as an example of how to interact with variables.
+#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
+pub struct Variable {
+ pub ty: VariableSourceType,
+ // TODO: VariableIndex type?
+ pub index: u32,
+ // TODO: Type this to `VariableStorage`
+ pub storage: i64,
+}
+
+impl Variable {
+ pub fn new(ty: VariableSourceType, index: u32, storage: i64) -> Self {
+ Self { ty, index, storage }
+ }
+
+ // TODO: Retype this...
+ // TODO: Add VariableIdentifier
+ // TODO: StackVariableReference has a varIdentifier, i think thats really it.
+ pub fn from_identifier(ident: u64) -> Self {
+ unsafe { BNFromVariableIdentifier(ident) }.into()
+ }
+
+ pub fn to_identifier(&self) -> u64 {
+ let raw = BNVariable::from(*self);
+ unsafe { BNToVariableIdentifier(&raw) }
+ }
+}
+
+impl From<BNVariable> for Variable {
+ fn from(value: BNVariable) -> Self {
+ Self {
+ ty: value.type_,
+ index: value.index,
+ storage: value.storage,
+ }
+ }
+}
+
+impl From<&BNVariable> for Variable {
+ fn from(value: &BNVariable) -> Self {
+ Self::from(*value)
+ }
+}
+
+impl From<Variable> for BNVariable {
+ fn from(value: Variable) -> Self {
+ Self {
+ type_: value.ty,
+ index: value.index,
+ storage: value.storage,
+ }
+ }
+}
+
+impl From<&Variable> for BNVariable {
+ fn from(value: &Variable) -> Self {
+ BNVariable::from(*value)
+ }
+}
+
+impl CoreArrayProvider for Variable {
+ type Raw = BNVariable;
+ type Context = ();
+ type Wrapped<'a> = Self;
+}
+
+unsafe impl CoreArrayProviderInner for Variable {
+ unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
+ BNFreeVariableList(raw)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ Variable::from(raw)
+ }
+}
+
+#[derive(Debug, Clone, Hash, PartialEq, Eq)]
+pub struct MergedVariable {
+ pub target: Variable,
+ pub sources: Vec<Variable>,
+}
+
+impl MergedVariable {
+ pub(crate) fn from_raw(value: &BNMergedVariable) -> Self {
+ let raw_sources = unsafe { std::slice::from_raw_parts(value.sources, value.sourceCount) };
+ Self {
+ target: value.target.into(),
+ sources: raw_sources.iter().map(Into::into).collect(),
+ }
+ }
+
+ // TODO: If we want from_owned_raw/free_raw/into_raw we need a way to allocate sources.
+}
+
+impl CoreArrayProvider for MergedVariable {
+ type Raw = BNMergedVariable;
+ type Context = ();
+ type Wrapped<'a> = Self;
+}
+
+unsafe impl CoreArrayProviderInner for MergedVariable {
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeMergedVariableList(raw, count)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ Self::from_raw(raw)
+ }
+}
+
+// TODO: This is used in MLIL and HLIL, this really should exist in each of those.
+#[derive(Clone, Debug, Hash, Eq, PartialEq)]
+pub struct ConstantData {
+ // TODO: We really do not want to store a ref to function here.
+ pub function: Ref<Function>,
+ pub value: RegisterValue,
+}
+
+impl ConstantData {
+ pub fn new(function: Ref<Function>, value: RegisterValue) -> Self {
+ Self { function, value }
+ }
+}
+
+#[derive(Clone, Copy, Debug, Hash, Eq, PartialEq)]
+pub struct RegisterValue {
+ pub state: RegisterValueType,
+ // TODO: This value can be anything. Make `T`
+ pub value: i64,
+ pub offset: i64,
+ pub size: usize,
+}
+
+impl RegisterValue {
+ pub fn new(state: RegisterValueType, value: i64, offset: i64, size: usize) -> Self {
+ Self {
+ state,
+ value,
+ offset,
+ size,
+ }
+ }
+}
+
+impl From<BNRegisterValue> for RegisterValue {
+ fn from(value: BNRegisterValue) -> Self {
+ Self {
+ state: value.state,
+ value: value.value,
+ offset: value.offset,
+ size: value.size,
+ }
+ }
+}
+
+impl From<RegisterValue> for BNRegisterValue {
+ fn from(value: RegisterValue) -> Self {
+ Self {
+ state: value.state,
+ value: value.value,
+ offset: value.offset,
+ size: value.size,
+ }
+ }
+}
+
+#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
+pub struct ValueRange<T> {
+ pub start: T,
+ pub end: T,
+ pub step: u64,
+}
+
+impl From<BNValueRange> for ValueRange<u64> {
+ fn from(value: BNValueRange) -> Self {
+ Self {
+ start: value.start,
+ end: value.end,
+ step: value.step,
+ }
+ }
+}
+
+impl From<ValueRange<u64>> for BNValueRange {
+ fn from(value: ValueRange<u64>) -> Self {
+ Self {
+ start: value.start,
+ end: value.end,
+ step: value.step,
+ }
+ }
+}
+
+impl From<BNValueRange> for ValueRange<i64> {
+ fn from(value: BNValueRange) -> Self {
+ Self {
+ start: value.start as i64,
+ end: value.end as i64,
+ step: value.step,
+ }
+ }
+}
+
+impl From<ValueRange<i64>> for BNValueRange {
+ fn from(value: ValueRange<i64>) -> Self {
+ Self {
+ start: value.start as u64,
+ end: value.end as u64,
+ step: value.step,
+ }
+ }
+}
+
+// TODO: Document where its used and why it exists.
+// TODO: What if we are looking up u64?
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct LookupTableEntry {
+ /// The set of integers that correspond with [`Self::to`].
+ from: HashSet<i64>,
+ /// The associated "mapped" value.
+ to: i64,
+}
+
+impl LookupTableEntry {
+ pub(crate) fn from_raw(value: &BNLookupTableEntry) -> Self {
+ let from_values = unsafe { std::slice::from_raw_parts(value.fromValues, value.fromCount) };
+ Self {
+ // TODO: Better way to construct HashSet<i64>?
+ from: HashSet::from_iter(from_values.iter().copied()),
+ to: value.toValue,
+ }
+ }
+}
+
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum PossibleValueSet {
+ UndeterminedValue,
+ EntryValue {
+ // TODO: This is actually the BNVariable storage.
+ // TODO: Type this to `VariableStorage` or something.
+ reg: i64,
+ },
+ ConstantValue {
+ // TODO: Make this T
+ // TODO: This can be really anything (signed, unsigned or even a float).
+ value: i64,
+ },
+ ConstantPointerValue {
+ // TODO: Shouldn't this be u64?
+ value: i64,
+ },
+ ExternalPointerValue {
+ // TODO: Shouldn't this be u64?
+ value: i64,
+ offset: i64,
+ },
+ StackFrameOffset {
+ value: i64,
+ },
+ ReturnAddressValue,
+ ImportedAddressValue,
+ SignedRangeValue {
+ value: i64,
+ ranges: Vec<ValueRange<i64>>,
+ },
+ UnsignedRangeValue {
+ value: i64,
+ ranges: Vec<ValueRange<u64>>,
+ },
+ LookupTableValue {
+ table: Vec<LookupTableEntry>,
+ },
+ InSetOfValues {
+ values: HashSet<i64>,
+ },
+ NotInSetOfValues {
+ values: HashSet<i64>,
+ },
+ // TODO: Can you even get _just_ a constant data value?
+ ConstantDataValue {
+ value: i64,
+ size: usize,
+ },
+ ConstantDataZeroExtendValue {
+ // TODO: Zero extend should be u64?
+ value: i64,
+ size: usize,
+ },
+ ConstantDataSignExtendValue {
+ value: i64,
+ size: usize,
+ },
+ ConstantDataAggregateValue {
+ // WTF is aggregate??
+ value: i64,
+ size: usize,
+ },
+}
+
+impl PossibleValueSet {
+ pub(crate) fn from_raw(value: &BNPossibleValueSet) -> Self {
+ match value.state {
+ RegisterValueType::UndeterminedValue => Self::UndeterminedValue,
+ RegisterValueType::EntryValue => Self::EntryValue { reg: value.value },
+ RegisterValueType::ConstantValue => Self::ConstantValue { value: value.value },
+ RegisterValueType::ConstantPointerValue => {
+ Self::ConstantPointerValue { value: value.value }
+ }
+ RegisterValueType::ExternalPointerValue => Self::ExternalPointerValue {
+ value: value.value,
+ offset: value.offset,
+ },
+ RegisterValueType::StackFrameOffset => Self::StackFrameOffset { value: value.value },
+ RegisterValueType::ReturnAddressValue => Self::ReturnAddressValue,
+ RegisterValueType::ImportedAddressValue => Self::ImportedAddressValue,
+ RegisterValueType::SignedRangeValue => {
+ let raw_ranges = unsafe { std::slice::from_raw_parts(value.ranges, value.count) };
+ Self::SignedRangeValue {
+ value: value.value,
+ ranges: raw_ranges.iter().map(|&r| r.into()).collect(),
+ }
+ }
+ RegisterValueType::UnsignedRangeValue => {
+ let raw_ranges = unsafe { std::slice::from_raw_parts(value.ranges, value.count) };
+ Self::UnsignedRangeValue {
+ value: value.value,
+ ranges: raw_ranges.iter().map(|&r| r.into()).collect(),
+ }
+ }
+ RegisterValueType::LookupTableValue => {
+ let raw_entries = unsafe { std::slice::from_raw_parts(value.table, value.count) };
+ Self::LookupTableValue {
+ table: raw_entries.iter().map(LookupTableEntry::from_raw).collect(),
+ }
+ }
+ RegisterValueType::InSetOfValues => {
+ let raw_values = unsafe { std::slice::from_raw_parts(value.valueSet, value.count) };
+ Self::InSetOfValues {
+ values: raw_values.iter().copied().collect(),
+ }
+ }
+ RegisterValueType::NotInSetOfValues => {
+ let raw_values = unsafe { std::slice::from_raw_parts(value.valueSet, value.count) };
+ Self::NotInSetOfValues {
+ values: raw_values.iter().copied().collect(),
+ }
+ }
+ RegisterValueType::ConstantDataValue => Self::ConstantDataValue {
+ value: value.value,
+ size: value.size,
+ },
+ RegisterValueType::ConstantDataZeroExtendValue => Self::ConstantDataZeroExtendValue {
+ value: value.value,
+ size: value.size,
+ },
+ RegisterValueType::ConstantDataSignExtendValue => Self::ConstantDataSignExtendValue {
+ value: value.value,
+ size: value.size,
+ },
+ RegisterValueType::ConstantDataAggregateValue => Self::ConstantDataAggregateValue {
+ value: value.value,
+ size: value.size,
+ },
+ }
+ }
+
+ /// Take ownership over an "owned" core allocated value. Do not call this for a rust allocated value.
+ pub(crate) fn from_owned_raw(mut value: BNPossibleValueSet) -> Self {
+ let owned = Self::from_raw(&value);
+ // TODO: This entire function is a little wonky.
+ Self::free_raw(&mut value);
+ owned
+ }
+
+ pub(crate) fn into_raw(value: Self) -> BNPossibleValueSet {
+ let mut raw = BNPossibleValueSet {
+ state: value.value_type(),
+ ..Default::default()
+ };
+ match value {
+ PossibleValueSet::UndeterminedValue => {}
+ PossibleValueSet::EntryValue { reg } => {
+ raw.value = reg;
+ }
+ PossibleValueSet::ConstantValue { value } => {
+ raw.value = value;
+ }
+ PossibleValueSet::ConstantPointerValue { value } => {
+ raw.value = value;
+ }
+ PossibleValueSet::ExternalPointerValue { value, offset } => {
+ raw.value = value;
+ raw.offset = offset;
+ }
+ PossibleValueSet::StackFrameOffset { value } => {
+ raw.value = value;
+ }
+ PossibleValueSet::ReturnAddressValue => {}
+ PossibleValueSet::ImportedAddressValue => {}
+ PossibleValueSet::SignedRangeValue { value, ranges } => {
+ raw.value = value;
+ // TODO: raw.ranges
+ // TODO: requires core allocation and freeing.
+ // TODO: See `BNFreePossibleValueSet` for why this sucks.
+ }
+ PossibleValueSet::UnsignedRangeValue { value, ranges } => {
+ raw.value = value;
+ // TODO: raw.ranges
+ // TODO: requires core allocation and freeing.
+ // TODO: See `BNFreePossibleValueSet` for why this sucks.
+ }
+ PossibleValueSet::LookupTableValue { table } => {
+ // TODO: raw.table
+ // TODO: requires core allocation and freeing.
+ // TODO: See `BNFreePossibleValueSet` for why this sucks.
+ }
+ PossibleValueSet::InSetOfValues { values } => {
+ // TODO: raw.valueSet
+ // TODO: requires core allocation and freeing.
+ // TODO: See `BNFreePossibleValueSet` for why this sucks.
+ }
+ PossibleValueSet::NotInSetOfValues { values } => {
+ // TODO: raw.valueSet
+ // TODO: requires core allocation and freeing.
+ // TODO: See `BNFreePossibleValueSet` for why this sucks.
+ }
+ PossibleValueSet::ConstantDataValue { value, size } => {
+ raw.value = value;
+ raw.size = size;
+ }
+ PossibleValueSet::ConstantDataZeroExtendValue { value, size } => {
+ raw.value = value;
+ raw.size = size;
+ }
+ PossibleValueSet::ConstantDataSignExtendValue { value, size } => {
+ raw.value = value;
+ raw.size = size;
+ }
+ PossibleValueSet::ConstantDataAggregateValue { value, size } => {
+ raw.value = value;
+ raw.size = size;
+ }
+ };
+ raw
+ }
+
+ /// Free a CORE ALLOCATED possible value set. Do not use this with [Self::into_raw] values.
+ pub(crate) fn free_raw(value: &mut BNPossibleValueSet) {
+ unsafe { BNFreePossibleValueSet(value) }
+ }
+
+ /// Free a RUST ALLOCATED possible value set. Do not use this with CORE ALLOCATED values.
+ pub(crate) fn free_owned_raw(value: BNPossibleValueSet) {
+ // TODO: Once we fill out allocation of the possible value set then we should fill this out as well.
+ }
+
+ pub fn value_type(&self) -> RegisterValueType {
+ match self {
+ PossibleValueSet::UndeterminedValue => RegisterValueType::UndeterminedValue,
+ PossibleValueSet::EntryValue { .. } => RegisterValueType::EntryValue,
+ PossibleValueSet::ConstantValue { .. } => RegisterValueType::ConstantValue,
+ PossibleValueSet::ConstantPointerValue { .. } => {
+ RegisterValueType::ConstantPointerValue
+ }
+ PossibleValueSet::ExternalPointerValue { .. } => {
+ RegisterValueType::ExternalPointerValue
+ }
+ PossibleValueSet::StackFrameOffset { .. } => RegisterValueType::StackFrameOffset,
+ PossibleValueSet::ReturnAddressValue => RegisterValueType::ReturnAddressValue,
+ PossibleValueSet::ImportedAddressValue => RegisterValueType::ImportedAddressValue,
+ PossibleValueSet::SignedRangeValue { .. } => RegisterValueType::SignedRangeValue,
+ PossibleValueSet::UnsignedRangeValue { .. } => RegisterValueType::UnsignedRangeValue,
+ PossibleValueSet::LookupTableValue { .. } => RegisterValueType::LookupTableValue,
+ PossibleValueSet::InSetOfValues { .. } => RegisterValueType::InSetOfValues,
+ PossibleValueSet::NotInSetOfValues { .. } => RegisterValueType::NotInSetOfValues,
+ PossibleValueSet::ConstantDataValue { .. } => RegisterValueType::ConstantDataValue,
+ PossibleValueSet::ConstantDataZeroExtendValue { .. } => {
+ RegisterValueType::ConstantDataZeroExtendValue
+ }
+ PossibleValueSet::ConstantDataSignExtendValue { .. } => {
+ RegisterValueType::ConstantDataSignExtendValue
+ }
+ PossibleValueSet::ConstantDataAggregateValue { .. } => {
+ RegisterValueType::ConstantDataAggregateValue
+ }
+ }
+ }
+}
+
+#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
+pub struct IndirectBranchInfo {
+ pub source: Location,
+ pub dest: Location,
+ pub auto_defined: bool,
+}
+
+impl From<BNIndirectBranchInfo> for IndirectBranchInfo {
+ fn from(value: BNIndirectBranchInfo) -> Self {
+ Self {
+ source: Location::from_raw(value.sourceAddr, value.sourceArch),
+ dest: Location::from_raw(value.destAddr, value.destArch),
+ auto_defined: value.autoDefined,
+ }
+ }
+}
+
+impl From<IndirectBranchInfo> for BNIndirectBranchInfo {
+ fn from(value: IndirectBranchInfo) -> Self {
+ let source_arch = value
+ .source
+ .arch
+ .map(|a| a.handle)
+ .unwrap_or(std::ptr::null_mut());
+ let dest_arch = value
+ .source
+ .arch
+ .map(|a| a.handle)
+ .unwrap_or(std::ptr::null_mut());
+ Self {
+ sourceArch: source_arch,
+ sourceAddr: value.source.addr,
+ destArch: dest_arch,
+ destAddr: value.dest.addr,
+ autoDefined: value.auto_defined,
+ }
+ }
+}
+
+impl CoreArrayProvider for IndirectBranchInfo {
+ type Raw = BNIndirectBranchInfo;
+ type Context = ();
+ type Wrapped<'a> = Self;
+}
+
+unsafe impl CoreArrayProviderInner for IndirectBranchInfo {
+ unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
+ BNFreeIndirectBranchList(raw)
+ }
+
+ unsafe fn wrap_raw<'a>(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped<'a> {
+ Self::from(*raw)
+ }
+}
diff --git a/rust/src/worker_thread.rs b/rust/src/worker_thread.rs
new file mode 100644
index 00000000..349456e5
--- /dev/null
+++ b/rust/src/worker_thread.rs
@@ -0,0 +1,71 @@
+use crate::string::BnStrCompatible;
+use binaryninjacore_sys::*;
+use std::ffi::{c_char, c_void};
+
+pub struct WorkerThreadActionExecutor {
+ func: Box<dyn Fn()>,
+}
+
+impl WorkerThreadActionExecutor {
+ unsafe extern "C" fn cb_execute(ctx: *mut c_void) {
+ let f: Box<Self> = Box::from_raw(ctx as *mut Self);
+ f.execute();
+ }
+
+ pub fn execute(&self) {
+ (self.func)();
+ }
+}
+
+pub fn execute_on_worker_thread<F: Fn() + 'static, S: BnStrCompatible>(name: S, f: F) {
+ let boxed_executor = Box::new(WorkerThreadActionExecutor { func: Box::new(f) });
+ let raw_executor = Box::into_raw(boxed_executor);
+ let name = name.into_bytes_with_nul();
+ unsafe {
+ BNWorkerEnqueueNamed(
+ raw_executor as *mut c_void,
+ Some(WorkerThreadActionExecutor::cb_execute),
+ name.as_ref().as_ptr() as *const c_char,
+ )
+ }
+}
+
+pub fn execute_on_worker_thread_priority<F: Fn() + 'static, S: BnStrCompatible>(name: S, f: F) {
+ let boxed_executor = Box::new(WorkerThreadActionExecutor { func: Box::new(f) });
+ let raw_executor = Box::into_raw(boxed_executor);
+ let name = name.into_bytes_with_nul();
+ unsafe {
+ BNWorkerPriorityEnqueueNamed(
+ raw_executor as *mut c_void,
+ Some(WorkerThreadActionExecutor::cb_execute),
+ name.as_ref().as_ptr() as *const c_char,
+ )
+ }
+}
+
+pub fn execute_on_worker_thread_interactive<F: Fn() + 'static, S: BnStrCompatible>(name: S, f: F) {
+ let boxed_executor = Box::new(WorkerThreadActionExecutor { func: Box::new(f) });
+ let raw_executor = Box::into_raw(boxed_executor);
+ let name = name.into_bytes_with_nul();
+ unsafe {
+ BNWorkerInteractiveEnqueueNamed(
+ raw_executor as *mut c_void,
+ Some(WorkerThreadActionExecutor::cb_execute),
+ name.as_ref().as_ptr() as *const c_char,
+ )
+ }
+}
+
+/// Returns the number of worker threads that are currently running.
+/// By default, this is the number of cores on the system minus one
+///
+/// To set the worker thread count use [`set_worker_thread_count`].
+pub fn worker_thread_count() -> usize {
+ unsafe { BNGetWorkerThreadCount() }
+}
+
+/// Sets the number of worker threads that are currently running.
+/// By default, this is the number of cores on the system minus one.
+pub fn set_worker_thread_count(count: usize) {
+ unsafe { BNSetWorkerThreadCount(count) }
+}
diff --git a/rust/src/workflow.rs b/rust/src/workflow.rs
index 12253644..68cd3e18 100644
--- a/rust/src/workflow.rs
+++ b/rust/src/workflow.rs
@@ -3,14 +3,16 @@ use std::ffi::{c_char, c_void};
use std::ptr::NonNull;
use crate::architecture::CoreArchitecture;
-use crate::basicblock::BasicBlock;
+use crate::basic_block::BasicBlock;
+use crate::binary_view::BinaryView;
use crate::flowgraph::FlowGraph;
use crate::function::{Function, NativeBlock};
-use crate::llil::{self, FunctionForm, Mutable};
+use crate::high_level_il::HighLevelILFunction;
+use crate::low_level_il::function::{LowLevelILFunction, Mutable, NonSSA, NonSSAVariant};
+use crate::low_level_il::MutableLiftedILFunction;
+use crate::medium_level_il::MediumLevelILFunction;
use crate::rc::{Array, CoreArrayProvider, CoreArrayProviderInner, Guard, Ref, RefCountable};
use crate::string::{BnStrCompatible, BnString};
-use crate::{hlil, mlil};
-use crate::binaryview::BinaryView;
#[repr(transparent)]
/// The AnalysisContext struct is used to represent the current state of
@@ -34,78 +36,79 @@ impl AnalysisContext {
pub fn view(&self) -> Ref<BinaryView> {
let result = unsafe { BNAnalysisContextGetBinaryView(self.handle.as_ptr()) };
assert!(!result.is_null());
- unsafe { BinaryView::from_raw(result) }
+ unsafe { BinaryView::ref_from_raw(result) }
}
- /// Function for the current AnalysisContext
+ /// [`Function`] for the current AnalysisContext
pub fn function(&self) -> Ref<Function> {
let result = unsafe { BNAnalysisContextGetFunction(self.handle.as_ptr()) };
assert!(!result.is_null());
- unsafe { Function::from_raw(result) }
+ unsafe { Function::ref_from_raw(result) }
}
- /// LowLevelILFunction used to represent Low Level IL
- pub unsafe fn lifted_il_function<F: FunctionForm>(
+ /// [`LowLevelILFunction`] used to represent Low Level IL
+ pub unsafe fn lifted_il_function(
&self,
- ) -> Option<Ref<llil::Function<CoreArchitecture, Mutable, F>>> {
+ ) -> Option<Ref<MutableLiftedILFunction<CoreArchitecture>>> {
let func = self.function();
let result = unsafe { BNGetFunctionLiftedIL(func.handle) };
let arch = self.function().arch();
unsafe {
- Some(llil::Function::ref_from_raw(
+ Some(LowLevelILFunction::ref_from_raw(
arch,
NonNull::new(result)?.as_ptr(),
))
}
}
- pub fn set_lifted_il_function<F: FunctionForm>(
- &self,
- value: &llil::Function<CoreArchitecture, Mutable, F>,
- ) {
+ pub fn set_lifted_il_function(&self, value: &MutableLiftedILFunction<CoreArchitecture>) {
unsafe { BNSetLiftedILFunction(self.handle.as_ptr(), value.handle) }
}
- /// LowLevelILFunction used to represent Low Level IL
- pub unsafe fn llil_function<F: FunctionForm>(
+ // TODO: This returns LiftedNonSSA because the lifting code was written before we could patch the IL
+ // TODO: At some point we need to take the lifting code and make it available to regular IL.
+ /// [`LowLevelILFunction`] used to represent Low Level IL
+ pub unsafe fn llil_function<V: NonSSAVariant>(
&self,
- ) -> Option<Ref<llil::Function<CoreArchitecture, Mutable, F>>> {
+ ) -> Option<Ref<LowLevelILFunction<CoreArchitecture, Mutable, NonSSA<V>>>> {
let result = unsafe { BNAnalysisContextGetLowLevelILFunction(self.handle.as_ptr()) };
let arch = self.function().arch();
unsafe {
- Some(llil::Function::ref_from_raw(
+ Some(LowLevelILFunction::ref_from_raw(
arch,
NonNull::new(result)?.as_ptr(),
))
}
}
- pub fn set_llil_function<F: FunctionForm>(
+ // TODO: This returns LiftedNonSSA because the lifting code was written before we could patch the IL
+ // TODO: At some point we need to take the lifting code and make it available to regular IL.
+ pub fn set_llil_function<V: NonSSAVariant>(
&self,
- value: &llil::Function<CoreArchitecture, Mutable, F>,
+ value: &LowLevelILFunction<CoreArchitecture, Mutable, NonSSA<V>>,
) {
unsafe { BNSetLowLevelILFunction(self.handle.as_ptr(), value.handle) }
}
- /// MediumLevelILFunction used to represent Medium Level IL
- pub fn mlil_function(&self) -> Option<Ref<mlil::MediumLevelILFunction>> {
+ /// [`MediumLevelILFunction`] used to represent Medium Level IL
+ pub fn mlil_function(&self) -> Option<Ref<MediumLevelILFunction>> {
let result = unsafe { BNAnalysisContextGetMediumLevelILFunction(self.handle.as_ptr()) };
unsafe {
- Some(mlil::MediumLevelILFunction::ref_from_raw(
+ Some(MediumLevelILFunction::ref_from_raw(
NonNull::new(result)?.as_ptr(),
))
}
}
- pub fn set_mlil_function(&self, value: &mlil::MediumLevelILFunction) {
+ pub fn set_mlil_function(&self, value: &MediumLevelILFunction) {
unsafe { BNSetMediumLevelILFunction(self.handle.as_ptr(), value.handle) }
}
- /// HighLevelILFunction used to represent High Level IL
- pub fn hlil_function(&self, full_ast: bool) -> Option<Ref<hlil::HighLevelILFunction>> {
+ /// [`HighLevelILFunction`] used to represent High Level IL
+ pub fn hlil_function(&self, full_ast: bool) -> Option<Ref<HighLevelILFunction>> {
let result = unsafe { BNAnalysisContextGetHighLevelILFunction(self.handle.as_ptr()) };
unsafe {
- Some(hlil::HighLevelILFunction::ref_from_raw(
+ Some(HighLevelILFunction::ref_from_raw(
NonNull::new(result)?.as_ptr(),
full_ast,
))
@@ -126,7 +129,7 @@ impl AnalysisContext {
where
I: IntoIterator<Item = BasicBlock<NativeBlock>>,
{
- let blocks: Vec<_> = blocks.into_iter().map(|block| block).collect();
+ let blocks: Vec<_> = blocks.into_iter().collect();
let mut blocks_raw: Vec<*mut BNBasicBlock> =
blocks.iter().map(|block| block.handle).collect();
unsafe { BNSetBasicBlockList(self.handle.as_ptr(), blocks_raw.as_mut_ptr(), blocks.len()) }
@@ -192,7 +195,7 @@ impl Activity {
ctxt: *mut c_void,
analysis: *mut BNAnalysisContext,
) {
- let ctxt: &mut F = core::mem::transmute(ctxt);
+ let ctxt = &mut *(ctxt as *mut F);
if let Some(analysis) = NonNull::new(analysis) {
ctxt(&AnalysisContext::from_raw(analysis))
}
@@ -236,22 +239,6 @@ unsafe impl RefCountable for Activity {
}
}
-pub trait IntoActivityName {
- fn activity_name(self) -> BnString;
-}
-
-impl IntoActivityName for &Activity {
- fn activity_name(self) -> BnString {
- self.name()
- }
-}
-
-impl<S: BnStrCompatible> IntoActivityName for S {
- fn activity_name(self) -> BnString {
- BnString::new(self)
- }
-}
-
// TODO: We need to hide the JSON here behind a sensible/typed API.
#[repr(transparent)]
pub struct Workflow {
@@ -269,7 +256,7 @@ impl Workflow {
/// Create a new unregistered [Workflow] with no activities.
///
- /// To get a copy of an existing registered [Workflow] use [Workflow::new_from_copy].
+ /// To get a copy of an existing registered [Workflow] use [Workflow::clone].
pub fn new<S: BnStrCompatible>(name: S) -> Self {
let name = name.into_bytes_with_nul();
let result = unsafe { BNCreateWorkflow(name.as_ref().as_ptr() as *const c_char) };
@@ -280,8 +267,8 @@ impl Workflow {
///
/// * `name` - the name for the new [Workflow]
#[must_use]
- pub fn new_from_copy<S: BnStrCompatible + Clone>(name: S) -> Workflow {
- Self::new_from_copy_with_root(name, "")
+ pub fn clone<S: BnStrCompatible + Clone>(&self, name: S) -> Workflow {
+ self.clone_with_root(name, "")
}
/// Make a new unregistered [Workflow], copying all activities, within `root_activity`, and the execution strategy.
@@ -289,19 +276,17 @@ impl Workflow {
/// * `name` - the name for the new [Workflow]
/// * `root_activity` - perform the clone operation with this activity as the root
#[must_use]
- pub fn new_from_copy_with_root<S: BnStrCompatible + Clone, A: IntoActivityName>(
+ pub fn clone_with_root<S: BnStrCompatible, A: BnStrCompatible>(
+ &self,
name: S,
root_activity: A,
) -> Workflow {
- let raw_name = name.clone().into_bytes_with_nul();
- let activity = root_activity.activity_name();
- // I can't think of a single reason as to why we should let users pass a workflow handle into this.
- // To prevent warning being emitted we default to the name.
- let placeholder_workflow = Workflow::instance(name);
+ let raw_name = name.into_bytes_with_nul();
+ let activity = root_activity.into_bytes_with_nul();
unsafe {
Self::from_raw(
NonNull::new(BNWorkflowClone(
- placeholder_workflow.handle.as_ptr(),
+ self.handle.as_ptr(),
raw_name.as_ref().as_ptr() as *const c_char,
activity.as_ref().as_ptr() as *const c_char,
))
@@ -341,7 +326,12 @@ impl Workflow {
/// * `configuration` - a JSON representation of the workflow configuration
pub fn register_with_config<S: BnStrCompatible>(&self, config: S) -> Result<(), ()> {
let config = config.into_bytes_with_nul();
- if unsafe { BNRegisterWorkflow(self.handle.as_ptr(), config.as_ref().as_ptr() as *const c_char) } {
+ if unsafe {
+ BNRegisterWorkflow(
+ self.handle.as_ptr(),
+ config.as_ref().as_ptr() as *const c_char,
+ )
+ } {
Ok(())
} else {
Err(())
@@ -366,14 +356,16 @@ impl Workflow {
) -> Result<Activity, ()>
where
I: IntoIterator,
- I::Item: IntoActivityName,
+ I::Item: BnStrCompatible,
{
- let subactivities_raw: Vec<BnString> = subactivities
+ let subactivities_raw: Vec<_> = subactivities
.into_iter()
- .map(|x| x.activity_name())
+ .map(|x| x.into_bytes_with_nul())
+ .collect();
+ let mut subactivities_ptr: Vec<*const _> = subactivities_raw
+ .iter()
+ .map(|x| x.as_ref().as_ptr() as *const c_char)
.collect();
- let mut subactivities_ptr: Vec<*const _> =
- subactivities_raw.iter().map(|x| x.as_ptr()).collect();
let result = unsafe {
BNWorkflowRegisterActivity(
self.handle.as_ptr(),
@@ -387,17 +379,31 @@ impl Workflow {
}
/// Determine if an Activity exists in this [Workflow].
- pub fn contains<A: IntoActivityName>(&self, activity: A) -> bool {
- unsafe { BNWorkflowContains(self.handle.as_ptr(), activity.activity_name().as_ptr()) }
+ pub fn contains<A: BnStrCompatible>(&self, activity: A) -> bool {
+ unsafe {
+ BNWorkflowContains(
+ self.handle.as_ptr(),
+ activity.into_bytes_with_nul().as_ref().as_ptr() as *const c_char,
+ )
+ }
+ }
+
+ /// Retrieve the configuration as an adjacency list in JSON for the [Workflow].
+ pub fn configuration(&self) -> BnString {
+ self.configuration_with_activity("")
}
/// Retrieve the configuration as an adjacency list in JSON for the
- /// [Workflow], or if specified just for the given `activity`.
+ /// [Workflow], just for the given `activity`.
///
- /// `activity` - if specified, return the configuration for the `activity`
- pub fn configuration<A: IntoActivityName>(&self, activity: A) -> BnString {
- let result =
- unsafe { BNWorkflowGetConfiguration(self.handle.as_ptr(), activity.activity_name().as_ptr()) };
+ /// `activity` - return the configuration for the `activity`
+ pub fn configuration_with_activity<A: BnStrCompatible>(&self, activity: A) -> BnString {
+ let result = unsafe {
+ BNWorkflowGetConfiguration(
+ self.handle.as_ptr(),
+ activity.into_bytes_with_nul().as_ref().as_ptr() as *const c_char,
+ )
+ };
assert!(!result.is_null());
unsafe { BnString::from_raw(result) }
}
@@ -415,7 +421,10 @@ impl Workflow {
pub fn activity<A: BnStrCompatible>(&self, name: A) -> Option<Activity> {
let name = name.into_bytes_with_nul();
let result = unsafe {
- BNWorkflowGetActivity(self.handle.as_ptr(), name.as_ref().as_ptr() as *const c_char)
+ BNWorkflowGetActivity(
+ self.handle.as_ptr(),
+ name.as_ref().as_ptr() as *const c_char,
+ )
};
NonNull::new(result).map(|a| unsafe { Activity::from_raw(a) })
}
@@ -424,10 +433,14 @@ impl Workflow {
/// specified just for the given `activity`.
///
/// * `activity` - if specified, return the roots for the `activity`
- pub fn activity_roots<A: IntoActivityName>(&self, activity: A) -> Array<BnString> {
+ pub fn activity_roots<A: BnStrCompatible>(&self, activity: A) -> Array<BnString> {
let mut count = 0;
let result = unsafe {
- BNWorkflowGetActivityRoots(self.handle.as_ptr(), activity.activity_name().as_ptr(), &mut count)
+ BNWorkflowGetActivityRoots(
+ self.handle.as_ptr(),
+ activity.into_bytes_with_nul().as_ref().as_ptr() as *const c_char,
+ &mut count,
+ )
};
assert!(!result.is_null());
unsafe { Array::new(result as *mut *mut c_char, count, ()) }
@@ -437,7 +450,7 @@ impl Workflow {
///
/// * `activity` - if specified, return the direct children and optionally the descendants of the `activity` (includes `activity`)
/// * `immediate` - whether to include only direct children of `activity` or all descendants
- pub fn subactivities<A: IntoActivityName>(
+ pub fn subactivities<A: BnStrCompatible>(
&self,
activity: A,
immediate: bool,
@@ -446,7 +459,7 @@ impl Workflow {
let result = unsafe {
BNWorkflowGetSubactivities(
self.handle.as_ptr(),
- activity.activity_name().as_ptr(),
+ activity.into_bytes_with_nul().as_ref().as_ptr() as *const c_char,
immediate,
&mut count,
)
@@ -461,20 +474,23 @@ impl Workflow {
/// * `activities` - the list of Activities to assign
pub fn assign_subactivities<A, I>(&self, activity: A, activities: I) -> bool
where
- A: IntoActivityName,
+ A: BnStrCompatible,
I: IntoIterator,
- I::Item: IntoActivityName,
+ I::Item: BnStrCompatible,
{
- let mut input_list: Vec<BnString> =
- activities.into_iter().map(|a| a.activity_name()).collect();
- // SAFETY: this works because BnString and *mut ffi::c_char are
- // transmutable
- let input_list_ptr = input_list.as_mut_ptr() as *mut *const c_char;
+ let input_list: Vec<_> = activities
+ .into_iter()
+ .map(|a| a.into_bytes_with_nul())
+ .collect();
+ let mut input_list_ptr: Vec<*const _> = input_list
+ .iter()
+ .map(|x| x.as_ref().as_ptr() as *const c_char)
+ .collect();
unsafe {
BNWorkflowAssignSubactivities(
self.handle.as_ptr(),
- activity.activity_name().as_ptr(),
- input_list_ptr,
+ activity.into_bytes_with_nul().as_ref().as_ptr() as *const c_char,
+ input_list_ptr.as_mut_ptr(),
input_list.len(),
)
}
@@ -491,35 +507,43 @@ impl Workflow {
/// * `activities` - the list of Activities to insert
pub fn insert<A, I>(&self, activity: A, activities: I) -> bool
where
- A: IntoActivityName,
+ A: BnStrCompatible,
I: IntoIterator,
- I::Item: IntoActivityName,
+ I::Item: BnStrCompatible,
{
- let mut input_list: Vec<BnString> =
- activities.into_iter().map(|a| a.activity_name()).collect();
- // SAFETY: this works because BnString and *mut ffi::c_char are
- // transmutable
- let input_list_ptr = input_list.as_mut_ptr() as *mut *const c_char;
+ let input_list: Vec<_> = activities
+ .into_iter()
+ .map(|a| a.into_bytes_with_nul())
+ .collect();
+ let mut input_list_ptr: Vec<*const _> = input_list
+ .iter()
+ .map(|x| x.as_ref().as_ptr() as *const c_char)
+ .collect();
unsafe {
BNWorkflowInsert(
self.handle.as_ptr(),
- activity.activity_name().as_ptr(),
- input_list_ptr,
+ activity.into_bytes_with_nul().as_ref().as_ptr() as *const c_char,
+ input_list_ptr.as_mut_ptr(),
input_list.len(),
)
}
}
/// Remove the specified `activity`
- pub fn remove<A: IntoActivityName>(&self, activity: A) -> bool {
- unsafe { BNWorkflowRemove(self.handle.as_ptr(), activity.activity_name().as_ptr()) }
+ pub fn remove<A: BnStrCompatible>(&self, activity: A) -> bool {
+ unsafe {
+ BNWorkflowRemove(
+ self.handle.as_ptr(),
+ activity.into_bytes_with_nul().as_ref().as_ptr() as *const c_char,
+ )
+ }
}
/// Replace the specified `activity`.
///
/// * `activity` - the Activity to replace
/// * `new_activity` - the replacement Activity
- pub fn replace<A: IntoActivityName, N: IntoActivityName>(
+ pub fn replace<A: BnStrCompatible, N: BnStrCompatible>(
&self,
activity: A,
new_activity: N,
@@ -527,8 +551,8 @@ impl Workflow {
unsafe {
BNWorkflowReplace(
self.handle.as_ptr(),
- activity.activity_name().as_ptr(),
- new_activity.activity_name().as_ptr(),
+ activity.into_bytes_with_nul().as_ref().as_ptr() as *const c_char,
+ new_activity.into_bytes_with_nul().as_ref().as_ptr() as *const c_char,
)
}
}
@@ -537,15 +561,20 @@ impl Workflow {
///
/// * `activity` - if specified, generate the Flowgraph using `activity` as the root
/// * `sequential` - whether to generate a **Composite** or **Sequential** style graph
- pub fn graph<A: IntoActivityName>(
+ pub fn graph<A: BnStrCompatible>(
&self,
activity: A,
sequential: Option<bool>,
) -> Option<FlowGraph> {
let sequential = sequential.unwrap_or(false);
- let activity_name = activity.activity_name();
- let graph =
- unsafe { BNWorkflowGetGraph(self.handle.as_ptr(), activity_name.as_ptr(), sequential) };
+ let activity_name = activity.into_bytes_with_nul();
+ let graph = unsafe {
+ BNWorkflowGetGraph(
+ self.handle.as_ptr(),
+ activity_name.as_ref().as_ptr() as *const c_char,
+ sequential,
+ )
+ };
if graph.is_null() {
return None;
}
@@ -554,17 +583,17 @@ impl Workflow {
/// Not yet implemented.
pub fn show_metrics(&self) {
- unsafe { BNWorkflowShowReport(self.handle.as_ptr(), b"metrics\x00".as_ptr() as *const c_char) }
+ unsafe { BNWorkflowShowReport(self.handle.as_ptr(), c"metrics".as_ptr()) }
}
/// Show the Workflow topology in the UI.
pub fn show_topology(&self) {
- unsafe { BNWorkflowShowReport(self.handle.as_ptr(), b"topology\x00".as_ptr() as *const c_char) }
+ unsafe { BNWorkflowShowReport(self.handle.as_ptr(), c"topology".as_ptr()) }
}
/// Not yet implemented.
pub fn show_trace(&self) {
- unsafe { BNWorkflowShowReport(self.handle.as_ptr(), b"trace\x00".as_ptr() as *const c_char) }
+ unsafe { BNWorkflowShowReport(self.handle.as_ptr(), c"trace".as_ptr()) }
}
}