summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRyan Snyder <ryan@vector35.com>2021-01-21 18:27:48 +0000
committerKyleMiles <krm504@nyu.edu>2021-01-21 19:06:55 +0000
commitd3140edec185f47235b9e4642bdd56d6c585a341 (patch)
treea61859c29e4e3539daea2b761bb1439d942beaf4
parentc0ddbf0c76d3f1bb7a2b2024f749afc8b9482575 (diff)
This is a combination of 23 commits, the work of Ryan Snyder:
Initial fresh repo Add support for recent calling convention API updates and folds the binaryninjacore-sys crate directly into this one. Add support for auto function analysis suppression Finish moving binaryninjacore-sys back into this crate Update for Symbol/Segment core API changes Update for Symbol API cleanup api: advance submodule reference, support Token changes arch/lifting: support for flags in custom architectures arch/lifting: support default flag write behaviors, handle more ops build: enable headless binary support on MacOS via evil hack bv: add BinaryView wrapper support, remove wrong comment api: update to latest binja dev branch support deps: bump dep versions rust: bump to 2018 edition api: bump to avoid cargo submodule brokenness build: improve binaryninja path detection; enable linux linkhack bv: stub for bv load settings arch: fix flag related crash, minor llil update api: update for recent changes macos: disable linkhack briefly
-rw-r--r--.gitignore10
-rw-r--r--README.md4
-rw-r--r--rust/Cargo.toml11
-rw-r--r--rust/LICENSE13
-rw-r--r--rust/binaryninjacore-sys/Cargo.toml8
-rw-r--r--rust/binaryninjacore-sys/build.rs130
-rw-r--r--rust/binaryninjacore-sys/linkhack/linkhack.c20
-rw-r--r--rust/binaryninjacore-sys/src/lib.rs6
-rw-r--r--rust/binaryninjacore-sys/wrapper.hpp1
-rw-r--r--rust/src/architecture.rs1885
-rw-r--r--rust/src/basicblock.rs274
-rw-r--r--rust/src/binaryview.rs1340
-rw-r--r--rust/src/callingconvention.rs657
-rw-r--r--rust/src/command.rs263
-rw-r--r--rust/src/ffi.rs13
-rw-r--r--rust/src/fileaccessor.rs78
-rw-r--r--rust/src/filemetadata.rs174
-rw-r--r--rust/src/function.rs260
-rw-r--r--rust/src/headless.rs68
-rw-r--r--rust/src/lib.rs171
-rw-r--r--rust/src/llil/block.rs91
-rw-r--r--rust/src/llil/expression.rs816
-rw-r--r--rust/src/llil/function.rs198
-rw-r--r--rust/src/llil/instruction.rs175
-rw-r--r--rust/src/llil/lifting.rs1292
-rw-r--r--rust/src/llil/mod.rs80
-rw-r--r--rust/src/llil/operation.rs765
-rw-r--r--rust/src/platform.rs227
-rw-r--r--rust/src/rc.rs361
-rw-r--r--rust/src/section.rs251
-rw-r--r--rust/src/segment.rs190
-rw-r--r--rust/src/settings.rs49
-rw-r--r--rust/src/string.rs201
-rw-r--r--rust/src/symbol.rs241
-rw-r--r--rust/src/types.rs109
35 files changed, 10432 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index af00d5a3..18784fc3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -73,3 +73,13 @@ build
generator
types/
plugins/
+
+# Rust
+rust/**/*.swp
+rust/**/*.rs.bk
+rust/Cargo.lock
+rust/target/
+rust/binaryninjacore-sys/Cargo.lock
+rust/binaryninjacore-sys/target/
+rust/examples/*/Cargo.lock
+rust/examples/*/target/
diff --git a/README.md b/README.md
index 37522603..982c4d0a 100644
--- a/README.md
+++ b/README.md
@@ -65,3 +65,7 @@ There are many examples available. The [Python examples folder ](https://github.
* [print_syscalls](https://github.com/Vector35/binaryninja-api/tree/dev/examples/print_syscalls) is a standalone executable that prints the syscalls used in a given binary (only usable with licenses that support headless API access)
* [triage](https://github.com/Vector35/binaryninja-api/tree/dev/examples/triage) is a fully featured plugin that is shipped and enabled by default, demonstrating how to do a wide variety of tasks including extending the UI through QT
* [x86 extension](https://github.com/Vector35/binaryninja-api/tree/dev/examples/x86_extension) creates an architecture extension which shows how to modify the behavior of the build-in architectures without creating a complete replacement
+
+## Licensing
+
+Some components may be released under compatible but slightly different open source licenses and will have their own LICENSE file as appropriate.
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
new file mode 100644
index 00000000..d5e5e639
--- /dev/null
+++ b/rust/Cargo.toml
@@ -0,0 +1,11 @@
+[package]
+name = "binja"
+version = "0.1.0"
+authors = ["Ryan Snyder <ryan.snyder.or@gmail.com>"]
+edition = "2018"
+
+[dependencies]
+log = "0.4"
+libc = "0.2"
+rayon = { version = "1.0", optional = true }
+binaryninjacore-sys = { path = "binaryninjacore-sys" }
diff --git a/rust/LICENSE b/rust/LICENSE
new file mode 100644
index 00000000..265883c5
--- /dev/null
+++ b/rust/LICENSE
@@ -0,0 +1,13 @@
+Copyright 2021 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.
diff --git a/rust/binaryninjacore-sys/Cargo.toml b/rust/binaryninjacore-sys/Cargo.toml
new file mode 100644
index 00000000..5b4cc063
--- /dev/null
+++ b/rust/binaryninjacore-sys/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "binaryninjacore-sys"
+version = "0.1.0"
+authors = ["Ryan Snyder <ryan.snyder.or@gmail.com>"]
+build = "build.rs"
+
+[build-dependencies]
+bindgen = "0.49"
diff --git a/rust/binaryninjacore-sys/build.rs b/rust/binaryninjacore-sys/build.rs
new file mode 100644
index 00000000..b151fc3b
--- /dev/null
+++ b/rust/binaryninjacore-sys/build.rs
@@ -0,0 +1,130 @@
+extern crate bindgen;
+
+use std::io::prelude::*;
+use std::io::BufReader;
+use std::path::PathBuf;
+use std::fs::File;
+use std::env;
+
+#[cfg(target_os = "macos")]
+static LASTRUN_PATH: (&str, &str) = ("HOME", "Library/Application Support/Binary Ninja/lastrun");
+
+#[cfg(target_os = "linux")]
+static LASTRUN_PATH: (&str, &str) = ("HOME", ".binaryninja/lastrun");
+
+#[cfg(windows)]
+static LASTRUN_PATH: (&str, &str) = ("APPDATA", "Binary Ninja\\lastrun");
+
+fn link_path() -> PathBuf {
+ let home = PathBuf::from(env::var(LASTRUN_PATH.0).unwrap());
+ let lastrun = PathBuf::from(&home)
+ .join(LASTRUN_PATH.1);
+
+ File::open(lastrun).and_then(|f| {
+ let mut binja_path = String::new();
+ let mut reader = BufReader::new(f);
+
+ reader.read_line(&mut binja_path)?;
+ Ok(PathBuf::from(binja_path.trim()))
+ }).unwrap_or_else(|_| {
+ #[cfg(target_os = "macos")]
+ return PathBuf::from("/Applications/Binary Ninja.app/Contents/MacOS");
+
+ #[cfg(target_os = "linux")]
+ return home.join("binaryninja");
+
+ #[cfg(windows)]
+ return PathBuf::from(env::var("PROGRAMFILES").unwrap()).join("Vector35\\BinaryNinja\\");
+ })
+}
+
+fn main() {
+ // Allow the library search path to be overridden for internal dev builds, but
+ // otherwise search the usual install paths
+ let out_dir = env::var("OUT_DIR").unwrap();
+ let link_path = env::var("BINARYNINJADIR")
+ .map(PathBuf::from)
+ .unwrap_or_else(|_| link_path());
+
+ #[cfg(unix)]
+ {
+ use std::process::Command;
+
+ // Spectacularly evil linking hack due to Cargo shortcomings. In a nutshell,
+ // due to the inability for our .rlib crate to contribute linker arguments
+ // (or even just an rpath!) to our consumers, any resulting binaries will
+ // fail to run unless loaded into a process that already has the core loaded.
+ //
+ // For plugins this restriction is fine as they'll only be loaded by the core;
+ // headless binaries on the other hand (consider `cargo test`, too!) will fail
+ // because libbinaryninjacore is not in the library path on pretty much any
+ // system anywhere. If only we could trick the linker into linking with an
+ // absolute path...
+ //
+ // LOOK UPON MY WORKS, YE MIGHTY, AND DESPAIR
+ //
+ // We build a simple do-nothing library called liblinkhack and add it as a
+ // native dependency. Crucially, we ensure that LC_ID_DYLIB (macos) or DT_SONAME
+ // (linux) is set to the absolute path of libbinaryninjacore. This has the effect
+ // that any binary attempting to link with liblinkhack will have its dependency
+ // on liblinkhack recorded as the path to the binaryninja core. Later on, the
+ // actual core will get linked and that dependency will be recorded in the normal
+ // way.
+ //
+ // tl;dr we're linking against a fake, shim library that actually results in
+ // the binaryninja core getting linked twice, once with an absolute path and once
+ // correctly. yes, this is as horrifying as you think it is. no, there is no other
+ // way to make `cargo test` work.
+ //
+ // I'm not happy about it either.
+/*
+ #[cfg(target_os = "macos")]
+ Command::new("clang")
+ .args(&["-Wl,-dylib", "-o"])
+ .arg(&format!("{}/liblinkhack.dylib", out_dir))
+ .arg(&format!("-Wl,-install_name,{}/libbinaryninjacore.dylib", &link_path.to_str().unwrap()))
+ .arg(&format!("{}/linkhack/linkhack.c", env::var("CARGO_MANIFEST_DIR").unwrap()))
+ .status().unwrap();
+ */
+
+ #[cfg(target_os = "linux")]
+ {
+ Command::new("gcc")
+ .args(&["-shared", "-o"])
+ .arg(&format!("{}/liblinkhack.so", out_dir))
+ .arg(&format!("-Wl,-soname,{}/libbinaryninjacore.so.1", &link_path.to_str().unwrap()))
+ .arg(&format!("{}/linkhack/linkhack.c", env::var("CARGO_MANIFEST_DIR").unwrap()))
+ .status().unwrap();
+
+ // Linux builds of binaryninja ship with libbinaryninjacore.so.1 in the
+ // application folder and no symlink. The linker will attempt to link with
+ // libbinaryninjacore.so. Since this is likely going to fail, we detect this
+ // ahead of time and create an appropriately named symlink inside of OUT_DIR
+ // and add it to the library search path.
+ let symlink_target = PathBuf::from(&out_dir).join("libbinaryninjacore.so");
+ if !link_path.join("libbinaryninjacore.so").exists() && !symlink_target.exists() {
+ use std::os::unix::fs;
+ fs::symlink(link_path.join("libbinaryninjacore.so.1"), PathBuf::from(&out_dir).join("libbinaryninjacore.so"))
+ .expect("failed to create required symlink");
+ }
+ }
+
+ //println!("cargo:rustc-link-lib=linkhack");
+ //println!("cargo:rustc-link-search={}", out_dir);
+ }
+
+ println!("cargo:rustc-link-lib=binaryninjacore");
+ println!("cargo:rustc-link-search={}", link_path.to_str().unwrap());
+
+ let bindings = bindgen::builder().header("wrapper.hpp")
+ .generate_comments(false)
+ .whitelist_function("BN.*")
+ .rustified_enum("BN.*")
+ .rustfmt_bindings(false) // required; things break otherwise
+ .generate()
+ .expect("Unable to generate bindings");
+
+ bindings
+ .write_to_file(PathBuf::from(out_dir).join("bindings.rs"))
+ .expect("Couldn't write bindings!");
+}
diff --git a/rust/binaryninjacore-sys/linkhack/linkhack.c b/rust/binaryninjacore-sys/linkhack/linkhack.c
new file mode 100644
index 00000000..6d4b3f37
--- /dev/null
+++ b/rust/binaryninjacore-sys/linkhack/linkhack.c
@@ -0,0 +1,20 @@
+// Have some overlap with likely-called functions so ld on
+// linux doesn't skip linking liblinkhack
+void BNLog() {}
+void BNLogDebug() {}
+void BNLogInfo() {}
+void BNLogWarn() {}
+void BNLogError() {}
+void BNLogAlert() {}
+void BNShutdown() {}
+void BNNewViewReference() {}
+void BNFreeBinaryView() {}
+void BNInitCorePlugins() {}
+void BNAllocString() {}
+void BNFreeString() {}
+void BNRegisterBinaryViewType() {}
+void BNGetArchitectureList() {}
+void BNNewFunctionReference() {}
+void BNFreeFunction() {}
+void BNGetFunctionBasicBlockList() {}
+void BNRegisterArchitecture() {}
diff --git a/rust/binaryninjacore-sys/src/lib.rs b/rust/binaryninjacore-sys/src/lib.rs
new file mode 100644
index 00000000..04d76a43
--- /dev/null
+++ b/rust/binaryninjacore-sys/src/lib.rs
@@ -0,0 +1,6 @@
+#![allow(non_upper_case_globals)]
+#![allow(non_camel_case_types)]
+#![allow(non_snake_case)]
+#![allow(unused)]
+
+include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
diff --git a/rust/binaryninjacore-sys/wrapper.hpp b/rust/binaryninjacore-sys/wrapper.hpp
new file mode 100644
index 00000000..263de01c
--- /dev/null
+++ b/rust/binaryninjacore-sys/wrapper.hpp
@@ -0,0 +1 @@
+#include "binaryninja-api/binaryninjacore.h"
diff --git a/rust/src/architecture.rs b/rust/src/architecture.rs
new file mode 100644
index 00000000..fc56140c
--- /dev/null
+++ b/rust/src/architecture.rs
@@ -0,0 +1,1885 @@
+// container abstraction to avoid Vec<> (want CoreArchFlagList, CoreArchRegList)
+// RegisterInfo purge
+use binaryninjacore_sys::*;
+
+use std::ffi::{CString, CStr};
+use std::ptr;
+use std::ops::Drop;
+use std::borrow::{Borrow, Cow};
+use std::slice;
+use std::ops;
+use std::mem::zeroed;
+use std::hash::Hash;
+use std::collections::HashMap;
+
+use crate::{BranchType, Endianness};
+use crate::callingconvention::CallingConvention;
+use crate::platform::Platform;
+
+use crate::llil::{Lifter, LiftedExpr, FlagWriteOp};
+use crate::llil::{get_default_flag_write_llil, get_default_flag_cond_llil};
+
+use crate::string::*;
+use crate::rc::*;
+
+pub enum BranchInfo {
+ Unconditional(u64),
+ False(u64),
+ True(u64),
+ Call(u64),
+ FunctionReturn,
+ SystemCall,
+ Indirect,
+ Exception,
+ Unresolved,
+}
+
+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::*;
+
+ 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)) };
+
+ 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,
+ };
+
+ Some((res, arch))
+ }
+ _ => None,
+ }
+ }
+}
+
+#[repr(C)]
+pub struct InstructionInfo(BNInstructionInfo);
+impl InstructionInfo {
+ pub fn new(len: usize, branch_delay: bool) -> Self {
+ InstructionInfo (
+ BNInstructionInfo {
+ length: len,
+ archTransitionByTargetAddr: false,
+ branchDelay: branch_delay,
+ branchCount: 0usize,
+ branchType: [BranchType::UnresolvedBranch; 3],
+ branchTarget: [0u64; 3],
+ branchArch: [ptr::null_mut(); 3],
+ }
+ )
+ }
+
+ pub fn len(&self) -> usize { self.0.length }
+ pub fn branch_count(&self) -> usize { self.0.branchCount }
+ pub fn branch_delay(&self) -> bool { self.0.branchDelay }
+ pub fn branches(&self) -> BranchIter { BranchIter(self, 0 .. self.branch_count()) }
+
+ pub fn allow_arch_transition_by_target_addr(&mut self, transition: bool) {
+ self.0.archTransitionByTargetAddr = transition;
+ }
+
+ 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,
+ };
+
+ 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!");
+ }
+ }
+}
+
+pub enum InstructionTextTokenContents {
+ Text,
+ Instruction,
+ OperandSeparator,
+ Register,
+ Integer(u64), // TODO size?
+ PossibleAddress(u64), // TODO size?
+ BeginMemoryOperand,
+ EndMemoryOperand,
+ FloatingPoint,
+ CodeRelativeAddress(u64),
+}
+
+pub use binaryninjacore_sys::BNInstructionTextTokenContext as InstructionTextTokenContext;
+
+#[repr(C)]
+pub struct InstructionTextToken(BNInstructionTextToken);
+impl InstructionTextToken {
+ pub fn new<T: Into<Vec<u8>>>(contents: InstructionTextTokenContents, text: T) -> Self {
+ use self::BNInstructionTextTokenType::*;
+ use self::InstructionTextTokenContents::*;
+
+ let mut res: BNInstructionTextToken = unsafe { zeroed() };
+
+ res.context = InstructionTextTokenContext::NoTokenContext;
+ res.address = 0;
+ res.size = 0; // TODO supply? x86 seems to, others don't...
+ res.operand = 0xffff_ffff;
+ res.confidence = 0xff;
+
+ match contents {
+ Integer(v) => res.value = v,
+ PossibleAddress(v) |
+ CodeRelativeAddress(v) => {
+ res.value = v;
+ res.address = v;
+ }
+ _ => {},
+ }
+
+ res.type_ = match contents {
+
+ Text => TextToken,
+ Instruction => InstructionToken,
+ OperandSeparator => OperandSeparatorToken,
+ Register => RegisterToken,
+ Integer(_) => IntegerToken,
+ PossibleAddress(_) => PossibleAddressToken,
+ BeginMemoryOperand => BeginMemoryOperandToken,
+ EndMemoryOperand => EndMemoryOperandToken,
+ FloatingPoint => FloatingPointToken,
+ CodeRelativeAddress(_) => CodeRelativeAddressToken,
+ };
+
+ res.text = CString::new(text).unwrap().into_raw();
+
+ InstructionTextToken(res)
+ }
+
+ pub fn text(&self) -> &CStr {
+ unsafe { CStr::from_ptr(self.0.text) }
+ }
+
+ pub fn contents(&self) -> InstructionTextTokenContents {
+ use self::BNInstructionTextTokenType::*;
+ use self::InstructionTextTokenContents::*;
+
+ 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"),
+ }
+ }
+
+ pub fn context(&self) -> InstructionTextTokenContext {
+ self.0.context
+ }
+
+ pub fn size(&self) -> usize {
+ self.0.size
+ }
+
+ pub fn operand(&self) -> usize {
+ self.0.operand
+ }
+
+ pub fn address(&self) -> u64 {
+ self.0.address
+ }
+}
+
+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,
+ width: 0,
+ text: self.text().to_owned().into_raw(),
+ confidence: 0xff,
+ typeNames: ptr::null_mut(),
+ namesCount: 0,
+ }
+ )
+ }
+}
+
+impl Drop for InstructionTextToken {
+ fn drop(&mut self) {
+ let _owned = unsafe { CString::from_raw(self.0.text) };
+ }
+}
+
+pub use binaryninjacore_sys::BNImplicitRegisterExtend as ImplicitRegisterExtend;
+pub use binaryninjacore_sys::BNLowLevelILFlagCondition as FlagCondition;
+pub use binaryninjacore_sys::BNFlagRole as FlagRole;
+
+pub trait RegisterInfo: Sized {
+ type RegType: Register<InfoType=Self>;
+
+ fn parent(&self) -> Option<Self::RegType>;
+ fn size(&self) -> usize;
+ fn offset(&self) -> usize;
+ fn implicit_extend(&self) -> ImplicitRegisterExtend;
+}
+
+pub trait Register: Sized + Clone + Copy {
+ type InfoType: RegisterInfo<RegType=Self>;
+
+ fn name(&self) -> Cow<str>;
+ fn info(&self) -> Self::InfoType;
+
+ /// Unique identifier for this `Register`.
+ ///
+ /// *MUST* be in the range [0, 0x7fff_ffff]
+ fn id(&self) -> u32;
+}
+
+pub trait Flag: Sized + Clone + Copy {
+ type FlagClass: FlagClass;
+
+ fn name(&self) -> Cow<str>;
+ fn role(&self, class: Option<Self::FlagClass>) -> FlagRole;
+
+ /// Unique identifier for this `Flag`.
+ ///
+ /// *MUST* be in the range [0, 0x7fff_ffff]
+ fn id(&self) -> u32;
+}
+
+pub trait FlagWrite: Sized + Clone + Copy {
+ type FlagType: Flag;
+ type FlagClass: FlagClass;
+
+ fn name(&self) -> Cow<str>;
+ fn class(&self) -> Option<Self::FlagClass>;
+
+ /// Unique identifier for this `FlagWrite`.
+ ///
+ /// *MUST NOT* be 0.
+ /// *MUST* be in the range [1, 0x7fff_ffff]
+ fn id(&self) -> u32;
+
+ fn flags_written(&self) -> Vec<Self::FlagType>;
+}
+
+pub trait FlagClass: Sized + Clone + Copy + Hash + Eq {
+ fn name(&self) -> Cow<str>;
+
+ /// Unique identifier for this `FlagClass`.
+ ///
+ /// *MUST NOT* be 0.
+ /// *MUST* be in the range [1, 0x7fff_ffff]
+ fn id(&self) -> u32;
+}
+
+pub trait FlagGroup: Sized + Clone + Copy {
+ type FlagType: Flag;
+ type FlagClass: FlagClass;
+
+ fn name(&self) -> Cow<str>;
+
+ /// Unique identifier for this `FlagGroup`.
+ ///
+ /// *MUST* be in the range [0, 0x7fff_ffff]
+ fn id(&self) -> u32;
+
+ /// Returns the list of flags that need to be resolved in order
+ /// to take the clean flag resolution path -- at time of writing,
+ /// all required flags must have been set by the same instruction,
+ /// and the 'querying' instruction must be reachable from *one*
+ /// instruction that sets all of these flags.
+ fn flags_required(&self) -> Vec<Self::FlagType>;
+
+ /// Returns the mapping of Semantic Flag Classes to Flag Conditions,
+ /// in the context of this Flag Group.
+ ///
+ /// Example:
+ ///
+ /// If we have a group representing `cr1_lt` (as in PowerPC), we would
+ /// have multiple Semantic Flag Classes used by the different Flag Write
+ /// Types to represent the different comparisons, so for `cr1_lt` we
+ /// would return a mapping along the lines of:
+ ///
+ /// ```
+ /// cr1_signed -> LLFC_SLT,
+ /// cr1_unsigned -> LLFC_ULT,
+ /// ```
+ ///
+ /// This allows the core to recover the semantics of the comparison and
+ /// inline it into conditional branches when appropriate.
+ fn flag_conditions(&self) -> HashMap<Self::FlagClass, FlagCondition>;
+}
+
+pub trait Architecture: 'static + Sized + AsRef<CoreArchitecture> {
+ type Handle: Borrow<Self> + Clone;
+
+ type RegisterInfo: RegisterInfo<RegType=Self::Register>;
+ type Register: Register<InfoType=Self::RegisterInfo>;
+
+ type Flag: Flag<FlagClass=Self::FlagClass>;
+ type FlagWrite: FlagWrite<FlagType=Self::Flag, FlagClass=Self::FlagClass>;
+ type FlagClass: FlagClass;
+ type FlagGroup: FlagGroup<FlagType=Self::Flag, FlagClass=Self::FlagClass>;
+
+ type InstructionTextContainer: Into<Vec<InstructionTextToken>>;
+
+ fn endianness(&self) -> Endianness;
+ fn address_size(&self) -> usize;
+ fn default_integer_size(&self) -> usize;
+ fn instruction_alignment(&self) -> usize;
+ fn max_instr_len(&self) -> usize;
+ fn opcode_display_len(&self) -> usize;
+
+ fn associated_arch_by_addr(&self, addr: &mut u64) -> CoreArchitecture;
+
+ fn instruction_info(&self, data: &[u8], addr: u64) -> Option<InstructionInfo>;
+ fn instruction_text(&self, data: &[u8], addr: u64) -> Option<(usize, Self::InstructionTextContainer)>;
+ fn instruction_llil(&self, data: &[u8], addr: u64, il: &mut Lifter<Self>) -> Option<(usize, bool)>;
+
+ /// Fallback flag value calculation path. This method is invoked when the core is unable to
+ /// recover flag use semantics, and resorts to emitting instructions that explicitly set each
+ /// observed flag to the value of an expression returned by this function.
+ ///
+ /// This function *MUST NOT* append instructions that have side effects.
+ ///
+ /// This function *MUST NOT* observe the values of other flags.
+ ///
+ /// This function *MUST* return `None` or an expression representing a boolean value.
+ fn flag_write_llil<'a>(&self, flag: Self::Flag, flag_write_type: Self::FlagWrite, op: FlagWriteOp<Self::Register>, il: &'a mut Lifter<Self>)
+ -> Option<LiftedExpr<'a, Self>>
+ {
+ let role = flag.role(flag_write_type.class());
+ Some(get_default_flag_write_llil(self, role, op, il))
+ }
+
+ /// Determines what flags need to be examined in order to attempt automatic recovery of the
+ /// semantics of this flag use.
+ ///
+ /// If automatic recovery is not possible, the `flag_cond_llil` method will be invoked to give
+ /// this `Architecture` implementation arbitrary control over the expression to be evaluated.
+ fn flags_required_for_flag_condition(&self, condition: FlagCondition, class: Option<Self::FlagClass>) -> Vec<Self::Flag>;
+
+ /// This function *MUST NOT* append instructions that have side effects.
+ ///
+ /// This function *MUST NOT* observe the values of flags not returned by
+ /// `flags_required_for_flag_condition`.
+ ///
+ /// This function *MUST* return `None` or an expression representing a boolean value.
+ fn flag_cond_llil<'a>(&self, cond: FlagCondition, class: Option<Self::FlagClass>, il: &'a mut Lifter<Self>)
+ -> Option<LiftedExpr<'a, Self>>
+ {
+ Some(get_default_flag_cond_llil(self, cond, class, il))
+ }
+
+ /// Performs fallback resolution when the core was unable to recover the semantics of a
+ /// `LLIL_FLAG_GROUP` expression. This occurs when multiple instructions may have set the flags
+ /// at the flag group query, or when the `FlagGroup::flag_conditions()` map doesn't have an entry
+ /// for the `FlagClass` associated with the `FlagWrite` type of the expression that last set
+ /// the flags required by the `FlagGroup` `group`.
+ ///
+ /// In this fallback path, the `Architecture` must generate the boolean expression in terms of
+ /// the values of that flags returned by `group`'s `flags_required` method.
+ ///
+ /// This function must return an expression representing a boolean (as in, size of `0`) value.
+ /// It is not allowed to add any instructions that can cause side effects.
+ ///
+ /// This function must not observe the values of any flag not returned by `group`'s
+ /// `flags_required` method.
+ fn flag_group_llil<'a>(&self, group: Self::FlagGroup, il: &'a mut Lifter<Self>) -> Option<LiftedExpr<'a, Self>>;
+
+ fn registers_all(&self) -> Vec<Self::Register>;
+ fn registers_full_width(&self) -> Vec<Self::Register>;
+ fn registers_global(&self) -> Vec<Self::Register>;
+
+ fn flags(&self) -> Vec<Self::Flag>;
+ fn flag_write_types(&self) -> Vec<Self::FlagWrite>;
+ fn flag_classes(&self) -> Vec<Self::FlagClass>;
+ fn flag_groups(&self) -> Vec<Self::FlagGroup>;
+
+
+ fn stack_pointer_reg(&self) -> Option<Self::Register>;
+ fn link_reg(&self) -> Option<Self::Register>;
+
+ fn register_from_id(&self, id: u32) -> Option<Self::Register>;
+ fn flag_from_id(&self, id: u32) -> Option<Self::Flag>;
+ fn flag_write_from_id(&self, id: u32) -> Option<Self::FlagWrite>;
+ fn flag_class_from_id(&self, id: u32) -> Option<Self::FlagClass>;
+ fn flag_group_from_id(&self, id: u32) -> Option<Self::FlagGroup>;
+
+ fn handle(&self) -> Self::Handle;
+}
+
+pub struct CoreRegisterInfo(*mut BNArchitecture, u32, BNRegisterInfo);
+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))
+ } else {
+ None
+ }
+ }
+
+ fn size(&self) -> usize {
+ self.2.size
+ }
+
+ fn offset(&self) -> usize {
+ self.2.offset
+ }
+
+ fn implicit_extend(&self) -> ImplicitRegisterExtend {
+ self.2.extend
+ }
+}
+
+#[derive(Copy, Clone, Eq, PartialEq, Hash)]
+pub struct CoreRegister(*mut BNArchitecture, u32);
+impl Register for CoreRegister {
+ type InfoType = CoreRegisterInfo;
+
+ fn name(&self) -> Cow<str> {
+ unsafe {
+ let name = BNGetArchitectureRegisterName(self.0, self.1);
+
+ // We need to guarantee ownership, as if we're still
+ // a Borrowed variant we're about to free the underlying
+ // memory.
+ let res = CStr::from_ptr(name);
+ let res = res.to_string_lossy().into_owned().into();
+
+ BNFreeString(name);
+
+ res
+ }
+ }
+
+ fn info(&self) -> CoreRegisterInfo {
+ CoreRegisterInfo(self.0, self.1, unsafe {
+ BNGetArchitectureRegisterInfo(self.0, self.1)
+ })
+ }
+
+ fn id(&self) -> u32 {
+ self.1
+ }
+}
+
+#[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);
+
+ // We need to guarantee ownership, as if we're still
+ // a Borrowed variant we're about to free the underlying
+ // memory.
+ let res = CStr::from_ptr(name);
+ let res = res.to_string_lossy().into_owned().into();
+
+ BNFreeString(name);
+
+ res
+ }
+ }
+
+ fn role(&self, class: Option<CoreFlagClass>) -> FlagRole {
+ let class_id = match class {
+ Some(class) => class.1,
+ _ => 0
+ };
+
+ unsafe { BNGetArchitectureFlagRole(self.0, self.1, class_id) }
+ }
+
+ fn id(&self) -> u32 {
+ self.1
+ }
+}
+
+#[derive(Copy, Clone, Eq, PartialEq, Hash)]
+pub struct CoreFlagWrite(*mut BNArchitecture, u32);
+impl FlagWrite for CoreFlagWrite {
+ type FlagType = CoreFlag;
+ type FlagClass = CoreFlagClass;
+
+ fn name(&self) -> Cow<str> {
+ unsafe {
+ let name = BNGetArchitectureFlagWriteTypeName(self.0, self.1);
+
+ // We need to guarantee ownership, as if we're still
+ // a Borrowed variant we're about to free the underlying
+ // memory.
+ let res = CStr::from_ptr(name);
+ let res = res.to_string_lossy().into_owned().into();
+
+ BNFreeString(name);
+
+ res
+ }
+ }
+
+ fn id(&self) -> u32 {
+ self.1
+ }
+
+ 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 _)
+ };
+
+ let ret = unsafe {
+ slice::from_raw_parts_mut(regs, count).iter().map(|reg| CoreFlag(self.0, *reg)).collect()
+ };
+
+ unsafe { BNFreeRegisterList(regs); }
+
+ ret
+ }
+
+ fn class(&self) -> Option<CoreFlagClass> {
+ let class = unsafe { BNGetArchitectureSemanticClassForFlagWriteType(self.0, self.1) };
+
+ match class {
+ 0 => None,
+ id => Some(CoreFlagClass(self.0, id)),
+ }
+ }
+}
+
+#[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);
+
+ // We need to guarantee ownership, as if we're still
+ // a Borrowed variant we're about to free the underlying
+ // memory.
+ let res = CStr::from_ptr(name);
+ let res = res.to_string_lossy().into_owned().into();
+
+ BNFreeString(name);
+
+ res
+ }
+ }
+
+ fn id(&self) -> u32 {
+ self.1
+ }
+}
+
+#[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);
+
+ // We need to guarantee ownership, as if we're still
+ // a Borrowed variant we're about to free the underlying
+ // memory.
+ let res = CStr::from_ptr(name);
+ let res = res.to_string_lossy().into_owned().into();
+
+ BNFreeString(name);
+
+ res
+ }
+ }
+
+ fn id(&self) -> u32 {
+ self.1
+ }
+
+ 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 _)
+ };
+
+ let ret = unsafe {
+ slice::from_raw_parts_mut(regs, count).iter().map(|reg| CoreFlag(self.0, *reg)).collect()
+ };
+
+ unsafe { BNFreeRegisterList(regs); }
+
+ ret
+ }
+
+ fn flag_conditions(&self) -> HashMap<CoreFlagClass, FlagCondition> {
+ let mut count: usize = 0;
+
+ unsafe {
+ let flag_conds = BNGetArchitectureFlagConditionsForSemanticFlagGroup(self.0, self.1, &mut count as *mut _);
+
+ let ret = slice::from_raw_parts_mut(flag_conds, count).iter().map(|class_cond| {
+ (CoreFlagClass(self.0, class_cond.semanticClass), class_cond.condition)
+ }).collect();
+
+ BNFreeFlagConditionsForSemanticFlagGroup(flag_conds);
+
+ ret
+ }
+ }
+}
+
+pub struct CoreArchitectureList(*mut *mut BNArchitecture, usize);
+impl ops::Deref for CoreArchitectureList {
+ type Target = [CoreArchitecture];
+
+ fn deref(&self) -> &Self::Target {
+ unsafe { slice::from_raw_parts_mut(self.0 as *mut CoreArchitecture, self.1) }
+ }
+}
+
+impl Drop for CoreArchitectureList {
+ fn drop(&mut self) {
+ unsafe { BNFreeArchitectureList(self.0); }
+ }
+}
+
+pub struct InstructionTextTokenList(*mut BNInstructionTextToken, usize);
+
+impl ops::Deref for InstructionTextTokenList {
+ type Target = [InstructionTextToken];
+
+ fn deref(&self) -> &Self::Target {
+ unsafe { slice::from_raw_parts(&*(self.0 as *const InstructionTextToken), self.1) }
+ }
+}
+
+impl Drop for InstructionTextTokenList {
+ fn drop(&mut self) {
+ unsafe { BNFreeInstructionText(self.0, self.1) }
+ }
+}
+
+impl Into<Vec<InstructionTextToken>> for InstructionTextTokenList {
+ fn into(self) -> Vec<InstructionTextToken> {
+ self.to_vec()
+ }
+}
+
+#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
+pub struct CoreArchitecture(pub(crate) *mut BNArchitecture);
+
+unsafe impl Send for CoreArchitecture {}
+unsafe impl Sync for CoreArchitecture {}
+
+impl CoreArchitecture {
+ pub(crate) unsafe fn from_raw(raw: *mut BNArchitecture) -> Self {
+ CoreArchitecture(raw)
+ }
+
+ pub fn list_all() -> CoreArchitectureList {
+ let mut count: usize = 0;
+ let archs = unsafe { BNGetArchitectureList(&mut count as *mut _) };
+
+ CoreArchitectureList(archs, count)
+ }
+
+ pub fn by_name<N: Into<Vec<u8>>>(name: N) -> Option<Self> {
+ let name = match CString::new(name) {
+ Ok(s) => s,
+ _ => return None,
+ };
+
+ let res = unsafe { BNGetArchitectureByName(name.as_ptr()) };
+
+ match res.is_null() {
+ false => Some(CoreArchitecture(res)),
+ true => None,
+ }
+ }
+
+ pub fn name(&self) -> BnString {
+ unsafe {
+ BnString::from_raw(BNGetArchitectureName(self.0))
+ }
+ }
+}
+
+impl AsRef<CoreArchitecture> for CoreArchitecture {
+ fn as_ref(&self) -> &Self {
+ self
+ }
+}
+
+impl Architecture for CoreArchitecture {
+ type Handle = Self;
+
+ type RegisterInfo = CoreRegisterInfo;
+ type Register = CoreRegister;
+ type Flag = CoreFlag;
+ type FlagWrite = CoreFlagWrite;
+ type FlagClass = CoreFlagClass;
+ type FlagGroup = CoreFlagGroup;
+
+ type InstructionTextContainer = InstructionTextTokenList;
+
+ fn endianness(&self) -> Endianness {
+ unsafe { BNGetArchitectureEndianness(self.0) }
+ }
+
+ fn address_size(&self) -> usize {
+ unsafe { BNGetArchitectureAddressSize(self.0) }
+ }
+
+ fn default_integer_size(&self) -> usize {
+ unsafe { BNGetArchitectureDefaultIntegerSize(self.0) }
+ }
+
+ fn instruction_alignment(&self) -> usize {
+ unsafe { BNGetArchitectureInstructionAlignment(self.0) }
+ }
+
+ fn max_instr_len(&self) -> usize {
+ unsafe { BNGetArchitectureMaxInstructionLength(self.0) }
+ }
+
+ fn opcode_display_len(&self) -> usize {
+ unsafe { BNGetArchitectureOpcodeDisplayLength(self.0) }
+ }
+
+ fn associated_arch_by_addr(&self, addr: &mut u64) -> CoreArchitecture {
+ let arch = unsafe { BNGetAssociatedArchitectureByAddress(self.0, addr as *mut _) };
+
+ CoreArchitecture(arch)
+ }
+
+ 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)
+ } else {
+ None
+ }
+ }
+
+ fn instruction_text(&self, data: &[u8], addr: u64) -> Option<(usize, InstructionTextTokenList)> {
+ let mut consumed = data.len();
+ let mut count: usize = 0;
+ let mut result: *mut BNInstructionTextToken = ptr::null_mut();
+
+ unsafe {
+ if BNGetInstructionText(self.0, data.as_ptr(), addr, &mut consumed as *mut _, &mut result as *mut _,
+ &mut count as *mut _) {
+ Some((consumed, InstructionTextTokenList(result, count)))
+ } else {
+ None
+ }
+ }
+ }
+
+ fn instruction_llil(&self, _data: &[u8], _addr: u64, _il: &mut Lifter<Self>) -> Option<(usize, bool)> {
+ None
+ }
+
+ fn flag_write_llil<'a>(&self, _flag: Self::Flag, _flag_write: Self::FlagWrite, _op: FlagWriteOp<Self::Register>, _il: &'a mut Lifter<Self>)
+ -> Option<LiftedExpr<'a, Self>>
+ {
+ None
+ }
+
+ fn flag_cond_llil<'a>(&self, _cond: FlagCondition, _class: Option<Self::FlagClass>, _il: &'a mut Lifter<Self>)
+ -> Option<LiftedExpr<'a, Self>>
+ {
+ None
+ }
+
+ fn flag_group_llil<'a>(&self, _group: Self::FlagGroup, _il: &'a mut Lifter<Self>)
+ -> Option<LiftedExpr<'a, Self>>
+ {
+ None
+ }
+
+
+ fn registers_all(&self) -> Vec<CoreRegister> {
+ unsafe {
+ let mut count: usize = 0;
+ let regs = BNGetAllArchitectureRegisters(self.0, &mut count as *mut _);
+
+ let ret = slice::from_raw_parts_mut(regs, count)
+ .iter()
+ .map(|reg| CoreRegister(self.0, *reg))
+ .collect();
+
+ BNFreeRegisterList(regs);
+
+ ret
+ }
+ }
+
+ fn registers_full_width(&self) -> Vec<CoreRegister> {
+ unsafe {
+ let mut count: usize = 0;
+ let regs = BNGetFullWidthArchitectureRegisters(self.0, &mut count as *mut _);
+
+ let ret = slice::from_raw_parts_mut(regs, count)
+ .iter()
+ .map(|reg| CoreRegister(self.0, *reg))
+ .collect();
+
+ BNFreeRegisterList(regs);
+
+ ret
+ }
+ }
+
+ fn registers_global(&self) -> Vec<CoreRegister> {
+ unsafe {
+ let mut count: usize = 0;
+ let regs = BNGetArchitectureGlobalRegisters(self.0, &mut count as *mut _);
+
+ let ret = slice::from_raw_parts_mut(regs, count)
+ .iter()
+ .map(|reg| CoreRegister(self.0, *reg))
+ .collect();
+
+ BNFreeRegisterList(regs);
+
+ ret
+ }
+ }
+
+ fn flags(&self) -> Vec<CoreFlag> {
+ unsafe {
+ let mut count: usize = 0;
+ let regs = BNGetAllArchitectureFlags(self.0, &mut count as *mut _);
+
+ let ret = slice::from_raw_parts_mut(regs, count)
+ .iter()
+ .map(|reg| CoreFlag(self.0, *reg))
+ .collect();
+
+ BNFreeRegisterList(regs);
+
+ ret
+ }
+ }
+
+ fn flag_write_types(&self) -> Vec<CoreFlagWrite> {
+ unsafe {
+ let mut count: usize = 0;
+ let regs = BNGetAllArchitectureFlagWriteTypes(self.0, &mut count as *mut _);
+
+ let ret = slice::from_raw_parts_mut(regs, count)
+ .iter()
+ .map(|reg| CoreFlagWrite(self.0, *reg))
+ .collect();
+
+ BNFreeRegisterList(regs);
+
+ ret
+ }
+ }
+
+ fn flag_classes(&self) -> Vec<CoreFlagClass> {
+ unsafe {
+ let mut count: usize = 0;
+ let regs = BNGetAllArchitectureSemanticFlagClasses(self.0, &mut count as *mut _);
+
+ let ret = slice::from_raw_parts_mut(regs, count)
+ .iter()
+ .map(|reg| CoreFlagClass(self.0, *reg))
+ .collect();
+
+ BNFreeRegisterList(regs);
+
+ ret
+ }
+ }
+
+ fn flag_groups(&self) -> Vec<CoreFlagGroup> {
+ unsafe {
+ let mut count: usize = 0;
+ let regs = BNGetAllArchitectureSemanticFlagGroups(self.0, &mut count as *mut _);
+
+ let ret = slice::from_raw_parts_mut(regs, count)
+ .iter()
+ .map(|reg| CoreFlagGroup(self.0, *reg))
+ .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);
+
+ ret
+ }
+ }
+
+ fn stack_pointer_reg(&self) -> Option<CoreRegister> {
+ match unsafe { BNGetArchitectureStackPointerRegister(self.0) } {
+ 0xffff_ffff => None,
+ reg => Some(CoreRegister(self.0, reg))
+ }
+ }
+
+ fn link_reg(&self) -> Option<CoreRegister> {
+ match unsafe { BNGetArchitectureLinkRegister(self.0) } {
+ 0xffff_ffff => None,
+ reg => Some(CoreRegister(self.0, reg))
+ }
+ }
+
+ fn register_from_id(&self, id: u32) -> Option<CoreRegister> {
+ // TODO validate in debug builds
+ Some(CoreRegister(self.0, id))
+ }
+
+ fn flag_from_id(&self, id: u32) -> Option<CoreFlag> {
+ // TODO validate in debug builds
+ Some(CoreFlag(self.0, id))
+ }
+
+ fn flag_write_from_id(&self, id: u32) -> Option<CoreFlagWrite> {
+ // TODO validate in debug builds
+ Some(CoreFlagWrite(self.0, id))
+ }
+
+ fn flag_class_from_id(&self, id: u32) -> Option<CoreFlagClass> {
+ // TODO validate in debug builds
+ Some(CoreFlagClass(self.0, id))
+ }
+
+ fn flag_group_from_id(&self, id: u32) -> Option<CoreFlagGroup> {
+ // TODO validate in debug builds
+ Some(CoreFlagGroup(self.0, id))
+ }
+
+ fn handle(&self) -> CoreArchitecture {
+ *self
+ }
+}
+
+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();
+
+ unsafe {
+ let cc = $get_api(handle.0);
+
+ if cc.is_null() {
+ None
+ } else {
+ Some(Ref::new(CallingConvention::from_raw(cc, self.handle())))
+ }
+ }
+ }
+
+ fn $set_name(&self, cc: &CallingConvention<Self>) {
+ let handle = self.as_ref();
+
+ assert!(cc.arch_handle.borrow().as_ref().0 == handle.0, "use of calling convention with non-matching architecture!");
+
+ unsafe {
+ $set_api(handle.0, cc.handle);
+ }
+ }
+ }
+}
+
+/// Contains helper methods for all types implementing 'Architecture'
+pub trait ArchitectureExt: Architecture {
+ fn register_by_name<S: BnStrCompatible>(&self, name: S) -> Option<Self::Register> {
+ let name = name.as_bytes_with_nul();
+
+ match unsafe { BNGetArchitectureRegisterByName(self.as_ref().0, name.as_ref().as_ptr() as *mut _) } {
+ 0xffff_ffff => None,
+ reg => self.register_from_id(reg)
+ }
+ }
+
+ cc_func!(get_default_calling_convention, BNGetArchitectureDefaultCallingConvention,
+ set_default_calling_convention, BNSetArchitectureDefaultCallingConvention);
+
+ cc_func!(get_cdecl_calling_convention, BNGetArchitectureCdeclCallingConvention,
+ set_cdecl_calling_convention, BNSetArchitectureCdeclCallingConvention);
+
+ cc_func!(get_stdcall_calling_convention, BNGetArchitectureStdcallCallingConvention,
+ set_stdcall_calling_convention, BNSetArchitectureStdcallCallingConvention);
+
+ cc_func!(get_fastcall_calling_convention, BNGetArchitectureFastcallCallingConvention,
+ set_fastcall_calling_convention, BNSetArchitectureFastcallCallingConvention);
+
+ fn standalone_platform(&self) -> Option<Ref<Platform>> {
+ unsafe {
+ let handle = BNGetArchitectureStandalonePlatform(self.as_ref().0);
+
+ if handle.is_null() {
+ return None;
+ }
+
+ Some(Ref::new(Platform::from_raw(handle)))
+ }
+ }
+}
+
+impl<T: Architecture> ArchitectureExt for T { }
+
+pub fn register_architecture<S, A, F>(name: S, func: F) -> &'static A
+where
+ S: BnStrCompatible,
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync + Sized,
+ F: FnOnce(CustomArchitectureHandle<A>, CoreArchitecture) -> A,
+{
+ use std::os::raw::{c_void, c_char};
+ use std::mem;
+
+ #[repr(C)]
+ struct ArchitectureBuilder<A, F>
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ F: FnOnce(CustomArchitectureHandle<A>, CoreArchitecture) -> A,
+ {
+ arch: A,
+ func: F,
+ }
+
+ extern "C" fn cb_init<A, F>(ctxt: *mut c_void, obj: *mut BNArchitecture)
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ F: FnOnce(CustomArchitectureHandle<A>, CoreArchitecture) -> A,
+ {
+ unsafe {
+ let custom_arch = &mut *(ctxt as *mut ArchitectureBuilder<A, F>);
+ let custom_arch_handle = CustomArchitectureHandle {
+ handle: ctxt as *mut A
+ };
+
+ let create = ptr::read(&custom_arch.func);
+ ptr::write(&mut custom_arch.arch, create(custom_arch_handle, CoreArchitecture(obj)));
+ }
+ }
+
+ extern "C" fn cb_endianness<A>(ctxt: *mut c_void) -> BNEndianness
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let custom_arch = unsafe { &*(ctxt as *mut A) };
+ custom_arch.endianness()
+ }
+
+ extern "C" fn cb_address_size<A>(ctxt: *mut c_void) -> usize
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let custom_arch = unsafe { &*(ctxt as *mut A) };
+ custom_arch.address_size()
+ }
+
+ extern "C" fn cb_default_integer_size<A>(ctxt: *mut c_void) -> usize
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let custom_arch = unsafe { &*(ctxt as *mut A) };
+ custom_arch.default_integer_size()
+ }
+
+ extern "C" fn cb_instruction_alignment<A>(ctxt: *mut c_void) -> usize
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let custom_arch = unsafe { &*(ctxt as *mut A) };
+ custom_arch.instruction_alignment()
+ }
+
+ extern "C" fn cb_max_instr_len<A>(ctxt: *mut c_void) -> usize
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let custom_arch = unsafe { &*(ctxt as *mut A) };
+ custom_arch.max_instr_len()
+ }
+
+ extern "C" fn cb_opcode_display_len<A>(ctxt: *mut c_void) -> usize
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let custom_arch = unsafe { &*(ctxt as *mut A) };
+ custom_arch.opcode_display_len()
+ }
+
+ extern "C" fn cb_associated_arch_by_addr<A>(ctxt: *mut c_void, addr: *mut u64) -> *mut BNArchitecture
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let custom_arch = unsafe { &*(ctxt as *mut A) };
+ let addr = unsafe { &mut *(addr) };
+
+ custom_arch.associated_arch_by_addr(addr).0
+ }
+
+ extern "C" fn cb_instruction_info<A>(ctxt: *mut c_void, data: *const u8, addr: u64,
+ len: usize, result: *mut BNInstructionInfo) -> bool
+ 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) };
+
+ match custom_arch.instruction_info(data, addr) {
+ Some(info) => {
+ result.0 = info.0;
+ true
+ }
+ None => false,
+ }
+ }
+
+ extern "C" fn cb_get_instruction_text<A>(ctxt: *mut c_void, data: *const u8, addr: u64, len: *mut usize,
+ result: *mut *mut BNInstructionTextToken, count: *mut usize) -> bool
+ 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 };
+
+ match custom_arch.instruction_text(data, addr) {
+ Some((res_size, res_tokens)) => {
+ unsafe {
+ let mut res_tokens = res_tokens.into();
+ res_tokens.shrink_to_fit();
+ assert!(res_tokens.capacity() == res_tokens.len());
+
+ *len = res_size;
+ *count = res_tokens.len();
+
+ *result = res_tokens.as_mut_ptr() as *mut _;
+ mem::forget(res_tokens);
+ }
+ true
+ }
+ None => false,
+ }
+ }
+
+ extern "C" fn cb_free_instruction_text(tokens: *mut BNInstructionTextToken, count: usize) {
+ let _tokens = unsafe { Vec::from_raw_parts(tokens as *mut InstructionTextToken, count, count) };
+ }
+
+ extern "C" fn cb_instruction_llil<A>(ctxt: *mut c_void, data: *const u8, addr: u64, len: *mut usize,
+ il: *mut BNLowLevelILFunction) -> bool
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let custom_arch = unsafe { &*(ctxt as *mut A) };
+ let custom_arch_handle = CustomArchitectureHandle {
+ 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) };
+
+ match custom_arch.instruction_llil(data, addr, &mut lifter) {
+ Some((res_len, res_value)) => {
+ unsafe { *len = res_len };
+ res_value
+ }
+ None => false,
+ }
+ }
+
+ extern "C" fn cb_reg_name<A>(ctxt: *mut c_void, reg: u32) -> *mut c_char
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ 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(),
+ }
+ }
+
+ extern "C" fn cb_flag_name<A>(ctxt: *mut c_void, flag: u32) -> *mut c_char
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ 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(),
+ }
+ }
+
+ extern "C" fn cb_flag_write_name<A>(ctxt: *mut c_void, flag_write: u32) -> *mut c_char
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ 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(),
+ }
+ }
+
+ extern "C" fn cb_semantic_flag_class_name<A>(ctxt: *mut c_void, class: u32) -> *mut c_char
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ 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(),
+ }
+ }
+
+ extern "C" fn cb_semantic_flag_group_name<A>(ctxt: *mut c_void, group: u32) -> *mut c_char
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ 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(),
+ }
+ }
+
+ fn alloc_register_list<I: Iterator<Item=u32> + ExactSizeIterator>(items: I, count: &mut usize) -> *mut u32 {
+ let len = items.len();
+ *count = len;
+
+ if len == 0 {
+ ptr::null_mut()
+ } else {
+ let mut res = Vec::with_capacity(len + 1);
+
+ res.push(len as u32);
+
+ for i in items {
+ res.push(i.clone().into());
+ }
+
+ assert!(res.len() == len + 1);
+
+ let raw = res.as_mut_ptr();
+ mem::forget(res);
+
+ unsafe { raw.offset(1) }
+ }
+ }
+
+ extern "C" fn cb_registers_full_width<A>(ctxt: *mut c_void, count: *mut usize) -> *mut u32
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let custom_arch = unsafe { &*(ctxt as *mut A) };
+ let regs = custom_arch.registers_full_width();
+
+ alloc_register_list(regs.iter().map(|r| r.id()), unsafe { &mut *count })
+ }
+
+ extern "C" fn cb_registers_all<A>(ctxt: *mut c_void, count: *mut usize) -> *mut u32
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let custom_arch = unsafe { &*(ctxt as *mut A) };
+ let regs = custom_arch.registers_all();
+
+ alloc_register_list(regs.iter().map(|r| r.id()), unsafe { &mut *count })
+ }
+
+ extern "C" fn cb_registers_global<A>(ctxt: *mut c_void, count: *mut usize) -> *mut u32
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let custom_arch = unsafe { &*(ctxt as *mut A) };
+ let regs = custom_arch.registers_global();
+
+ alloc_register_list(regs.iter().map(|r| r.id()), unsafe { &mut *count })
+ }
+
+ extern "C" fn cb_flags<A>(ctxt: *mut c_void, count: *mut usize) -> *mut u32
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let custom_arch = unsafe { &*(ctxt as *mut A) };
+ let flags = custom_arch.flags();
+
+ alloc_register_list(flags.iter().map(|r| r.id()), unsafe { &mut *count })
+ }
+
+ extern "C" fn cb_flag_write_types<A>(ctxt: *mut c_void, count: *mut usize) -> *mut u32
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let custom_arch = unsafe { &*(ctxt as *mut A) };
+ let flag_writes = custom_arch.flag_write_types();
+
+ alloc_register_list(flag_writes.iter().map(|r| r.id()), unsafe { &mut *count })
+ }
+
+ extern "C" fn cb_semantic_flag_classes<A>(ctxt: *mut c_void, count: *mut usize) -> *mut u32
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let custom_arch = unsafe { &*(ctxt as *mut A) };
+ let flag_classes = custom_arch.flag_classes();
+
+ alloc_register_list(flag_classes.iter().map(|r| r.id()), unsafe { &mut *count })
+ }
+
+ extern "C" fn cb_semantic_flag_groups<A>(ctxt: *mut c_void, count: *mut usize) -> *mut u32
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let custom_arch = unsafe { &*(ctxt as *mut A) };
+ let flag_groups = custom_arch.flag_groups();
+
+ alloc_register_list(flag_groups.iter().map(|r| r.id()), unsafe { &mut *count })
+ }
+
+ extern "C" fn cb_flag_role<A>(ctxt: *mut c_void, flag: u32, class: u32) -> BNFlagRole
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ 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)) {
+ flag.role(class)
+ } else {
+ FlagRole::SpecialFlagRole
+ }
+ }
+
+ extern "C" fn cb_flags_required_for_flag_cond<A>(ctxt: *mut c_void, cond: BNLowLevelILFlagCondition, class: u32,
+ count: *mut usize) -> *mut u32
+ 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 flags = custom_arch.flags_required_for_flag_condition(cond, class);
+
+ alloc_register_list(flags.iter().map(|r| r.id()), unsafe { &mut *count })
+ }
+
+ extern "C" fn cb_flags_required_for_semantic_flag_group<A>(ctxt: *mut c_void, group: u32, count: *mut usize) -> *mut u32
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let custom_arch = unsafe { &*(ctxt as *mut A) };
+
+ if let Some(group) = custom_arch.flag_group_from_id(group) {
+ let flags = group.flags_required();
+ alloc_register_list(flags.iter().map(|r| r.id()), unsafe { &mut *count })
+ } else {
+ unsafe { *count = 0; }
+ ptr::null_mut()
+ }
+ }
+
+ extern "C" fn cb_flag_conditions_for_semantic_flag_group<A>(ctxt: *mut c_void, group: u32, count: *mut usize)
+ -> *mut BNFlagConditionForSemanticClass
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let custom_arch = unsafe { &*(ctxt as *mut A) };
+
+ if let Some(group) = custom_arch.flag_group_from_id(group) {
+ let flag_conditions = group.flag_conditions();
+
+ unsafe {
+ let allocation_size = mem::size_of::<BNFlagConditionForSemanticClass>() * flag_conditions.len();
+ let result = libc::malloc(allocation_size) as *mut BNFlagConditionForSemanticClass;
+ let out_slice = slice::from_raw_parts_mut(result, flag_conditions.len());
+
+ for (i, (class, cond)) in flag_conditions.iter().enumerate() {
+ let out = out_slice.get_unchecked_mut(i);
+
+ out.semanticClass = class.id();
+ out.condition = *cond;
+ }
+
+ *count = flag_conditions.len();
+ result
+ }
+ } else {
+ unsafe { *count = 0; }
+ ptr::null_mut()
+ }
+ }
+
+ extern "C" fn cb_free_flag_conditions_for_semantic_flag_group<A>(_ctxt: *mut c_void, conds: *mut BNFlagConditionForSemanticClass)
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ unsafe { libc::free(conds as *mut _); }
+ }
+
+ extern "C" fn cb_flags_written_by_write_type<A>(ctxt: *mut c_void, write_type: u32, count: *mut usize) -> *mut u32
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let custom_arch = unsafe { &*(ctxt as *mut A) };
+
+ if let Some(write_type) = custom_arch.flag_write_from_id(write_type) {
+ let written = write_type.flags_written();
+ alloc_register_list(written.iter().map(|f| f.id()), unsafe { &mut *count })
+ } else {
+ unsafe { *count = 0; }
+ ptr::null_mut()
+ }
+ }
+
+ extern "C" fn cb_semantic_class_for_flag_write_type<A>(ctxt: *mut c_void, write_type: u32) -> u32
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let custom_arch = unsafe { &*(ctxt as *mut A) };
+ custom_arch.flag_write_from_id(write_type).map(|w| w.id()).unwrap_or(0)
+ }
+
+ extern "C" fn cb_flag_write_llil<A>(ctxt: *mut c_void, op: BNLowLevelILOperation, size: usize, flag_write: u32,
+ flag: u32, operands_raw: *mut BNRegisterOrConstant, operand_count: usize,
+ il: *mut BNLowLevelILFunction) -> usize
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let custom_arch = unsafe { &*(ctxt as *mut A) };
+ let custom_arch_handle = CustomArchitectureHandle {
+ 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) };
+
+ 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(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;
+ }
+ } else {
+ warn!("unable to unpack flag write op: {:?} with {} operands", op, operands.len());
+ }
+
+ let role = flag.role(flag_write.class());
+
+ unsafe {
+ BNGetDefaultArchitectureFlagWriteLowLevelIL(custom_arch.as_ref().0, op, size,
+ role, operands_raw, operand_count, il)
+ }
+ } else {
+ // TODO this should be impossible; requires bad flag/flag_write ids passed in;
+ // explode more violently
+ lifter.unimplemented().expr_idx
+ }
+ }
+
+ extern "C" fn cb_flag_cond_llil<A>(ctxt: *mut c_void, cond: FlagCondition, class: u32,
+ il: *mut BNLowLevelILFunction) -> usize
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let custom_arch = unsafe { &*(ctxt as *mut A) };
+ let custom_arch_handle = CustomArchitectureHandle {
+ handle: ctxt as *mut A
+ };
+
+ let class = custom_arch.flag_class_from_id(class);
+
+ let mut lifter = unsafe { Lifter::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;
+ }
+
+ lifter.unimplemented().expr_idx
+ }
+
+ extern "C" fn cb_flag_group_llil<A>(ctxt: *mut c_void, group: u32,
+ il: *mut BNLowLevelILFunction) -> usize
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let custom_arch = unsafe { &*(ctxt as *mut A) };
+ let custom_arch_handle = CustomArchitectureHandle {
+ handle: ctxt as *mut A
+ };
+
+ let mut lifter = unsafe { Lifter::from_raw(custom_arch_handle, il) };
+
+ if let Some(group) = custom_arch.flag_group_from_id(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;
+ }
+ }
+
+ lifter.unimplemented().expr_idx
+ }
+
+ extern "C" fn cb_free_register_list(_ctxt: *mut c_void, regs: *mut u32) {
+ if regs.is_null() {
+ return;
+ }
+
+ unsafe {
+ let actual_start = regs.offset(-1);
+ let len = *actual_start + 1;
+ let _regs = Vec::from_raw_parts(actual_start, len as usize, len as usize);
+ }
+ }
+
+ extern "C" fn cb_register_info<A>(ctxt: *mut c_void, reg: u32, result: *mut BNRegisterInfo)
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let custom_arch = unsafe { &*(ctxt as *mut A) };
+ let result = unsafe { &mut *result };
+
+ if let Some(reg) = custom_arch.register_from_id(reg) {
+ let info = reg.info();
+
+ result.fullWidthRegister = match info.parent() {
+ Some(p) => p.id(),
+ None => reg.id(),
+ };
+
+ result.offset = info.offset();
+ result.size = info.size();
+ result.extend = info.implicit_extend();
+ }
+ }
+
+ extern "C" fn cb_stack_pointer<A>(ctxt: *mut c_void) -> u32
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let custom_arch = unsafe { &*(ctxt as *mut A) };
+
+ if let Some(reg) = custom_arch.stack_pointer_reg() {
+ reg.id()
+ } else {
+ 0xffff_ffff
+ }
+ }
+
+ extern "C" fn cb_link_reg<A>(ctxt: *mut c_void) -> u32
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let custom_arch = unsafe { &*(ctxt as *mut A) };
+
+ if let Some(reg) = custom_arch.link_reg() {
+ reg.id()
+ } else {
+ 0xffff_ffff
+ }
+ }
+
+ extern "C" fn cb_reg_stack_name<A>(ctxt: *mut c_void, _stack: u32) -> *mut c_char
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let _custom_arch = unsafe { &*(ctxt as *mut A) };
+ BnString::new("reg_stack").into_raw()
+ }
+
+ extern "C" fn cb_reg_stacks<A>(ctxt: *mut c_void, count: *mut usize) -> *mut u32
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let _custom_arch = unsafe { &*(ctxt as *mut A) };
+
+ unsafe { *count = 0; }
+ ptr::null_mut()
+ }
+
+ extern "C" fn cb_reg_stack_info<A>(ctxt: *mut c_void, _stack: u32, _info: *mut BNRegisterStackInfo)
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let _custom_arch = unsafe { &*(ctxt as *mut A) };
+ }
+
+ extern "C" fn cb_intrinsic_name<A>(ctxt: *mut c_void, _intrinsic: u32) -> *mut c_char
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let _custom_arch = unsafe { &*(ctxt as *mut A) };
+ BnString::new("intrinsic").into_raw()
+ }
+
+ extern "C" fn cb_intrinsics<A>(ctxt: *mut c_void, count: *mut usize) -> *mut u32
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let _custom_arch = unsafe { &*(ctxt as *mut A) };
+
+ unsafe { *count = 0; }
+ ptr::null_mut()
+ }
+
+ extern "C" fn cb_intrinsic_inputs<A>(ctxt: *mut c_void, _intrinsic: u32, count: *mut usize) -> *mut BNNameAndType
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let _custom_arch = unsafe { &*(ctxt as *mut A) };
+
+ unsafe { *count = 0; }
+ ptr::null_mut()
+ }
+
+ 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) };
+ }
+
+ extern "C" fn cb_intrinsic_outputs<A>(ctxt: *mut c_void, _intrinsic: u32, count: *mut usize) -> *mut BNTypeWithConfidence
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let _custom_arch = unsafe { &*(ctxt as *mut A) };
+
+ unsafe { *count = 0; }
+ ptr::null_mut()
+ }
+
+ extern "C" fn cb_free_type_list<A>(ctxt: *mut c_void, _tl: *mut BNTypeWithConfidence, _count: usize)
+ where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync,
+ {
+ let _custom_arch = unsafe { &*(ctxt as *mut A) };
+ }
+
+ extern "C" fn cb_assemble(_ctxt: *mut c_void, _code: *const c_char, _addr: u64,
+ _result: *mut BNDataBuffer, errors: *mut *mut c_char) -> bool
+ {
+ unsafe { *errors = ptr::null_mut(); }
+ false
+ }
+
+ extern "C" fn cb_patch_unavailable(_ctxt: *mut c_void, _data: *const u8, _addr: u64, _len: usize) -> bool {
+ false
+ }
+
+ extern "C" fn cb_do_patch_unavailable(_ctxt: *mut c_void, _data: *mut u8, _addr: u64, _len: usize) -> bool {
+ false
+ }
+
+ extern "C" fn cb_skip_patch_unavailable(_ctxt: *mut c_void, _data: *mut u8, _addr: u64, _len: usize, _val: u64) -> bool {
+ false
+ }
+
+ let name = name.as_bytes_with_nul();
+
+ let uninit_arch = ArchitectureBuilder {
+ arch: unsafe { mem::uninitialized() },
+ func: func,
+ };
+
+ let raw = Box::into_raw(Box::new(uninit_arch));
+ let mut custom_arch = BNCustomArchitecture {
+ context: raw as *mut _,
+ init: Some(cb_init::<A, F>),
+ getEndianness: Some(cb_endianness::<A>),
+ getAddressSize: Some(cb_address_size::<A>),
+ getDefaultIntegerSize: Some(cb_default_integer_size::<A>),
+ getInstructionAlignment: Some(cb_instruction_alignment::<A>),
+ getMaxInstructionLength: Some(cb_max_instr_len::<A>),
+ getOpcodeDisplayLength: Some(cb_opcode_display_len::<A>),
+ getAssociatedArchitectureByAddress: Some(cb_associated_arch_by_addr::<A>),
+ getInstructionInfo: Some(cb_instruction_info::<A>),
+ getInstructionText: Some(cb_get_instruction_text::<A>),
+ freeInstructionText: Some(cb_free_instruction_text),
+ getInstructionLowLevelIL: Some(cb_instruction_llil::<A>),
+
+ getRegisterName: Some(cb_reg_name::<A>),
+ getFlagName: Some(cb_flag_name::<A>),
+ getFlagWriteTypeName: Some(cb_flag_write_name::<A>),
+ getSemanticFlagClassName: Some(cb_semantic_flag_class_name::<A>),
+ getSemanticFlagGroupName: Some(cb_semantic_flag_group_name::<A>),
+
+ getFullWidthRegisters: Some(cb_registers_full_width::<A>),
+ getAllRegisters: Some(cb_registers_all::<A>),
+ getAllFlags: Some(cb_flags::<A>),
+ getAllFlagWriteTypes: Some(cb_flag_write_types::<A>),
+ getAllSemanticFlagClasses: Some(cb_semantic_flag_classes::<A>),
+ getAllSemanticFlagGroups: Some(cb_semantic_flag_groups::<A>),
+
+ getFlagRole: Some(cb_flag_role::<A>),
+ getFlagsRequiredForFlagCondition: Some(cb_flags_required_for_flag_cond::<A>),
+
+ getFlagsRequiredForSemanticFlagGroup: Some(cb_flags_required_for_semantic_flag_group::<A>),
+ getFlagConditionsForSemanticFlagGroup: Some(cb_flag_conditions_for_semantic_flag_group::<A>),
+ freeFlagConditionsForSemanticFlagGroup: Some(cb_free_flag_conditions_for_semantic_flag_group::<A>),
+
+ getFlagsWrittenByFlagWriteType: Some(cb_flags_written_by_write_type::<A>),
+ getSemanticClassForFlagWriteType: Some(cb_semantic_class_for_flag_write_type::<A>),
+
+ getFlagWriteLowLevelIL: Some(cb_flag_write_llil::<A>),
+ getFlagConditionLowLevelIL: Some(cb_flag_cond_llil::<A>),
+ getSemanticFlagGroupLowLevelIL: Some(cb_flag_group_llil::<A>),
+
+ freeRegisterList: Some(cb_free_register_list),
+ getRegisterInfo: Some(cb_register_info::<A>),
+ getStackPointerRegister: Some(cb_stack_pointer::<A>),
+ getLinkRegister: Some(cb_link_reg::<A>),
+ getGlobalRegisters: Some(cb_registers_global::<A>),
+
+ getRegisterStackName: Some(cb_reg_stack_name::<A>),
+ getAllRegisterStacks: Some(cb_reg_stacks::<A>),
+ getRegisterStackInfo: Some(cb_reg_stack_info::<A>),
+
+ getIntrinsicName: Some(cb_intrinsic_name::<A>),
+ getAllIntrinsics: Some(cb_intrinsics::<A>),
+ getIntrinsicInputs: Some(cb_intrinsic_inputs::<A>),
+ freeNameAndTypeList: Some(cb_free_name_and_types::<A>),
+ getIntrinsicOutputs: Some(cb_intrinsic_outputs::<A>),
+ freeTypeList: Some(cb_free_type_list::<A>),
+
+ assemble: Some(cb_assemble),
+
+ isNeverBranchPatchAvailable: Some(cb_patch_unavailable),
+ isAlwaysBranchPatchAvailable: Some(cb_patch_unavailable),
+ isInvertBranchPatchAvailable: Some(cb_patch_unavailable),
+ isSkipAndReturnZeroPatchAvailable: Some(cb_patch_unavailable),
+ isSkipAndReturnValuePatchAvailable: Some(cb_patch_unavailable),
+
+ convertToNop: Some(cb_do_patch_unavailable),
+ alwaysBranch: Some(cb_do_patch_unavailable),
+ invertBranch: Some(cb_do_patch_unavailable),
+ skipAndReturnValue: Some(cb_skip_patch_unavailable),
+ };
+
+ unsafe {
+ let res = BNRegisterArchitecture(name.as_ref().as_ptr() as *mut _, &mut custom_arch as *mut _);
+
+ assert!(!res.is_null());
+
+ &(*raw).arch
+ }
+}
+
+pub struct CustomArchitectureHandle<A>
+where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync
+{
+ handle: *mut A
+}
+
+unsafe impl<A> Send for CustomArchitectureHandle<A>
+where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync
+{}
+
+unsafe impl<A> Sync for CustomArchitectureHandle<A>
+where
+ A: 'static + Architecture<Handle=CustomArchitectureHandle<A>> + Send + Sync
+{}
+
+impl<A> Clone for CustomArchitectureHandle<A>
+where
+ A: 'static + Architecture<Handle=Self> + Send + Sync
+{
+ fn clone(&self) -> Self {
+ Self { handle: self.handle }
+ }
+}
+
+impl<A> Copy for CustomArchitectureHandle<A>
+where
+ A: 'static + Architecture<Handle=Self> + Send + Sync
+{
+}
+
+impl<A> Borrow<A> for CustomArchitectureHandle<A>
+where
+ A: 'static + Architecture<Handle=Self> + Send + Sync
+{
+ fn borrow(&self) -> &A {
+ unsafe { &*self.handle }
+ }
+}
diff --git a/rust/src/basicblock.rs b/rust/src/basicblock.rs
new file mode 100644
index 00000000..db7989a0
--- /dev/null
+++ b/rust/src/basicblock.rs
@@ -0,0 +1,274 @@
+use std::fmt;
+
+use binaryninjacore_sys::*;
+use crate::architecture::CoreArchitecture;
+use crate::function::Function;
+
+use crate::rc::*;
+
+enum EdgeDirection {
+ Incoming,
+ Outgoing,
+}
+
+pub struct Edge<'a, C: 'a + BlockContext> {
+ branch: super::BranchType,
+ back_edge: bool,
+ 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!(f, "{:?} ({}) {:?} -> {:?}", self.branch, self.back_edge, &*self.source, &*self.target)
+ }
+}
+
+pub struct EdgeContext<'a, C: 'a + BlockContext> {
+ dir: EdgeDirection,
+ orig_block: &'a BasicBlock<C>,
+}
+
+unsafe impl<'a, C: 'a + BlockContext> CoreOwnedArrayProvider for Edge<'a, C> {
+ type Raw = BNBasicBlockEdge;
+ type Context = EdgeContext<'a, C>;
+
+ unsafe fn free(raw: *mut BNBasicBlockEdge, count: usize, _context: &Self::Context) {
+ BNFreeBasicBlockEdgeList(raw, count);
+ }
+}
+
+unsafe impl<'a, C: 'a + BlockContext> CoreOwnedArrayWrapper<'a> for Edge<'a, C> {
+ type Wrapped = Edge<'a, C>;
+
+ unsafe fn wrap_raw(raw: &'a BNBasicBlockEdge, context: &'a Self::Context) -> Edge<'a, C> {
+ let edge_target = Guard::new(BasicBlock::from_raw(raw.target, context.orig_block.context.clone()), raw);
+ let orig_block = Guard::new(BasicBlock::from_raw(context.orig_block.handle, context.orig_block.context.clone()), raw);
+
+ let (source, target) = match context.dir {
+ EdgeDirection::Incoming => (edge_target, orig_block),
+ EdgeDirection::Outgoing => (orig_block, edge_target),
+ };
+
+ Edge {
+ branch: raw.type_,
+ back_edge: raw.backEdge,
+ source: source,
+ target: target,
+ }
+ }
+}
+
+pub trait BlockContext: Clone + Sync + Send + Sized {
+ type Instruction;
+ type Iter: Iterator<Item=Self::Instruction>;
+
+ fn start(&self, block: &BasicBlock<Self>) -> Self::Instruction;
+ fn iter(&self, block: &BasicBlock<Self>) -> Self::Iter;
+}
+
+#[derive(PartialEq, Eq, Hash)]
+pub struct BasicBlock<C: BlockContext> {
+ pub(crate) handle: *mut BNBasicBlock,
+ 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 }
+ }
+
+ // TODO native bb vs il bbs
+ pub fn function(&self) -> Ref<Function> {
+ unsafe {
+ let func = BNGetBasicBlockFunction(self.handle);
+ Ref::new(Function::from_raw(func))
+ }
+ }
+
+ pub fn arch(&self) -> CoreArchitecture {
+ unsafe {
+ let arch = BNGetBasicBlockArchitecture(self.handle);
+ CoreArchitecture::from_raw(arch)
+ }
+ }
+
+ pub fn iter(&self) -> C::Iter {
+ self.context.iter(self)
+ }
+
+ pub fn raw_start(&self) -> u64 {
+ unsafe { BNGetBasicBlockStart(self.handle) }
+ }
+
+ pub fn raw_end(&self) -> u64 {
+ unsafe { BNGetBasicBlockEnd(self.handle) }
+ }
+
+ pub fn raw_length(&self) -> u64 {
+ unsafe { BNGetBasicBlockLength(self.handle) }
+ }
+
+ pub fn incoming_edges(&self) -> Array<Edge<C>> {
+ unsafe {
+ let mut count = 0;
+ let edges = BNGetBasicBlockIncomingEdges(self.handle, &mut count);
+
+ Array::new(edges, count, EdgeContext {
+ dir: EdgeDirection::Incoming,
+ orig_block: self,
+ })
+ }
+ }
+
+ pub fn outgoing_edges(&self) -> Array<Edge<C>> {
+ unsafe {
+ let mut count = 0;
+ let edges = BNGetBasicBlockOutgoingEdges(self.handle, &mut count);
+
+ Array::new(edges, count, EdgeContext {
+ dir: EdgeDirection::Outgoing,
+ orig_block: self,
+ })
+ }
+ }
+
+ // is this valid for il blocks?
+ pub fn has_undetermined_outgoing_edges(&self) -> bool {
+ unsafe { BNBasicBlockHasUndeterminedOutgoingEdges(self.handle) }
+ }
+
+ pub fn can_exit(&self) -> bool {
+ unsafe { BNBasicBlockCanExit(self.handle) }
+ }
+
+ pub fn index(&self) -> usize {
+ unsafe { BNGetBasicBlockIndex(self.handle) }
+ }
+
+ pub fn immediate_dominator(&self) -> Option<Ref<Self>> {
+ unsafe {
+ let block = BNGetBasicBlockImmediateDominator(self.handle, false);
+
+ if block.is_null() {
+ return None;
+ }
+
+ Some(Ref::new(BasicBlock::from_raw(block, self.context.clone())))
+ }
+ }
+
+ pub fn dominators(&self) -> Array<BasicBlock<C>> {
+ unsafe {
+ let mut count = 0;
+ let blocks = BNGetBasicBlockDominators(self.handle, &mut count, false);
+
+ Array::new(blocks, count, self.context.clone())
+ }
+ }
+
+ pub fn strict_dominators(&self) -> Array<BasicBlock<C>> {
+ unsafe {
+ let mut count = 0;
+ let blocks = BNGetBasicBlockStrictDominators(self.handle, &mut count, false);
+
+ Array::new(blocks, count, self.context.clone())
+ }
+ }
+
+ pub fn dominator_tree_children(&self) -> Array<BasicBlock<C>> {
+ unsafe {
+ let mut count = 0;
+ let blocks = BNGetBasicBlockDominatorTreeChildren(self.handle, &mut count, false);
+
+ Array::new(blocks, count, self.context.clone())
+ }
+ }
+
+ pub fn dominance_frontier(&self) -> Array<BasicBlock<C>> {
+ unsafe {
+ let mut count = 0;
+ let blocks = BNGetBasicBlockDominanceFrontier(self.handle, &mut count, false);
+
+ Array::new(blocks, count, self.context.clone())
+ }
+ }
+
+ // TODO iterated dominance frontier
+
+}
+
+impl<'a, C: BlockContext> IntoIterator for &'a BasicBlock<C> {
+ type Item = C::Instruction;
+ type IntoIter = C::Iter;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.iter()
+ }
+}
+
+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())
+ }
+}
+
+impl<C: BlockContext> ToOwned for BasicBlock<C> {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl<C: BlockContext> RefCountable for BasicBlock<C> {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: BNNewBasicBlockReference(handle.handle),
+ context: handle.context.clone(),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeBasicBlock(handle.handle);
+ }
+}
+
+unsafe impl<C: BlockContext> CoreOwnedArrayProvider for BasicBlock<C> {
+ type Raw = *mut BNBasicBlock;
+ type Context = C;
+
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeBasicBlockList(raw, count);
+ }
+}
+
+unsafe impl<'a, C: 'a + BlockContext> CoreOwnedArrayWrapper<'a> for BasicBlock<C> {
+ type Wrapped = Guard<'a, BasicBlock<C>>;
+
+ unsafe fn wrap_raw(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped {
+ Guard::new(BasicBlock::from_raw(*raw, context.clone()), context)
+ }
+}
+
diff --git a/rust/src/binaryview.rs b/rust/src/binaryview.rs
new file mode 100644
index 00000000..b6d1d1a7
--- /dev/null
+++ b/rust/src/binaryview.rs
@@ -0,0 +1,1340 @@
+use binaryninjacore_sys::*;
+
+pub use binaryninjacore_sys::BNModificationStatus as ModificationStatus;
+
+use std::result;
+use std::slice;
+use std::ops;
+use std::marker::PhantomData;
+
+use std::os::raw::{c_void, c_char};
+use std::mem;
+use std::ptr;
+
+use crate::Endianness;
+
+use crate::architecture::Architecture;
+use crate::architecture::CoreArchitecture;
+use crate::platform::Platform;
+use crate::filemetadata::FileMetadata;
+use crate::fileaccessor::FileAccessor;
+use crate::symbol::{SymType, Symbol};
+use crate::segment::{Segment, SegmentBuilder};
+use crate::section::{Section, SectionBuilder};
+use crate::function::{Function, NativeBlock};
+use crate::basicblock::BasicBlock;
+use crate::types::Type;
+use crate::settings::Settings;
+
+use crate::string::*;
+use crate::rc::*;
+
+// TODO
+// merge filemetadata/fileaccessor under here?
+// general reorg of modules related to bv
+
+pub type Result<R> = result::Result<R, ()>;
+
+pub trait BinaryViewBase: AsRef<BinaryView> {
+ fn read(&self, _buf: &mut [u8], _offset: u64) -> usize { 0 }
+ fn write(&self, _offset: u64, _data: &[u8]) -> usize { 0 }
+ fn insert(&self, _offset: u64, _data: &[u8]) -> usize { 0 }
+ fn remove(&self, _offset: u64, _len: usize) -> usize { 0 }
+
+ fn offset_valid(&self, offset: u64) -> bool {
+ let mut buf = [0u8; 1];
+
+ // don't use self.read so that if segments were used we
+ // check against those as well
+ self.as_ref().read(&mut buf[..], offset) == buf.len()
+ }
+
+ fn offset_readable(&self, offset: u64) -> bool {
+ self.offset_valid(offset)
+ }
+
+ fn offset_writable(&self, offset: u64) -> bool {
+ self.offset_valid(offset)
+ }
+
+ fn offset_executable(&self, offset: u64) -> bool {
+ self.offset_valid(offset)
+ }
+
+ fn offset_backed_by_file(&self, offset: u64) -> bool {
+ self.offset_valid(offset)
+ }
+
+ fn next_valid_offset_after(&self, offset: u64) -> u64 {
+ let start = self.as_ref().start();
+
+ if offset < start {
+ start
+ } else {
+ offset
+ }
+ }
+
+ #[allow(unused)]
+ fn modification_status(&self, offset: u64) -> ModificationStatus {
+ ModificationStatus::Original
+ }
+
+ fn start(&self) -> u64 { 0 }
+ fn len(&self) -> usize { 0 }
+
+ fn executable(&self) -> bool { true }
+ fn relocatable(&self) -> bool { true }
+
+ fn entry_point(&self) -> u64;
+ 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())
+ .unwrap_or(false)
+ }
+}
+
+pub trait BinaryViewExt: BinaryViewBase {
+ fn metadata(&self) -> Ref<FileMetadata> {
+ unsafe {
+ let raw = BNGetFileForView(self.as_ref().handle);
+
+ Ref::new(FileMetadata::from_raw(raw))
+ }
+ }
+
+ fn parent_view(&self) -> Result<Ref<BinaryView>> {
+ let handle = unsafe { BNGetParentView(self.as_ref().handle) };
+
+ if handle.is_null() {
+ return Err(());
+ }
+
+ unsafe { Ok(Ref::new(BinaryView { handle })) }
+ }
+
+ /// Reads up to `len` bytes from address `offset`
+ fn read_vec(&self, offset: u64, len: usize) -> Vec<u8> {
+ let mut ret = Vec::with_capacity(len);
+
+ unsafe {
+ let res;
+
+ {
+ let dest_slice = ret.get_unchecked_mut(0 .. len);
+ res = self.read(dest_slice, offset);
+ }
+
+ ret.set_len(res);
+ }
+
+ ret
+ }
+
+ /// Appends up to `len` bytes from address `offset` into `dest`
+ fn read_into_vec(&self, dest: &mut Vec<u8>, offset: u64, len: usize) -> usize {
+ let starting_len = dest.len();
+ let space = dest.capacity() - starting_len;
+
+ if space < len {
+ dest.reserve(len - space);
+ }
+
+ unsafe {
+ let res;
+
+ {
+ let dest_slice = dest.get_unchecked_mut(starting_len .. starting_len + len);
+ res = self.read(dest_slice, offset);
+ }
+
+ if res > 0 {
+ dest.set_len(starting_len + res);
+ }
+
+ res
+ }
+ }
+
+ fn notify_data_written(&self, offset: u64, len: usize) {
+ unsafe { BNNotifyDataWritten(self.as_ref().handle, offset, len); }
+ }
+
+ fn notify_data_inserted(&self, offset: u64, len: usize) {
+ unsafe { BNNotifyDataInserted(self.as_ref().handle, offset, len); }
+ }
+
+ fn notify_data_removed(&self, offset: u64, len: usize) {
+ unsafe { BNNotifyDataRemoved(self.as_ref().handle, offset, len as u64); }
+ }
+
+ fn offset_has_code_semantics(&self, offset: u64) -> bool {
+ unsafe { BNIsOffsetCodeSemantics(self.as_ref().handle, offset) }
+ }
+
+ fn offset_has_writable_semantics(&self, offset: u64) -> bool {
+ unsafe { BNIsOffsetWritableSemantics(self.as_ref().handle, offset) }
+ }
+
+ fn end(&self) -> u64 {
+ unsafe { BNGetEndOffset(self.as_ref().handle) }
+ }
+
+ fn update_analysis_and_wait(&self) {
+ unsafe { BNUpdateAnalysisAndWait(self.as_ref().handle); }
+ }
+
+ fn default_arch(&self) -> Option<CoreArchitecture> {
+ unsafe {
+ let raw = BNGetDefaultArchitecture(self.as_ref().handle);
+
+ if raw.is_null() {
+ return None;
+ }
+
+ Some(CoreArchitecture::from_raw(raw))
+ }
+ }
+
+ fn set_default_arch<A: Architecture>(&self, arch: &A) {
+ unsafe {
+ BNSetDefaultArchitecture(self.as_ref().handle, arch.as_ref().0);
+ }
+ }
+
+ fn default_platform(&self) -> Option<Ref<Platform>> {
+ unsafe {
+ let raw = BNGetDefaultPlatform(self.as_ref().handle);
+
+ if raw.is_null() {
+ return None;
+ }
+
+ Some(Ref::new(Platform::from_raw(raw)))
+ }
+ }
+
+ fn set_default_platform(&self, plat: &Platform) {
+ unsafe {
+ BNSetDefaultPlatform(self.as_ref().handle, plat.handle);
+ }
+ }
+
+ fn get_instruction_len<A: Architecture>(&self, arch: &A, addr: u64) -> Option<usize> {
+ unsafe {
+ let size = BNGetInstructionLength(self.as_ref().handle, arch.as_ref().0, addr);
+
+ if size > 0 {
+ Some(size)
+ } else {
+ None
+ }
+ }
+ }
+
+ fn symbol_by_address(&self, addr: u64) -> Result<Ref<Symbol>> {
+ unsafe {
+ let raw_sym = BNGetSymbolByAddress(self.as_ref().handle, addr, ptr::null_mut());
+
+ if raw_sym.is_null() {
+ return Err(());
+ }
+
+ Ok(Ref::new(Symbol::from_raw(raw_sym)))
+ }
+ }
+
+ fn symbol_by_raw_name<S: BnStrCompatible>(&self, raw_name: S) -> Result<Ref<Symbol>> {
+ let raw_name = raw_name.as_bytes_with_nul();
+
+ unsafe {
+ let raw_sym = BNGetSymbolByRawName(self.as_ref().handle, raw_name.as_ref().as_ptr() as *mut _, ptr::null_mut());
+
+ if raw_sym.is_null() {
+ return Err(());
+ }
+
+ Ok(Ref::new(Symbol::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());
+
+ Array::new(handles, count, ())
+ }
+ }
+
+ fn symbols_by_name<S: BnStrCompatible>(&self, name: S) -> Array<Symbol> {
+ let raw_name = name.as_bytes_with_nul();
+
+ unsafe {
+ let mut count = 0;
+ let handles = BNGetSymbolsByName(self.as_ref().handle, raw_name.as_ref().as_ptr() as *mut _, &mut count, ptr::null_mut());
+
+ Array::new(handles, count, ())
+ }
+ }
+
+ fn symbols_in_range(&self, range: ops::Range<u64>) -> Array<Symbol> {
+ unsafe {
+ let mut count = 0;
+ let len = range.end.wrapping_sub(range.start);
+ let handles = BNGetSymbolsInRange(self.as_ref().handle, range.start, len, &mut count, ptr::null_mut());
+
+ Array::new(handles, count, ())
+ }
+ }
+
+ fn symbols_of_type(&self, ty: SymType) -> Array<Symbol> {
+ unsafe {
+ let mut count = 0;
+ let handles = BNGetSymbolsOfType(self.as_ref().handle, ty.into(), &mut count, ptr::null_mut());
+
+ Array::new(handles, count, ())
+ }
+ }
+
+ fn symbols_of_type_in_range(&self, ty: SymType, range: ops::Range<u64>) -> Array<Symbol> {
+ unsafe {
+ let mut count = 0;
+ let len = range.end.wrapping_sub(range.start);
+ let handles = BNGetSymbolsOfTypeInRange(self.as_ref().handle, ty.into(), range.start, len, &mut count, ptr::null_mut());
+
+ Array::new(handles, count, ())
+ }
+ }
+
+ fn define_auto_symbol(&self, sym: &Symbol) {
+ unsafe { BNDefineAutoSymbol(self.as_ref().handle, sym.handle); }
+ }
+
+ fn define_auto_symbol_with_type<'a, T: Into<Option<&'a Type>>>(&self, sym: &Symbol, plat: &Platform, ty: T) {
+ let raw_type = if let Some(t) = ty.into() {
+ t.handle
+ } else {
+ ptr::null_mut()
+ };
+
+ unsafe { BNDefineAutoSymbolAndVariableOrFunction(self.as_ref().handle, plat.handle, sym.handle, raw_type); }
+ }
+
+ fn undefine_auto_symbol(&self, sym: &Symbol) {
+ unsafe { BNUndefineAutoSymbol(self.as_ref().handle, sym.handle); }
+ }
+
+ fn define_user_symbol(&self, sym: &Symbol) {
+ unsafe { BNDefineUserSymbol(self.as_ref().handle, sym.handle); }
+ }
+
+ fn undefine_user_symbol(&self, sym: &Symbol) {
+ unsafe { BNUndefineUserSymbol(self.as_ref().handle, sym.handle); }
+ }
+
+ fn segments(&self) -> Array<Segment> {
+ unsafe {
+ let mut count = 0;
+ let segs = BNGetSegments(self.as_ref().handle, &mut count);
+
+ Array::new(segs, count, ())
+ }
+ }
+
+ fn segment_at(&self, addr: u64) -> Option<Segment> {
+ unsafe {
+ let raw_seg = BNGetSegmentAt(self.as_ref().handle, addr);
+ if !raw_seg.is_null() {
+ Some(Segment::from_raw(raw_seg))
+ } else {
+ None
+ }
+ }
+ }
+
+ fn add_segment(&self, segment: SegmentBuilder) {
+ segment.create(self.as_ref());
+ }
+
+ 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.as_bytes_with_nul();
+ let name_ptr = name.as_ref().as_ptr() as *mut _;
+
+ unsafe { BNRemoveAutoSection(self.as_ref().handle, name_ptr); }
+ }
+
+ fn remove_user_section<S: BnStrCompatible>(&self, name: S) {
+ let name = name.as_bytes_with_nul();
+ let name_ptr = name.as_ref().as_ptr() as *mut _;
+
+ unsafe { BNRemoveUserSection(self.as_ref().handle, name_ptr); }
+ }
+
+ fn section_by_name<S: BnStrCompatible>(&self, name: S) -> Result<Section> {
+ unsafe {
+ let raw_name = name.as_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(());
+ }
+
+ Ok(Section::from_raw(raw_section))
+ }
+ }
+
+ fn sections(&self) -> Array<Section> {
+ unsafe {
+ let mut count = 0;
+ let sections = BNGetSections(self.as_ref().handle, &mut count);
+
+ Array::new(sections, count, ())
+ }
+ }
+
+ fn sections_at(&self, addr: u64) -> Array<Section> {
+ unsafe {
+ let mut count = 0;
+ let sections = BNGetSectionsAt(self.as_ref().handle, addr, &mut count);
+
+ Array::new(sections, count, ())
+ }
+ }
+
+ fn add_auto_function(&self, plat: &Platform, addr: u64) {
+ unsafe { BNAddFunctionForAnalysis(self.as_ref().handle, plat.handle, addr); }
+ }
+
+ fn add_entry_point(&self, plat: &Platform, addr: u64) {
+ unsafe { BNAddEntryPointForAnalysis(self.as_ref().handle, plat.handle, addr); }
+ }
+
+ fn create_user_function(&self, plat: &Platform, addr: u64) {
+ unsafe { BNCreateUserFunction(self.as_ref().handle, plat.handle, addr); }
+ }
+
+ fn has_functions(&self) -> bool {
+ unsafe { BNHasFunctions(self.as_ref().handle) }
+ }
+
+ fn entry_point_function(&self) -> Result<Ref<Function>> {
+ unsafe {
+ let func = BNGetAnalysisEntryPoint(self.as_ref().handle);
+
+ if func.is_null() {
+ return Err(());
+ }
+
+ Ok(Ref::new(Function::from_raw(func)))
+ }
+ }
+
+ fn functions(&self) -> Array<Function> {
+ unsafe {
+ let mut count = 0;
+ let functions = BNGetAnalysisFunctionList(self.as_ref().handle, &mut count);
+
+ Array::new(functions, count, ())
+ }
+ }
+
+ /// List of functions *starting* at `addr`
+ fn functions_at(&self, addr: u64) -> Array<Function> {
+ unsafe {
+ let mut count = 0;
+ let functions = BNGetAnalysisFunctionsForAddress(self.as_ref().handle, addr, &mut count);
+
+ Array::new(functions, count, ())
+ }
+ }
+
+ fn function_at(&self, platform: &Platform, addr: u64) -> Result<Ref<Function>> {
+ unsafe {
+ let handle = BNGetAnalysisFunction(self.as_ref().handle, platform.handle, addr);
+
+ if handle.is_null() {
+ return Err(());
+ }
+
+ Ok(Ref::new(Function::from_raw(handle)))
+ }
+ }
+
+ fn basic_blocks_containing(&self, addr: u64) -> Array<BasicBlock<NativeBlock>> {
+ unsafe {
+ let mut count = 0;
+ let blocks = BNGetBasicBlocksForAddress(self.as_ref().handle, addr, &mut count);
+
+ Array::new(blocks, count, NativeBlock::new())
+ }
+ }
+
+ fn basic_blocks_starting_at(&self, addr: u64) -> Array<BasicBlock<NativeBlock>> {
+ unsafe {
+ let mut count = 0;
+ let blocks = BNGetBasicBlocksStartingAtAddress(self.as_ref().handle, addr, &mut count);
+
+ Array::new(blocks, count, NativeBlock::new())
+ }
+ }
+
+ fn is_new_auto_function_analysis_suppressed(&self) -> bool {
+ unsafe { BNGetNewAutoFunctionAnalysisSuppressed(self.as_ref().handle) }
+ }
+
+ fn set_new_auto_function_analysis_suppressed(&self, suppress: bool) {
+ unsafe { BNSetNewAutoFunctionAnalysisSuppressed(self.as_ref().handle, suppress); }
+ }
+}
+
+impl<T: BinaryViewBase> BinaryViewExt for T {}
+
+#[derive(PartialEq, Eq, Hash)]
+pub struct BinaryView {
+ pub(crate) handle: *mut BNBinaryView,
+}
+
+unsafe impl Send for BinaryView {}
+unsafe impl Sync for BinaryView {}
+
+impl BinaryView {
+ pub(crate) unsafe fn from_raw(handle: *mut BNBinaryView) -> Self {
+ debug_assert!(!handle.is_null());
+
+ Self { handle }
+ }
+
+ pub fn from_filename<S: BnStrCompatible>(meta: &FileMetadata, filename: S) -> Result<Ref<Self>> {
+ let file = filename.as_bytes_with_nul();
+
+ let handle = unsafe { BNCreateBinaryDataViewFromFilename(meta.handle, file.as_ref().as_ptr() as *mut _) };
+
+ if handle.is_null() {
+ return Err(());
+ }
+
+ unsafe { Ok(Ref::new(Self { handle })) }
+ }
+
+ pub fn from_accessor(meta: &FileMetadata, file: &mut FileAccessor) -> Result<Ref<Self>> {
+ let handle = unsafe { BNCreateBinaryDataViewFromFile(meta.handle, &mut file.api_object as *mut _) };
+
+ if handle.is_null() {
+ return Err(());
+ }
+
+ unsafe { Ok(Ref::new(Self { handle })) }
+ }
+
+ pub fn from_data(meta: &FileMetadata, data: &[u8]) -> Result<Ref<Self>> {
+ let handle = unsafe { BNCreateBinaryDataViewFromData(meta.handle, data.as_ptr() as *mut _, data.len()) };
+
+ if handle.is_null() {
+ return Err(());
+ }
+
+ unsafe { Ok(Ref::new(Self { handle })) }
+ }
+}
+
+impl AsRef<BinaryView> for BinaryView {
+ fn as_ref(&self) -> &Self {
+ self
+ }
+}
+
+impl BinaryViewBase for BinaryView {
+ fn read(&self, buf: &mut [u8], offset: u64) -> usize {
+ unsafe {
+ BNReadViewData(self.handle, buf.as_mut_ptr() as *mut _, offset, buf.len())
+ }
+ }
+
+ fn write(&self, offset: u64, data: &[u8]) -> usize {
+ unsafe { BNWriteViewData(self.handle, offset, data.as_ptr() as *const _, data.len()) }
+ }
+
+ fn insert(&self, offset: u64, data: &[u8]) -> usize {
+ unsafe { BNInsertViewData(self.handle, offset, data.as_ptr() as *const _, data.len()) }
+ }
+
+ fn remove(&self, offset: u64, len: usize) -> usize {
+ 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) }
+ }
+
+ fn offset_readable(&self, offset: u64) -> bool {
+ unsafe { BNIsOffsetReadable(self.handle, offset) }
+ }
+
+ fn offset_writable(&self, offset: u64) -> bool {
+ unsafe { BNIsOffsetWritable(self.handle, offset) }
+ }
+
+ fn offset_executable(&self, offset: u64) -> bool {
+ unsafe { BNIsOffsetExecutable(self.handle, offset) }
+ }
+
+ fn offset_backed_by_file(&self, offset: u64) -> bool {
+ unsafe { BNIsOffsetBackedByFile(self.handle, offset) }
+ }
+
+ fn next_valid_offset_after(&self, offset: u64) -> u64 {
+ unsafe { BNGetNextValidOffset(self.handle, offset) }
+ }
+
+ fn default_endianness(&self) -> Endianness {
+ unsafe { BNGetDefaultEndianness(self.handle) }
+ }
+
+ fn relocatable(&self) -> bool {
+ unsafe { BNIsRelocatable(self.handle) }
+ }
+
+ fn address_size(&self) -> usize {
+ unsafe { BNGetViewAddressSize(self.handle) }
+ }
+
+ fn start(&self) -> u64 {
+ unsafe { BNGetStartOffset(self.handle) }
+ }
+
+ fn len(&self) -> usize {
+ unsafe { BNGetViewLength(self.handle) as usize }
+ }
+
+ fn entry_point(&self) -> u64 {
+ unsafe { BNGetEntryPoint(self.handle) }
+ }
+
+ fn executable(&self) -> bool {
+ unsafe { BNIsExecutableView(self.handle) }
+ }
+}
+
+impl ToOwned for BinaryView {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for BinaryView {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: BNNewViewReference(handle.handle),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeBinaryView(handle.handle);
+ }
+}
+
+pub trait BinaryViewTypeBase: AsRef<BinaryViewType> {
+ fn is_valid_for(&self, data: &BinaryView) -> bool;
+
+ fn load_settings_for_data(&self, data: &BinaryView) -> Ref<Settings> {
+ unsafe {
+ Ref::new(Settings::from_raw(BNGetBinaryViewDefaultLoadSettingsForData(self.as_ref().0, data.handle)))
+ }
+ }
+}
+
+pub trait BinaryViewTypeExt: BinaryViewTypeBase {
+ fn name(&self) -> BnString {
+ unsafe {
+ BnString::from_raw(BNGetBinaryViewTypeName(self.as_ref().0))
+ }
+ }
+
+ fn long_name(&self) -> BnString {
+ unsafe {
+ BnString::from_raw(BNGetBinaryViewTypeLongName(self.as_ref().0))
+ }
+ }
+
+ fn register_arch<A: Architecture>(&self, id: u32, endianness: Endianness, arch: &A)
+ {
+ unsafe {
+ BNRegisterArchitectureForViewType(self.as_ref().0, id, endianness, arch.as_ref().0);
+ }
+ }
+
+ fn register_platform(&self, id: u32, plat: &Platform)
+ {
+ let arch = plat.arch();
+
+ unsafe {
+ BNRegisterPlatformForViewType(self.as_ref().0, id, arch.0, plat.handle);
+ }
+ }
+
+ fn open(&self, data: &BinaryView) -> Result<Ref<BinaryView>> {
+ let handle = unsafe { BNCreateBinaryViewOfType(self.as_ref().0, data.handle) };
+
+ if handle.is_null() {
+ error!("failed to create BinaryView of BinaryViewType '{}'", self.name());
+ return Err(());
+ }
+
+ unsafe {
+ Ok(Ref::new(BinaryView::from_raw(handle)))
+ }
+ }
+}
+
+impl<T: BinaryViewTypeBase> BinaryViewTypeExt for T {}
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash)]
+pub struct BinaryViewType(*mut BNBinaryViewType);
+
+unsafe impl Send for BinaryViewType {}
+unsafe impl Sync for BinaryViewType {}
+
+impl BinaryViewType {
+ pub fn list_all() -> Array<BinaryViewType> {
+ unsafe {
+ let mut count: usize = 0;
+ let types = BNGetBinaryViewTypes(&mut count as *mut _);
+
+ Array::new(types, count, ())
+ }
+ }
+
+ pub fn list_valid_types_for(data: &BinaryView) -> Array<BinaryViewType> {
+ unsafe {
+ let mut count: usize = 0;
+ let types = BNGetBinaryViewTypesForData(data.handle, &mut count as *mut _);
+
+ Array::new(types, count, ())
+ }
+ }
+
+ /// Looks up a BinaryViewType by its short name
+ pub fn by_name<N: BnStrCompatible>(name: N) -> Result<Self> {
+ let bytes = name.as_bytes_with_nul();
+
+ let res = unsafe { BNGetBinaryViewTypeByName(bytes.as_ref().as_ptr() as *const _) };
+
+ match res.is_null() {
+ false => Ok(BinaryViewType(res)),
+ true => Err(()),
+ }
+ }
+
+}
+
+impl AsRef<BinaryViewType> for BinaryViewType {
+ fn as_ref(&self) -> &Self {
+ self
+ }
+}
+
+impl BinaryViewTypeBase for BinaryViewType {
+ fn is_valid_for(&self, data: &BinaryView) -> bool {
+ unsafe { BNIsBinaryViewTypeValidForData(self.0, data.handle) }
+ }
+
+ fn load_settings_for_data(&self, data: &BinaryView) -> Ref<Settings> {
+ unsafe {
+ Ref::new(Settings::from_raw(BNGetBinaryViewLoadSettingsForData(self.0, data.handle)))
+ }
+ }
+}
+
+unsafe impl CoreOwnedArrayProvider for BinaryViewType {
+ type Raw = *mut BNBinaryViewType;
+ type Context = ();
+
+ unsafe fn free(raw: *mut Self::Raw, _count: usize, _context: &Self::Context) {
+ BNFreeBinaryViewTypeList(raw);
+ }
+}
+
+unsafe impl<'a> CoreOwnedArrayWrapper<'a> for BinaryViewType {
+ type Wrapped = BinaryViewType;
+
+ unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
+ BinaryViewType(*raw)
+ }
+}
+
+pub unsafe trait CustomBinaryView: 'static + BinaryViewBase + Sync + Sized {
+ type Args: Send;
+
+ fn new(handle: BinaryView, args: &Self::Args) -> Result<Self>;
+ fn init(&self, args: Self::Args) -> Result<()>;
+}
+
+pub trait CustomBinaryViewType: 'static + BinaryViewTypeBase + Sync {
+ fn create_custom_view<'builder>(&self, data: &BinaryView, builder: CustomViewBuilder<'builder, Self>) -> Result<CustomView<'builder>>;
+}
+
+/// Represents a request from the core to instantiate a custom BinaryView
+pub struct CustomViewBuilder<'a, T: CustomBinaryViewType + ?Sized> {
+ view_type: &'a T,
+ actual_parent: &'a BinaryView,
+}
+
+/// Represents a partially initialized custom `BinaryView` that should be returned to the core
+/// from the `create_custom_view` method of a `CustomBinaryViewType`.
+#[must_use]
+pub struct CustomView<'builder> {
+ // this object can't actually be treated like a real
+ // BinaryView as it isn't fully initialized until the
+ // core receives it from the BNCustomBinaryViewType::create
+ // callback.
+ handle: Ref<BinaryView>,
+ _builder: PhantomData<&'builder ()>,
+}
+
+
+/// Registers a custom `BinaryViewType` with the core.
+///
+/// The `constructor` argument is called immediately after successful registration of the type with
+/// the core. The `BinaryViewType` argument passed to `constructor` is the object that the
+/// `AsRef<BinaryViewType>`
+/// implementation of the `CustomBinaryViewType` must return.
+pub fn register_view_type<S, T, F>(name: S, long_name: S, constructor: F) -> &'static T
+where
+ S: BnStrCompatible,
+ T: CustomBinaryViewType,
+ F: FnOnce(BinaryViewType) -> T,
+{
+ extern "C" fn cb_valid<T>(ctxt: *mut c_void, data: *mut BNBinaryView) -> bool
+ where
+ T: CustomBinaryViewType
+ {
+ ffi_wrap!("BinaryViewTypeBase::is_valid_for", unsafe {
+ let view_type = &*(ctxt as *mut T);
+ let data = BinaryView::from_raw(data);
+
+ view_type.is_valid_for(&data)
+ })
+ }
+
+ extern "C" fn cb_create<T>(ctxt: *mut c_void, data: *mut BNBinaryView) -> *mut BNBinaryView
+ where
+ T: CustomBinaryViewType
+ {
+ ffi_wrap!("BinaryViewTypeBase::create", unsafe {
+ let view_type = &*(ctxt as *mut T);
+ let data = BinaryView::from_raw(data);
+
+ let builder = CustomViewBuilder {
+ view_type: view_type,
+ actual_parent: &data,
+ };
+
+ if let Ok(bv) = view_type.create_custom_view(&data, builder) {
+ // 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
+ } else {
+ 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 {
+ ptr::null_mut()
+ })
+ }
+
+ extern "C" fn cb_load_settings<T>(ctxt: *mut c_void, data: *mut BNBinaryView) -> *mut BNSettings
+ where
+ T: CustomBinaryViewType
+ {
+ ffi_wrap!("BinaryViewTypeBase::load_settings", unsafe {
+ let view_type = &*(ctxt as *mut T);
+ let data = BinaryView::from_raw(data);
+
+ Ref::into_raw(view_type.load_settings_for_data(&data)).handle
+ })
+ }
+
+ let name = name.as_bytes_with_nul();
+ let name_ptr = name.as_ref().as_ptr() as *mut _;
+
+ let long_name = long_name.as_bytes_with_nul();
+ let long_name_ptr = long_name.as_ref().as_ptr() as *mut _;
+
+ let ctxt = Box::new(unsafe { mem::uninitialized::<T>() });
+ let ctxt = Box::into_raw(ctxt);
+
+ let mut bn_obj = BNCustomBinaryViewType {
+ context: ctxt as *mut _,
+ create: Some(cb_create::<T>),
+ parse: Some(cb_parse::<T>),
+ isValidForData: Some(cb_valid::<T>),
+ getLoadSettingsForData: Some(cb_load_settings::<T>),
+ };
+
+ unsafe {
+ let res = BNRegisterBinaryViewType(name_ptr, long_name_ptr, &mut bn_obj as *mut _);
+
+ if res.is_null() {
+ // avoid leaking the space allocated for the type, but also
+ // avoid running its Drop impl (if any -- not that there should
+ // be one since view types live for the life of the process)
+ mem::forget(*Box::from_raw(ctxt));
+
+ panic!("bvt registration failed");
+ }
+
+ ptr::write(ctxt, constructor(BinaryViewType(res)));
+
+ &*ctxt
+ }
+}
+
+impl<'a, T: CustomBinaryViewType> CustomViewBuilder<'a, T> {
+ /// Begins creating a custom BinaryView.
+ ///
+ /// This function may only be called from the `create_custom_view` function of a
+ /// `CustomBinaryViewType`.
+ ///
+ /// `parent` specifies the view that the core will treat as the parent view, that
+ /// Segments created against the created view will be backed by `parent`. It will
+ /// usually be (but is not required to be) the `data` argument of the `create_custom_view`
+ /// callback.
+ ///
+ /// `constructor` will not be called until well after the value returned by this function
+ /// has been returned by `create_custom_view` callback to the core, and may not ever
+ /// be called if the value returned by this function is dropped or leaked.
+ ///
+ /// # Errors
+ ///
+ /// This function will fail if the `FileMetadata` object associated with the *expected* parent
+ /// (i.e., the `data` argument passed to the `create_custom_view` function) already has an
+ /// associated `BinaryView` of the same `CustomBinaryViewType`. Multiple `BinaryView` objects
+ /// of the same `BinaryViewType` belonging to the same `FileMetadata` object is prohibited and
+ /// can cause strange, delayed segmentation faults.
+ ///
+ /// # Safety
+ ///
+ /// `constructor` should avoid doing anything with the object it returns, especially anything
+ /// that would cause the core to invoke any of the `BinaryViewBase` methods. The core isn't
+ /// going to consider the object fully initialized until after that callback has run.
+ ///
+ /// 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
+ pub fn create<V>(self, parent: &BinaryView, view_args: V::Args) -> Result<CustomView<'a>>
+ where
+ V: CustomBinaryView,
+ {
+ let file = self.actual_parent.metadata();
+ let view_type = self.view_type;
+
+ let view_name = view_type.name();
+
+ if let Ok(bv) = file.get_view_of_type(view_name.as_cstr()) {
+ // 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
+ // will run for the new view before we've even finished initializing,
+ // and that's all she wrote.
+ //
+ // 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!("attempt to create duplicate view of type '{}' (existing: {:?})", view_name.as_str(), bv.handle);
+
+ 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 CustomViewContext<V>
+ where
+ V: CustomBinaryView,
+ {
+ view: V,
+ raw_handle: *mut BNBinaryView,
+ initialized: bool,
+ args: V::Args,
+ }
+
+ extern "C" fn cb_init<V>(ctxt: *mut c_void) -> bool
+ where
+ V: CustomBinaryView,
+ {
+ ffi_wrap!("BinaryViewBase::init", unsafe {
+ let context = &mut *(ctxt as *mut CustomViewContext<V>);
+ let handle = BinaryView::from_raw(context.raw_handle);
+
+ if let Ok(v) = V::new(handle, &context.args) {
+ ptr::write(&mut context.view, v);
+ context.initialized = true;
+
+ if context.view.init(ptr::read(&context.args)).is_ok() {
+ true
+ } else {
+ error!("CustomBinaryView::init failed; custom view returned Err");
+ false
+ }
+ } else {
+ error!("CustomBinaryView::new failed; custom view returned Err");
+ false
+ }
+ })
+ }
+
+ extern "C" fn cb_free_object<V>(ctxt: *mut c_void)
+ where
+ V: CustomBinaryView,
+ {
+ 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
+
+ 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!");
+ }
+ }
+ })
+ }
+
+ extern "C" fn cb_read<V>(ctxt: *mut c_void, dest: *mut c_void, offset: u64, len: usize) -> usize
+ where
+ V: CustomBinaryView,
+ {
+ 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.read(dest, offset)
+ })
+ }
+
+ extern "C" fn cb_write<V>(ctxt: *mut c_void, offset: u64, src: *const c_void, len: usize) -> usize
+ where
+ V: CustomBinaryView,
+ {
+ 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.write(offset, src)
+ })
+ }
+
+ extern "C" fn cb_insert<V>(ctxt: *mut c_void, offset: u64, src: *const c_void, len: usize) -> usize
+ where
+ V: CustomBinaryView,
+ {
+ 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.insert(offset, src)
+ })
+ }
+
+ extern "C" fn cb_remove<V>(ctxt: *mut c_void, offset: u64, len: u64) -> usize
+ where
+ V: CustomBinaryView,
+ {
+ ffi_wrap!("BinaryViewBase::remove", unsafe {
+ let context = &*(ctxt as *mut CustomViewContext<V>);
+
+ context.view.remove(offset, len as usize)
+ })
+ }
+
+ extern "C" fn cb_modification<V>(ctxt: *mut c_void, offset: u64) -> ModificationStatus
+ where
+ V: CustomBinaryView,
+ {
+ ffi_wrap!("BinaryViewBase::modification_status", unsafe {
+ let context = &*(ctxt as *mut CustomViewContext<V>);
+
+ context.view.modification_status(offset)
+ })
+ }
+
+ extern "C" fn cb_offset_valid<V>(ctxt: *mut c_void, offset: u64) -> bool
+ where
+ V: CustomBinaryView,
+ {
+ ffi_wrap!("BinaryViewBase::offset_valid", unsafe {
+ let context = &*(ctxt as *mut CustomViewContext<V>);
+
+ context.view.offset_valid(offset)
+ })
+ }
+
+ extern "C" fn cb_offset_readable<V>(ctxt: *mut c_void, offset: u64) -> bool
+ where
+ V: CustomBinaryView,
+ {
+ ffi_wrap!("BinaryViewBase::readable", unsafe {
+ let context = &*(ctxt as *mut CustomViewContext<V>);
+
+ context.view.offset_readable(offset)
+ })
+ }
+
+ extern "C" fn cb_offset_writable<V>(ctxt: *mut c_void, offset: u64) -> bool
+ where
+ V: CustomBinaryView,
+ {
+ ffi_wrap!("BinaryViewBase::writable", unsafe {
+ let context = &*(ctxt as *mut CustomViewContext<V>);
+
+ context.view.offset_writable(offset)
+ })
+ }
+
+ extern "C" fn cb_offset_executable<V>(ctxt: *mut c_void, offset: u64) -> bool
+ where
+ V: CustomBinaryView,
+ {
+ ffi_wrap!("BinaryViewBase::offset_executable", unsafe {
+ let context = &*(ctxt as *mut CustomViewContext<V>);
+
+ context.view.offset_executable(offset)
+ })
+ }
+
+ extern "C" fn cb_offset_backed_by_file<V>(ctxt: *mut c_void, offset: u64) -> bool
+ where
+ V: CustomBinaryView,
+ {
+ ffi_wrap!("BinaryViewBase::offset_backed_by_file", unsafe {
+ let context = &*(ctxt as *mut CustomViewContext<V>);
+
+ context.view.offset_backed_by_file(offset)
+ })
+ }
+
+ extern "C" fn cb_next_valid_offset<V>(ctxt: *mut c_void, offset: u64) -> u64
+ where
+ V: CustomBinaryView,
+ {
+ ffi_wrap!("BinaryViewBase::next_valid_offset_after", unsafe {
+ let context = &*(ctxt as *mut CustomViewContext<V>);
+
+ context.view.next_valid_offset_after(offset)
+ })
+ }
+
+ extern "C" fn cb_start<V>(ctxt: *mut c_void) -> u64
+ where
+ V: CustomBinaryView,
+ {
+ ffi_wrap!("BinaryViewBase::start", unsafe {
+ let context = &*(ctxt as *mut CustomViewContext<V>);
+
+ context.view.start()
+ })
+ }
+
+ extern "C" fn cb_length<V>(ctxt: *mut c_void) -> u64
+ where
+ V: CustomBinaryView,
+ {
+ ffi_wrap!("BinaryViewBase::len", unsafe {
+ let context = &*(ctxt as *mut CustomViewContext<V>);
+
+ context.view.len() as u64
+ })
+ }
+
+ extern "C" fn cb_entry_point<V>(ctxt: *mut c_void) -> u64
+ where
+ V: CustomBinaryView,
+ {
+ ffi_wrap!("BinaryViewBase::entry_point", unsafe {
+ let context = &*(ctxt as *mut CustomViewContext<V>);
+
+ context.view.entry_point()
+ })
+ }
+
+ extern "C" fn cb_executable<V>(ctxt: *mut c_void) -> bool
+ where
+ V: CustomBinaryView,
+ {
+ ffi_wrap!("BinaryViewBase::executable", unsafe {
+ let context = &*(ctxt as *mut CustomViewContext<V>);
+
+ context.view.executable()
+ })
+ }
+
+ extern "C" fn cb_endianness<V>(ctxt: *mut c_void) -> Endianness
+ where
+ V: CustomBinaryView,
+ {
+ ffi_wrap!("BinaryViewBase::default_endianness", unsafe {
+ let context = &*(ctxt as *mut CustomViewContext<V>);
+
+ context.view.default_endianness()
+ })
+ }
+
+ extern "C" fn cb_relocatable<V>(ctxt: *mut c_void) -> bool
+ where
+ V: CustomBinaryView,
+ {
+ ffi_wrap!("BinaryViewBase::relocatable", unsafe {
+ let context = &*(ctxt as *mut CustomViewContext<V>);
+
+ context.view.relocatable()
+ })
+ }
+
+ extern "C" fn cb_address_size<V>(ctxt: *mut c_void) -> usize
+ where
+ V: CustomBinaryView,
+ {
+ ffi_wrap!("BinaryViewBase::address_size", unsafe {
+ let context = &*(ctxt as *mut CustomViewContext<V>);
+
+ context.view.address_size()
+ })
+ }
+
+ extern "C" fn cb_save<V>(ctxt: *mut c_void, _fa: *mut BNFileAccessor) -> bool
+ where
+ V: CustomBinaryView,
+ {
+ ffi_wrap!("BinaryViewBase::save", unsafe {
+ let _context = &*(ctxt as *mut CustomViewContext<V>);
+ false
+ })
+ }
+
+ let ctxt = Box::new(CustomViewContext::<V> {
+ view: unsafe { mem::uninitialized() },
+ raw_handle: ptr::null_mut(),
+ initialized: false,
+ args: view_args,
+ });
+
+ let ctxt = Box::into_raw(ctxt);
+
+ let mut bn_obj = BNCustomBinaryView {
+ context: ctxt as *mut _,
+ init: Some(cb_init::<V>),
+ freeObject: Some(cb_free_object::<V>),
+ externalRefTaken: None,
+ externalRefReleased: None,
+ read: Some(cb_read::<V>),
+ write: Some(cb_write::<V>),
+ insert: Some(cb_insert::<V>),
+ remove: Some(cb_remove::<V>),
+ getModification: Some(cb_modification::<V>),
+ isValidOffset: Some(cb_offset_valid::<V>),
+ isOffsetReadable: Some(cb_offset_readable::<V>),
+ isOffsetWritable: Some(cb_offset_writable::<V>),
+ isOffsetExecutable: Some(cb_offset_executable::<V>),
+ isOffsetBackedByFile: Some(cb_offset_backed_by_file::<V>),
+ getNextValidOffset: Some(cb_next_valid_offset::<V>),
+ getStart: Some(cb_start::<V>),
+ getLength: Some(cb_length::<V>),
+ getEntryPoint: Some(cb_entry_point::<V>),
+ isExecutable: Some(cb_executable::<V>),
+ getDefaultEndianness: Some(cb_endianness::<V>),
+ isRelocatable: Some(cb_relocatable::<V>),
+ getAddressSize: Some(cb_address_size::<V>),
+ save: Some(cb_save::<V>),
+ };
+
+ unsafe {
+ let res = BNCreateCustomBinaryView(view_name.as_cstr().as_ptr(), file.handle, parent.handle, &mut bn_obj);
+
+ if res.is_null() {
+ // TODO not sure when this can even happen, let alone what we're supposed to do about
+ // it. cb_init isn't normally called until later, and cb_free_object definitely won't
+ // have been called, so we'd at least be on the hook for freeing that stuff...
+ // probably.
+ //
+ // no idea how to force this to fail so I can test this, so just going to do the
+ // reasonable thing and panic.
+ panic!("failed to create custom binary view!");
+ }
+
+ (*ctxt).raw_handle = res;
+
+ Ok(CustomView {
+ handle: Ref::new(BinaryView::from_raw(res)),
+ _builder: PhantomData,
+ })
+ }
+ }
+
+ pub fn wrap_existing(self, wrapped_view: Ref<BinaryView>) -> Result<CustomView<'a>>
+ {
+ Ok(CustomView {
+ handle: wrapped_view,
+ _builder: PhantomData,
+ })
+ }
+}
+
diff --git a/rust/src/callingconvention.rs b/rust/src/callingconvention.rs
new file mode 100644
index 00000000..a607b67f
--- /dev/null
+++ b/rust/src/callingconvention.rs
@@ -0,0 +1,657 @@
+use std::mem;
+use std::ptr;
+use std::slice;
+use std::os::raw::c_void;
+use std::borrow::Borrow;
+use std::marker::PhantomData;
+
+use binaryninjacore_sys::*;
+
+use crate::architecture::{Architecture, ArchitectureExt, Register};
+use crate::rc::{Ref, RefCountable};
+use crate::string::*;
+
+// 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>;
+
+ fn arg_registers_shared_index(&self) -> bool;
+ fn reserved_stack_space_for_arg_registers(&self) -> bool;
+ fn stack_adjusted_on_return(&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 global_pointer_reg(&self) -> Option<<Self::Arch as Architecture>::Register>;
+
+ fn implicitly_defined_registers(&self) -> Vec<<Self::Arch as Architecture>::Register>;
+}
+
+pub fn register_calling_convention<A, N, C>(arch: &A, name: N, cc: C) -> Ref<CallingConvention<A>>
+where
+ A: Architecture,
+ N: BnStrCompatible,
+ C: 'static + CallingConventionBase<Arch=A>,
+{
+ struct CustomCallingConventionContext<C>
+ where
+ C: CallingConventionBase,
+ {
+ raw_handle: *mut BNCallingConvention,
+ cc: C,
+ }
+
+ extern "C" fn cb_free<C>(ctxt: *mut c_void)
+ where
+ C: CallingConventionBase,
+ {
+ ffi_wrap!("CallingConvention::free", unsafe {
+ let _ctxt = Box::from_raw(ctxt as *mut CustomCallingConventionContext<C>);
+ })
+ }
+
+ fn alloc_register_list<I: Iterator<Item=u32> + ExactSizeIterator>(items: I, count: &mut usize) -> *mut u32 {
+ let len = items.len();
+ *count = len;
+
+ if len == 0 {
+ ptr::null_mut()
+ } else {
+ let mut res = Vec::with_capacity(len + 1);
+
+ res.push(len as u32);
+
+ for i in items {
+ res.push(i.clone().into());
+ }
+
+ assert!(res.len() == len + 1);
+
+ let raw = res.as_mut_ptr();
+ mem::forget(res);
+
+ unsafe { raw.offset(1) }
+ }
+ }
+
+ extern "C" fn cb_free_register_list(_ctxt: *mut c_void, regs: *mut u32) {
+ ffi_wrap!("CallingConvention::free_register_list", unsafe {
+ if regs.is_null() {
+ return;
+ }
+
+ let actual_start = regs.offset(-1);
+ let len = *actual_start + 1;
+ let _regs = Vec::from_raw_parts(actual_start, len as usize, len as usize);
+ })
+ }
+
+ extern "C" fn cb_caller_saved<C>(ctxt: *mut c_void, count: *mut usize) -> *mut u32
+ where
+ C: CallingConventionBase,
+ {
+ ffi_wrap!("CallingConvention::caller_saved_registers", unsafe {
+ let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
+ let regs = ctxt.cc.caller_saved_registers();
+
+ alloc_register_list(regs.iter().map(|r| r.id()), &mut *count)
+ })
+ }
+
+ extern "C" fn cb_callee_saved<C>(ctxt: *mut c_void, count: *mut usize) -> *mut u32
+ where
+ C: CallingConventionBase,
+ {
+ ffi_wrap!("CallingConvention::callee_saved_registers", unsafe {
+ let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
+ let regs = ctxt.cc.callee_saved_registers();
+
+ alloc_register_list(regs.iter().map(|r| r.id()), &mut *count)
+ })
+ }
+
+ extern "C" fn cb_int_args<C>(ctxt: *mut c_void, count: *mut usize) -> *mut u32
+ where
+ C: CallingConventionBase,
+ {
+ ffi_wrap!("CallingConvention::int_arg_registers", unsafe {
+ let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
+ let regs = ctxt.cc.int_arg_registers();
+
+ alloc_register_list(regs.iter().map(|r| r.id()), &mut *count)
+ })
+ }
+
+ extern "C" fn cb_float_args<C>(ctxt: *mut c_void, count: *mut usize) -> *mut u32
+ where
+ C: CallingConventionBase,
+ {
+ ffi_wrap!("CallingConvention::float_arg_registers", unsafe {
+ let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
+ let regs = ctxt.cc.float_arg_registers();
+
+ alloc_register_list(regs.iter().map(|r| r.id()), &mut *count)
+ })
+ }
+
+ extern "C" fn cb_arg_shared_index<C>(ctxt: *mut c_void) -> bool
+ where
+ C: CallingConventionBase,
+ {
+ ffi_wrap!("CallingConvention::arg_registers_shared_index", unsafe {
+ let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
+
+ ctxt.cc.arg_registers_shared_index()
+ })
+ }
+
+ extern "C" fn cb_stack_reserved_arg_regs<C>(ctxt: *mut c_void) -> bool
+ where
+ C: CallingConventionBase,
+ {
+ ffi_wrap!("CallingConvention::reserved_stack_space_for_arg_registers", unsafe {
+ let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
+
+ ctxt.cc.reserved_stack_space_for_arg_registers()
+ })
+ }
+
+ extern "C" fn cb_stack_adjusted_on_return<C>(ctxt: *mut c_void) -> bool
+ where
+ C: CallingConventionBase,
+ {
+ ffi_wrap!("CallingConvention::stack_adjusted_on_return", unsafe {
+ let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
+
+ ctxt.cc.stack_adjusted_on_return()
+ })
+ }
+
+ extern "C" fn cb_return_int_reg<C>(ctxt: *mut c_void) -> u32
+ where
+ C: CallingConventionBase,
+ {
+ ffi_wrap!("CallingConvention::return_int_reg", unsafe {
+ let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
+
+ match ctxt.cc.return_int_reg() {
+ Some(r) => r.id(),
+ _ => 0xffff_ffff,
+ }
+ })
+ }
+
+ extern "C" fn cb_return_hi_int_reg<C>(ctxt: *mut c_void) -> u32
+ where
+ C: CallingConventionBase,
+ {
+ 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(),
+ _ => 0xffff_ffff,
+ }
+ })
+ }
+
+ extern "C" fn cb_return_float_reg<C>(ctxt: *mut c_void) -> u32
+ where
+ C: CallingConventionBase,
+ {
+ ffi_wrap!("CallingConvention::return_float_reg", unsafe {
+ let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
+
+ match ctxt.cc.return_float_reg() {
+ Some(r) => r.id(),
+ _ => 0xffff_ffff,
+ }
+ })
+ }
+
+ extern "C" fn cb_global_pointer_reg<C>(ctxt: *mut c_void) -> u32
+ where
+ C: CallingConventionBase,
+ {
+ ffi_wrap!("CallingConvention::global_pointer_reg", unsafe {
+ let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
+
+ match ctxt.cc.global_pointer_reg() {
+ Some(r) => r.id(),
+ _ => 0xffff_ffff,
+ }
+ })
+ }
+
+ extern "C" fn cb_implicitly_defined_registers<C>(ctxt: *mut c_void, count: *mut usize) -> *mut u32
+ where
+ C: CallingConventionBase,
+ {
+ ffi_wrap!("CallingConvention::implicitly_defined_registers", unsafe {
+ let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
+ let regs = ctxt.cc.implicitly_defined_registers();
+
+ alloc_register_list(regs.iter().map(|r| r.id()), &mut *count)
+ })
+ }
+
+ extern "C" fn cb_incoming_reg_value<C>(_ctxt: *mut c_void, _reg: u32, _func: *mut BNFunction, val: *mut BNRegisterValue)
+ where
+ C: CallingConventionBase,
+ {
+ ffi_wrap!("CallingConvention::incoming_reg_value", unsafe {
+ //let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
+ let val = &mut *val;
+
+ val.state = BNRegisterValueType::EntryValue;
+ val.value = _reg as i64;
+ })
+ }
+
+ extern "C" fn cb_incoming_flag_value<C>(_ctxt: *mut c_void, _flag: u32, _func: *mut BNFunction, val: *mut BNRegisterValue)
+ where
+ C: CallingConventionBase,
+ {
+ ffi_wrap!("CallingConvention::incoming_flag_value", unsafe {
+ //let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
+ let val = &mut *val;
+
+ val.state = BNRegisterValueType::EntryValue;
+ val.value = _flag as i64;
+ })
+ }
+
+ extern "C" fn cb_incoming_var_for_param<C>(ctxt: *mut c_void, var: *const BNVariable, _func: *mut BNFunction, param: *mut BNVariable)
+ where
+ C: CallingConventionBase,
+ {
+ ffi_wrap!("CallingConvention::incoming_var_for_param", unsafe {
+ let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
+ ptr::write(param, BNGetDefaultIncomingVariableForParameterVariable(ctxt.raw_handle, var));
+ })
+ }
+
+ extern "C" fn cb_incoming_param_for_var<C>(ctxt: *mut c_void, var: *const BNVariable, _func: *mut BNFunction, param: *mut BNVariable)
+ where
+ C: CallingConventionBase,
+ {
+ ffi_wrap!("CallingConvention::incoming_var_for_param", unsafe {
+ let ctxt = &*(ctxt as *mut CustomCallingConventionContext<C>);
+ ptr::write(param, BNGetDefaultParameterVariableForIncomingVariable(ctxt.raw_handle, var));
+ })
+ }
+
+ let name = name.as_bytes_with_nul();
+ let raw = Box::into_raw(Box::new(CustomCallingConventionContext {
+ raw_handle: ptr::null_mut(),
+ cc: cc,
+ }));
+ let mut cc = BNCustomCallingConvention {
+ context: raw as *mut _,
+
+ freeObject: Some(cb_free::<C>),
+
+ getCallerSavedRegisters: Some(cb_caller_saved::<C>),
+ getCalleeSavedRegisters: Some(cb_callee_saved::<C>),
+ getIntegerArgumentRegisters: Some(cb_int_args::<C>),
+ getFloatArgumentRegisters: Some(cb_float_args::<C>),
+ freeRegisterList: Some(cb_free_register_list),
+
+ areArgumentRegistersSharedIndex: Some(cb_arg_shared_index::<C>),
+ isStackReservedForArgumentRegisters: Some(cb_stack_reserved_arg_regs::<C>),
+ isStackAdjustedOnReturn: Some(cb_stack_adjusted_on_return::<C>),
+
+ getIntegerReturnValueRegister: Some(cb_return_int_reg::<C>),
+ getHighIntegerReturnValueRegister: Some(cb_return_hi_int_reg::<C>),
+ getFloatReturnValueRegister: Some(cb_return_float_reg::<C>),
+ getGlobalPointerRegister: Some(cb_global_pointer_reg::<C>),
+
+ getImplicitlyDefinedRegisters: Some(cb_implicitly_defined_registers::<C>),
+ getIncomingRegisterValue: Some(cb_incoming_reg_value::<C>),
+ getIncomingFlagValue: Some(cb_incoming_flag_value::<C>),
+ getIncomingVariableForParameterVariable: Some(cb_incoming_var_for_param::<C>),
+ getParameterVariableForIncomingVariable: Some(cb_incoming_param_for_var::<C>),
+ };
+
+ unsafe {
+ let cc_name = name.as_ref().as_ptr() as *mut _;
+ let result = BNCreateCallingConvention(arch.as_ref().0, cc_name, &mut cc);
+
+ assert!(!result.is_null());
+
+ (*raw).raw_handle = result;
+
+ BNRegisterCallingConvention(arch.as_ref().0, result);
+
+ Ref::new(CallingConvention {
+ handle: result,
+ arch_handle: arch.handle(),
+ _arch: PhantomData,
+ })
+ }
+}
+
+pub struct CallingConvention<A: Architecture> {
+ pub(crate) handle: *mut BNCallingConvention,
+ pub(crate) arch_handle: A::Handle,
+ _arch: PhantomData<*mut A>,
+}
+
+unsafe impl<A: Architecture> Send for CallingConvention<A> {}
+unsafe impl<A: Architecture> Sync for CallingConvention<A> {}
+
+impl<A: Architecture> CallingConvention<A> {
+ pub(crate) unsafe fn from_raw(handle: *mut BNCallingConvention, arch: A::Handle) -> Self {
+ CallingConvention {
+ handle: handle,
+ arch_handle: arch,
+ _arch: PhantomData,
+ }
+ }
+}
+
+impl<A: Architecture> Eq for CallingConvention<A> {}
+impl<A: Architecture> PartialEq for CallingConvention<A> {
+ fn eq(&self, rhs: &Self) -> bool {
+ self.handle == rhs.handle
+ }
+}
+
+use std::hash::{Hash, Hasher};
+impl<A: Architecture> Hash for CallingConvention<A> {
+ 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> {
+ 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).iter().map(|&r| {
+ arch.register_from_id(r).expect("bad reg id from CallingConvention")
+ }).collect();
+
+ BNFreeRegisterList(regs);
+
+ res
+ }
+ }
+
+ fn callee_saved_registers(&self) -> Vec<A::Register> {
+ 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).iter().map(|&r| {
+ arch.register_from_id(r).expect("bad reg id from CallingConvention")
+ }).collect();
+
+ BNFreeRegisterList(regs);
+
+ res
+ }
+ }
+
+ fn int_arg_registers(&self) -> Vec<A::Register> {
+ Vec::new()
+ }
+
+ fn float_arg_registers(&self) -> Vec<A::Register> {
+ Vec::new()
+ }
+
+ fn arg_registers_shared_index(&self) -> bool {
+ unsafe { BNAreArgumentRegistersSharedIndex(self.handle) }
+ }
+
+ fn reserved_stack_space_for_arg_registers(&self) -> bool {
+ unsafe { BNIsStackReservedForArgumentRegisters(self.handle) }
+ }
+
+ fn stack_adjusted_on_return(&self) -> bool {
+ unsafe { BNIsStackAdjustedOnReturn(self.handle) }
+ }
+
+ fn return_int_reg(&self) -> Option<A::Register> {
+ match unsafe { BNGetIntegerReturnValueRegister(self.handle) } {
+ id if id < 0x8000_0000 => {
+ self.arch_handle.borrow().register_from_id(id)
+ }
+ _ => None,
+ }
+ }
+
+ fn return_hi_int_reg(&self) -> Option<A::Register> {
+ match unsafe { BNGetHighIntegerReturnValueRegister(self.handle) } {
+ id if id < 0x8000_0000 => {
+ self.arch_handle.borrow().register_from_id(id)
+ }
+ _ => None,
+ }
+ }
+
+ fn return_float_reg(&self) -> Option<A::Register> {
+ match unsafe { BNGetFloatReturnValueRegister(self.handle) } {
+ id if id < 0x8000_0000 => {
+ self.arch_handle.borrow().register_from_id(id)
+ }
+ _ => None,
+ }
+ }
+
+ fn global_pointer_reg(&self) -> Option<A::Register> {
+ match unsafe { BNGetGlobalPointerRegister(self.handle) } {
+ id if id < 0x8000_0000 => {
+ self.arch_handle.borrow().register_from_id(id)
+ }
+ _ => None,
+ }
+ }
+
+ fn implicitly_defined_registers(&self) -> Vec<A::Register> {
+ Vec::new()
+ }
+}
+
+impl<A: Architecture> ToOwned for CallingConvention<A> {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl<A: Architecture> RefCountable for CallingConvention<A> {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: BNNewCallingConventionReference(handle.handle),
+ arch_handle: handle.arch_handle.clone(),
+ _arch: PhantomData,
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeCallingConvention(handle.handle);
+ }
+}
+
+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>,
+
+ arg_registers_shared_index: bool,
+ reserved_stack_space_for_arg_registers: bool,
+ stack_adjusted_on_return: bool,
+
+ return_int_reg: Option<A::Register>,
+ return_hi_int_reg: Option<A::Register>,
+ return_float_reg: Option<A::Register>,
+
+ global_pointer_reg: Option<A::Register>,
+
+ implicitly_defined_registers: Vec<A::Register>,
+
+ arch_handle: A::Handle,
+ _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 {
+ self.$name = val;
+ self
+ }
+ }
+}
+
+macro_rules! reg_list {
+ ($name:ident) => {
+ pub fn $name(mut self, regs: &[&str]) -> Self {
+ { // FIXME NLL
+ let arch = self.arch_handle.borrow();
+ let arch_regs = regs.iter().filter_map(|&r| arch.register_by_name(r));
+
+ self.$name = arch_regs.collect();
+ }
+
+ self
+ }
+ }
+}
+
+macro_rules! reg {
+ ($name:ident) => {
+ pub fn $name(mut self, reg: &str) -> Self {
+ { // FIXME NLL
+ let arch = self.arch_handle.borrow();
+ self.$name = arch.register_by_name(reg);
+ }
+
+ self
+ }
+ }
+}
+
+impl<A: Architecture> ConventionBuilder<A> {
+ pub fn new(arch: &A) -> Self {
+ Self {
+ caller_saved_registers: Vec::new(),
+ callee_saved_registers: Vec::new(),
+ int_arg_registers: Vec::new(),
+ float_arg_registers: Vec::new(),
+
+ arg_registers_shared_index: false,
+ reserved_stack_space_for_arg_registers: false,
+ stack_adjusted_on_return: false,
+
+ return_int_reg: None,
+ return_hi_int_reg: None,
+ return_float_reg: None,
+
+ global_pointer_reg: None,
+
+ implicitly_defined_registers: Vec::new(),
+
+ arch_handle: arch.handle(),
+ _arch: PhantomData,
+ }
+ }
+
+ reg_list!(caller_saved_registers);
+ reg_list!(callee_saved_registers);
+ reg_list!(int_arg_registers);
+ reg_list!(float_arg_registers);
+
+ bool_arg!(arg_registers_shared_index);
+ bool_arg!(reserved_stack_space_for_arg_registers);
+ bool_arg!(stack_adjusted_on_return);
+
+ reg!(return_int_reg);
+ reg!(return_hi_int_reg);
+ reg!(return_float_reg);
+
+ reg!(global_pointer_reg);
+
+ reg_list!(implicitly_defined_registers);
+
+ pub fn register(self, name: &str) -> Ref<CallingConvention<A>> {
+ 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> {
+ self.caller_saved_registers.clone()
+ }
+
+ fn callee_saved_registers(&self) -> Vec<A::Register> {
+ self.caller_saved_registers.clone()
+ }
+
+ fn int_arg_registers(&self) -> Vec<A::Register> {
+ self.int_arg_registers.clone()
+ }
+
+ fn float_arg_registers(&self) -> Vec<A::Register> {
+ self.float_arg_registers.clone()
+ }
+
+ fn arg_registers_shared_index(&self) -> bool {
+ self.arg_registers_shared_index
+ }
+
+ fn reserved_stack_space_for_arg_registers(&self) -> bool {
+ self.reserved_stack_space_for_arg_registers
+ }
+
+ fn stack_adjusted_on_return(&self) -> bool {
+ self.stack_adjusted_on_return
+ }
+
+ fn return_int_reg(&self) -> Option<A::Register> {
+ self.return_int_reg.clone()
+ }
+
+ fn return_hi_int_reg(&self) -> Option<A::Register> {
+ self.return_hi_int_reg.clone()
+ }
+
+ fn return_float_reg(&self) -> Option<A::Register> {
+ self.return_float_reg.clone()
+ }
+
+ fn global_pointer_reg(&self) -> Option<A::Register> {
+ self.global_pointer_reg.clone()
+ }
+
+ fn implicitly_defined_registers(&self) -> Vec<A::Register> {
+ self.implicitly_defined_registers.clone()
+ }
+}
diff --git a/rust/src/command.rs b/rust/src/command.rs
new file mode 100644
index 00000000..70839f6b
--- /dev/null
+++ b/rust/src/command.rs
@@ -0,0 +1,263 @@
+use binaryninjacore_sys::{BNRegisterPluginCommand,
+ BNRegisterPluginCommandForAddress,
+ BNRegisterPluginCommandForRange,
+ BNRegisterPluginCommandForFunction,
+ BNBinaryView,
+ BNFunction};
+
+use std::ops::Range;
+use std::os::raw::c_void;
+
+use crate::binaryview::BinaryView;
+use crate::function::Function;
+use crate::string::BnStrCompatible;
+
+pub trait Command: 'static + Sync {
+ fn action(&self, view: &BinaryView);
+ fn valid(&self, view: &BinaryView) -> bool;
+}
+
+impl<T> Command for T
+where
+ T: 'static + Sync + Fn(&BinaryView),
+{
+ fn action(&self, view: &BinaryView) {
+ self(view);
+ }
+
+ fn valid(&self, _view: &BinaryView) -> bool {
+ true
+ }
+}
+
+pub fn register<S, C>(name: S, desc: S, command: C)
+where
+ S: BnStrCompatible,
+ C: Command,
+{
+ extern "C" fn cb_action<C>(ctxt: *mut c_void, view: *mut BNBinaryView)
+ where
+ C: Command,
+ {
+ ffi_wrap!("Command::action", unsafe {
+ let cmd = &*(ctxt as *const C);
+ let view = BinaryView::from_raw(view);
+
+ cmd.action(&view);
+ })
+ }
+
+ extern "C" fn cb_valid<C>(ctxt: *mut c_void, view: *mut BNBinaryView) -> bool
+ where
+ C: Command,
+ {
+ ffi_wrap!("Command::valid", unsafe {
+ let cmd = &*(ctxt as *const C);
+ let view = BinaryView::from_raw(view);
+
+ cmd.valid(&view)
+ })
+ }
+
+ let name = name.as_bytes_with_nul();
+ let desc = desc.as_bytes_with_nul();
+
+ let name_ptr = name.as_ref().as_ptr() as *mut _;
+ let desc_ptr = desc.as_ref().as_ptr() as *mut _;
+
+ let ctxt = Box::into_raw(Box::new(command));
+
+ unsafe {
+ BNRegisterPluginCommand(name_ptr, desc_ptr,
+ Some(cb_action::<C>), Some(cb_valid::<C>),
+ ctxt as *mut _);
+ }
+}
+
+pub trait AddressCommand: 'static + Sync {
+ fn action(&self, view: &BinaryView, addr: u64);
+ fn valid(&self, view: &BinaryView, addr: u64) -> bool;
+}
+
+impl<T> AddressCommand for T
+where
+ T: 'static + Sync + Fn(&BinaryView, u64),
+{
+ fn action(&self, view: &BinaryView, addr: u64) {
+ self(view, addr);
+ }
+
+ fn valid(&self, _view: &BinaryView, _addr: u64) -> bool {
+ true
+ }
+}
+
+pub fn register_for_address<S, C>(name: S, desc: S, command: C)
+where
+ S: BnStrCompatible,
+ C: AddressCommand,
+{
+ extern "C" fn cb_action<C>(ctxt: *mut c_void, view: *mut BNBinaryView, addr: u64)
+ where
+ C: AddressCommand,
+ {
+ ffi_wrap!("AddressCommand::action", unsafe {
+ let cmd = &*(ctxt as *const C);
+ let view = BinaryView::from_raw(view);
+
+ cmd.action(&view, addr);
+ })
+ }
+
+ extern "C" fn cb_valid<C>(ctxt: *mut c_void, view: *mut BNBinaryView, addr: u64) -> bool
+ where
+ C: AddressCommand,
+ {
+ ffi_wrap!("AddressCommand::valid", unsafe {
+ let cmd = &*(ctxt as *const C);
+ let view = BinaryView::from_raw(view);
+
+ cmd.valid(&view, addr)
+ })
+ }
+
+ let name = name.as_bytes_with_nul();
+ let desc = desc.as_bytes_with_nul();
+
+ let name_ptr = name.as_ref().as_ptr() as *mut _;
+ let desc_ptr = desc.as_ref().as_ptr() as *mut _;
+
+ let ctxt = Box::into_raw(Box::new(command));
+
+ unsafe {
+ BNRegisterPluginCommandForAddress(name_ptr, desc_ptr,
+ Some(cb_action::<C>), Some(cb_valid::<C>),
+ ctxt as *mut _);
+ }
+}
+
+pub trait RangeCommand: 'static + Sync {
+ fn action(&self, view: &BinaryView, range: Range<u64>);
+ fn valid(&self, view: &BinaryView, range: Range<u64>) -> bool;
+}
+
+impl<T> RangeCommand for T
+where
+ T: 'static + Sync + Fn(&BinaryView, Range<u64>),
+{
+ fn action(&self, view: &BinaryView, range: Range<u64>) {
+ self(view, range);
+ }
+
+ fn valid(&self, _view: &BinaryView, _range: Range<u64>) -> bool {
+ true
+ }
+}
+
+pub fn register_for_range<S, C>(name: S, desc: S, command: C)
+where
+ S: BnStrCompatible,
+ C: RangeCommand,
+{
+ extern "C" fn cb_action<C>(ctxt: *mut c_void, view: *mut BNBinaryView, addr: u64, len: u64)
+ where
+ C: RangeCommand,
+ {
+ ffi_wrap!("RangeCommand::action", unsafe {
+ let cmd = &*(ctxt as *const C);
+ let view = BinaryView::from_raw(view);
+
+ cmd.action(&view, addr .. addr.wrapping_add(len));
+ })
+ }
+
+ extern "C" fn cb_valid<C>(ctxt: *mut c_void, view: *mut BNBinaryView, addr: u64, len: u64) -> bool
+ where
+ C: RangeCommand,
+ {
+ ffi_wrap!("RangeCommand::valid", unsafe {
+ let cmd = &*(ctxt as *const C);
+ let view = BinaryView::from_raw(view);
+
+ cmd.valid(&view, addr .. addr.wrapping_add(len))
+ })
+ }
+
+ let name = name.as_bytes_with_nul();
+ let desc = desc.as_bytes_with_nul();
+
+ let name_ptr = name.as_ref().as_ptr() as *mut _;
+ let desc_ptr = desc.as_ref().as_ptr() as *mut _;
+
+ let ctxt = Box::into_raw(Box::new(command));
+
+ unsafe {
+ BNRegisterPluginCommandForRange(name_ptr, desc_ptr,
+ Some(cb_action::<C>), Some(cb_valid::<C>),
+ ctxt as *mut _);
+ }
+}
+
+pub trait FunctionCommand: 'static + Sync {
+ fn action(&self, view: &BinaryView, func: &Function);
+ fn valid(&self, view: &BinaryView, func: &Function) -> bool;
+}
+
+impl<T> FunctionCommand for T
+where
+ T: 'static + Sync + Fn(&BinaryView, &Function),
+{
+ fn action(&self, view: &BinaryView, func: &Function) {
+ self(view, func);
+ }
+
+ fn valid(&self, _view: &BinaryView, _func: &Function) -> bool {
+ true
+ }
+}
+
+pub fn register_for_function<S, C>(name: S, desc: S, command: C)
+where
+ S: BnStrCompatible,
+ C: FunctionCommand,
+{
+ extern "C" fn cb_action<C>(ctxt: *mut c_void, view: *mut BNBinaryView, func: *mut BNFunction)
+ where
+ C: FunctionCommand,
+ {
+ ffi_wrap!("FunctionCommand::action", unsafe {
+ let cmd = &*(ctxt as *const C);
+ let view = BinaryView::from_raw(view);
+ let func = Function::from_raw(func);
+
+ cmd.action(&view, &func);
+ })
+ }
+
+ extern "C" fn cb_valid<C>(ctxt: *mut c_void, view: *mut BNBinaryView, func: *mut BNFunction) -> bool
+ where
+ C: FunctionCommand,
+ {
+ ffi_wrap!("FunctionCommand::valid", unsafe {
+ let cmd = &*(ctxt as *const C);
+ let view = BinaryView::from_raw(view);
+ let func = Function::from_raw(func);
+
+ cmd.valid(&view, &func)
+ })
+ }
+
+ let name = name.as_bytes_with_nul();
+ let desc = desc.as_bytes_with_nul();
+
+ let name_ptr = name.as_ref().as_ptr() as *mut _;
+ let desc_ptr = desc.as_ref().as_ptr() as *mut _;
+
+ let ctxt = Box::into_raw(Box::new(command));
+
+ unsafe {
+ BNRegisterPluginCommandForFunction(name_ptr, desc_ptr,
+ Some(cb_action::<C>), Some(cb_valid::<C>),
+ ctxt as *mut _);
+ }
+}
diff --git a/rust/src/ffi.rs b/rust/src/ffi.rs
new file mode 100644
index 00000000..54279233
--- /dev/null
+++ b/rust/src/ffi.rs
@@ -0,0 +1,13 @@
+
+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);
+ process::abort()
+ })
+ }}
+}
+
diff --git a/rust/src/fileaccessor.rs b/rust/src/fileaccessor.rs
new file mode 100644
index 00000000..e3320be2
--- /dev/null
+++ b/rust/src/fileaccessor.rs
@@ -0,0 +1,78 @@
+use binaryninjacore_sys::BNFileAccessor;
+use std::io::{Read, Write, Seek, SeekFrom};
+use std::marker::PhantomData;
+use std::slice;
+
+pub struct FileAccessor<'a>
+{
+ pub(crate) api_object: BNFileAccessor,
+ _ref: PhantomData<&'a mut ()>,
+}
+
+impl<'a> FileAccessor<'a>
+{
+ pub fn new<F>(f: &'a mut F) -> Self
+ where
+ F: 'a + Read + Write + Seek + Sized
+ {
+ use std::os::raw::c_void;
+
+ extern "C" fn cb_get_length<F>(ctxt: *mut c_void) -> u64
+ where
+ F: Read + Write + Seek + Sized
+ {
+ let f = unsafe { &mut *(ctxt as *mut F) };
+
+ match f.seek(SeekFrom::End(0)) {
+ Ok(len) => len,
+ Err(_) => 0,
+ }
+ }
+
+ extern "C" fn cb_read<F>(ctxt: *mut c_void, dest: *mut c_void, offset: u64, len: usize) -> usize
+ where
+ F: Read + Write + Seek + Sized
+ {
+ let f = unsafe { &mut *(ctxt as *mut F) };
+ let dest = unsafe { slice::from_raw_parts_mut(dest as *mut u8, len) };
+
+ if !f.seek(SeekFrom::Start(offset)).is_ok() {
+ debug!("Failed to seek to offset {:x}", offset);
+ return 0;
+ }
+
+ match f.read(dest) {
+ Ok(len) => len,
+ Err(_) => 0,
+ }
+ }
+
+ extern "C" fn cb_write<F>(ctxt: *mut c_void, offset: u64, src: *const c_void, len: usize) -> usize
+ where
+ F: Read + Write + Seek + Sized
+ {
+ let f = unsafe { &mut *(ctxt as *mut F) };
+ let src = unsafe { slice::from_raw_parts(src as *const u8, len) };
+
+ if !f.seek(SeekFrom::Start(offset)).is_ok() {
+ return 0;
+ }
+
+ match f.write(src) {
+ Ok(len) => len,
+ Err(_) => 0,
+ }
+ }
+
+ Self {
+ api_object: BNFileAccessor {
+ context: f as *mut F as *mut _,
+ getLength: Some(cb_get_length::<F>),
+ read: Some(cb_read::<F>),
+ write: Some(cb_write::<F>),
+ },
+ _ref: PhantomData,
+ }
+ }
+}
+
diff --git a/rust/src/filemetadata.rs b/rust/src/filemetadata.rs
new file mode 100644
index 00000000..bb09a03f
--- /dev/null
+++ b/rust/src/filemetadata.rs
@@ -0,0 +1,174 @@
+use binaryninjacore_sys::{BNFileMetadata,
+ BNCreateFileMetadata,
+ BNNewFileReference,
+ BNFreeFileMetadata,
+ BNCloseFile,
+ //BNSetFileMetadataNavigationHandler,
+ BNIsFileModified,
+ BNIsAnalysisChanged,
+ BNMarkFileModified,
+ BNMarkFileSaved,
+ BNIsBackedByDatabase,
+ BNGetFilename,
+ BNSetFilename,
+ BNBeginUndoActions,
+ BNCommitUndoActions,
+ BNUndo,
+ BNRedo,
+ BNGetCurrentView,
+ BNGetCurrentOffset,
+ BNNavigate,
+ BNGetFileViewOfType};
+
+use crate::binaryview::BinaryView;
+
+use crate::rc::*;
+use crate::string::*;
+
+#[derive(PartialEq, Eq, Hash)]
+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 fn new() -> Ref<Self> {
+ unsafe {
+ Ref::new(
+ Self {
+ handle: BNCreateFileMetadata(),
+ }
+ )
+ }
+ }
+
+ pub fn with_filename<S: BnStrCompatible>(name: S) -> Ref<Self> {
+ let ret = FileMetadata::new();
+ ret.set_filename(name);
+ ret
+ }
+
+ pub fn close(&self) {
+ unsafe { BNCloseFile(self.handle); }
+ }
+
+ pub fn filename(&self) -> BnString {
+ unsafe {
+ let raw = BNGetFilename(self.handle);
+ BnString::from_raw(raw)
+ }
+ }
+
+ pub fn set_filename<S: BnStrCompatible>(&self, name: S) {
+ let name = name.as_bytes_with_nul();
+
+ unsafe { BNSetFilename(self.handle, name.as_ref().as_ptr() as *mut _); }
+ }
+
+ pub fn is_modified(&self) -> bool {
+ unsafe { BNIsFileModified(self.handle) }
+ }
+
+ pub fn mark_modified(&self) {
+ unsafe { BNMarkFileModified(self.handle); }
+ }
+
+ pub fn is_analysis_changed(&self) -> bool {
+ unsafe { BNIsAnalysisChanged(self.handle) }
+ }
+
+ pub fn mark_saved(&self) {
+ unsafe { BNMarkFileSaved(self.handle); }
+ }
+
+ pub fn is_database_backed(&self) -> bool {
+ unsafe { BNIsBackedByDatabase(self.handle) }
+ }
+
+ pub fn begin_undo_actions(&self) {
+ unsafe { BNBeginUndoActions(self.handle); }
+ }
+
+ pub fn commit_undo_actions(&self) {
+ unsafe { BNCommitUndoActions(self.handle); }
+ }
+
+ pub fn undo(&self) {
+ unsafe { BNUndo(self.handle); }
+ }
+
+ pub fn redo(&self) {
+ unsafe { BNRedo(self.handle); }
+ }
+
+ pub fn current_view(&self) -> BnString {
+ unsafe {
+ BnString::from_raw(BNGetCurrentView(self.handle))
+ }
+ }
+
+ pub fn current_offset(&self) -> u64 {
+ unsafe { BNGetCurrentOffset(self.handle) }
+ }
+
+ pub fn navigate_to<S: BnStrCompatible>(&self, view: S, offset: u64) -> Result<(), ()> {
+ let view = view.as_bytes_with_nul();
+
+ unsafe {
+ if BNNavigate(self.handle, view.as_ref().as_ptr() as *const _, offset) {
+ Ok(())
+ } else {
+ Err(())
+ }
+ }
+ }
+
+ pub fn get_view_of_type<S: BnStrCompatible>(&self, view: S) -> Result<Ref<BinaryView>, ()> {
+ let view = view.as_bytes_with_nul();
+
+ unsafe {
+ let res = BNGetFileViewOfType(self.handle, view.as_ref().as_ptr() as *const _);
+
+ if res.is_null() {
+ Err(())
+ } else {
+ Ok(Ref::new(BinaryView::from_raw(res)))
+ }
+ }
+ }
+}
+
+impl ToOwned for FileMetadata {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for FileMetadata {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: BNNewFileReference(handle.handle),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeFileMetadata(handle.handle);
+ }
+}
+
+
+/*
+BNCreateDatabase,
+BNCreateDatabaseWithProgress,
+BNOpenExistingDatabase,
+BNOpenExistingDatabaseWithProgress,
+*/
+
diff --git a/rust/src/function.rs b/rust/src/function.rs
new file mode 100644
index 00000000..9a65f837
--- /dev/null
+++ b/rust/src/function.rs
@@ -0,0 +1,260 @@
+use std::fmt;
+
+use binaryninjacore_sys::*;
+
+use crate::architecture::CoreArchitecture;
+use crate::binaryview::{BinaryView, BinaryViewExt};
+use crate::platform::Platform;
+use crate::symbol::Symbol;
+use crate::basicblock::{BasicBlock, BlockContext};
+use crate::types::Type;
+
+use crate::llil;
+
+use crate::string::*;
+use crate::rc::*;
+
+pub struct Location {
+ pub arch: Option<CoreArchitecture>,
+ pub addr: u64,
+}
+
+impl From<u64> for Location {
+ fn from(addr: u64) -> Self {
+ Location {
+ arch: None,
+ addr: addr,
+ }
+ }
+}
+
+impl From<(CoreArchitecture, u64)> for Location {
+ fn from(loc: (CoreArchitecture, u64)) -> Self {
+ Location {
+ arch: Some(loc.0),
+ addr: loc.1,
+ }
+ }
+}
+
+pub struct NativeBlockIter {
+ arch: CoreArchitecture,
+ bv: Ref<BinaryView>,
+ cur: u64,
+ end: u64,
+}
+
+impl Iterator for NativeBlockIter {
+ type Item = u64;
+
+ fn next(&mut self) -> Option<u64> {
+ let res = self.cur;
+
+ if res >= self.end {
+ None
+ } else {
+ self.bv.get_instruction_len(&self.arch, res).map(|x| {
+ self.cur += x as u64;
+ res
+ }).or_else(|| {
+ self.cur = self.end;
+ None
+ })
+ }
+ }
+}
+
+#[derive(Clone)]
+pub struct NativeBlock {
+ _priv: (),
+}
+
+impl NativeBlock {
+ pub(crate) fn new() -> Self {
+ NativeBlock { _priv: () }
+ }
+}
+
+impl BlockContext for NativeBlock {
+ type Iter = NativeBlockIter;
+ type Instruction = u64;
+
+ fn start(&self, block: &BasicBlock<Self>) -> u64 {
+ block.raw_start()
+ }
+
+ fn iter(&self, block: &BasicBlock<Self>) -> NativeBlockIter {
+ NativeBlockIter {
+ arch: block.arch(),
+ bv: block.function().view(),
+ cur: block.raw_start(),
+ end: block.raw_end(),
+ }
+ }
+}
+
+#[derive(PartialEq, Eq, Hash)]
+pub struct Function {
+ handle: *mut BNFunction,
+}
+
+unsafe impl Send for Function {}
+unsafe impl Sync for Function {}
+
+impl Function {
+ pub(crate) unsafe fn from_raw(handle: *mut BNFunction) -> Self {
+ Self { handle }
+ }
+
+ pub fn arch(&self) -> CoreArchitecture {
+ unsafe {
+ let arch = BNGetFunctionArchitecture(self.handle);
+ CoreArchitecture::from_raw(arch)
+ }
+ }
+
+ pub fn platform(&self) -> Ref<Platform> {
+ unsafe {
+ let plat = BNGetFunctionPlatform(self.handle);
+ Ref::new(Platform::from_raw(plat))
+ }
+ }
+
+ pub fn view(&self) -> Ref<BinaryView> {
+ unsafe {
+ let view = BNGetFunctionData(self.handle);
+ Ref::new(BinaryView::from_raw(view))
+ }
+ }
+
+ pub fn symbol(&self) -> Ref<Symbol> {
+ unsafe {
+ let sym = BNGetFunctionSymbol(self.handle);
+ Ref::new(Symbol::from_raw(sym))
+ }
+ }
+
+ pub fn start(&self) -> u64 {
+ unsafe { BNGetFunctionStart(self.handle) }
+ }
+
+ pub fn comment(&self) -> BnString {
+ unsafe { BnString::from_raw(BNGetFunctionComment(self.handle)) }
+ }
+
+ pub fn set_comment<S: BnStrCompatible>(&self, comment: S) {
+ let raw = comment.as_bytes_with_nul();
+
+ unsafe {
+ BNSetFunctionComment(self.handle, raw.as_ref().as_ptr() as *mut _);
+ }
+ }
+
+ pub fn comment_at(&self, addr: u64) -> BnString {
+ unsafe { BnString::from_raw(BNGetCommentForAddress(self.handle, addr)) }
+ }
+
+ pub fn set_comment_at<S: BnStrCompatible>(&self, addr: u64, comment: S) {
+ let raw = comment.as_bytes_with_nul();
+
+ unsafe {
+ BNSetCommentForAddress(self.handle, addr, raw.as_ref().as_ptr() as *mut _);
+ }
+ }
+
+ pub fn basic_blocks(&self) -> Array<BasicBlock<NativeBlock>> {
+ unsafe {
+ let mut count = 0;
+ let blocks = BNGetFunctionBasicBlockList(self.handle, &mut count);
+ let context = NativeBlock { _priv: () };
+
+ Array::new(blocks, count, context)
+ }
+ }
+
+ pub fn basic_block_containing(&self, arch: &CoreArchitecture, addr: u64) -> Option<Ref<BasicBlock<NativeBlock>>> {
+ unsafe {
+ let block = BNGetFunctionBasicBlockAtAddress(self.handle, arch.0, addr);
+ let context = NativeBlock { _priv: () };
+
+ if block.is_null() {
+ return None;
+ }
+
+ Some(Ref::new(BasicBlock::from_raw(block, context)))
+ }
+ }
+
+ pub fn low_level_il(&self) -> Result<Ref<llil::RegularFunction<CoreArchitecture>>, ()> {
+ unsafe {
+ let llil = BNGetFunctionLowLevelIL(self.handle);
+
+ if llil.is_null() {
+ return Err(());
+ }
+
+ Ok(Ref::new(llil::RegularFunction::from_raw(self.arch(), llil)))
+ }
+ }
+
+ pub fn lifted_il(&self) -> Result<Ref<llil::LiftedFunction<CoreArchitecture>>, ()> {
+ unsafe {
+ let llil = BNGetFunctionLiftedIL(self.handle);
+
+ if llil.is_null() {
+ return Err(());
+ }
+
+ Ok(Ref::new(llil::LiftedFunction::from_raw(self.arch(), llil)))
+ }
+ }
+
+ pub fn set_user_type(&self, t: &Type) {
+ unsafe {
+ BNSetFunctionUserType(self.handle, t.handle);
+ }
+ }
+}
+
+impl fmt::Debug for Function {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "<func '{}' ({}) {:x}>", self.symbol().name(), self.platform().name(), self.start())
+ }
+}
+
+impl ToOwned for Function {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for Function {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: BNNewFunctionReference(handle.handle),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeFunction(handle.handle);
+ }
+}
+
+unsafe impl CoreOwnedArrayProvider for Function {
+ type Raw = *mut BNFunction;
+ type Context = ();
+
+ unsafe fn free(raw: *mut *mut BNFunction, count: usize, _context: &()) {
+ BNFreeFunctionList(raw, count);
+ }
+}
+
+unsafe impl<'a> CoreOwnedArrayWrapper<'a> for Function {
+ type Wrapped = Guard<'a, Function>;
+
+ unsafe fn wrap_raw(raw: &'a *mut BNFunction, context: &'a ()) -> Guard<'a, Function> {
+ Guard::new(Function::from_raw(*raw), context)
+ }
+}
diff --git a/rust/src/headless.rs b/rust/src/headless.rs
new file mode 100644
index 00000000..e6b7c51c
--- /dev/null
+++ b/rust/src/headless.rs
@@ -0,0 +1,68 @@
+use std::env;
+use std::mem;
+use std::os::raw;
+use std::ffi::{OsStr, CString, CStr};
+use std::path::PathBuf;
+
+#[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
+}
+
+#[cfg(not(target_os = "windows"))]
+fn binja_path() -> PathBuf {
+ use std::os::unix::ffi::OsStrExt;
+
+ if let Ok(p) = env::var("BINJA_DIR") {
+ return PathBuf::from(p);
+ }
+
+ extern {
+ fn dladdr(addr: *mut raw::c_void, info: *mut DlInfo) -> raw::c_int;
+ }
+
+ unsafe {
+ let mut info: DlInfo = mem::uninitialized();
+
+ if dladdr(BNSetBundledPluginDirectory as *mut _, &mut info) == 0 {
+ panic!("Failed to find libbinaryninjacore path!");
+ }
+
+ if info.dli_fname.is_null() {
+ panic!("Failed to find libbinaryninjacore path!");
+ }
+
+ let path = CStr::from_ptr(info.dli_fname);
+ let path = OsStr::from_bytes(path.to_bytes());
+ let mut path = PathBuf::from(path);
+
+ path.pop();
+ path
+ }
+}
+
+
+#[cfg(target_os = "windows")]
+fn binja_path() -> PathBuf {
+ PathBuf::from(env::var("PROGRAMFILES").unwrap()).join("Vector35\\BinaryNinja\\")
+}
+
+use binaryninjacore_sys::{BNSetBundledPluginDirectory,
+ BNInitCorePlugins,
+ BNInitUserPlugins,
+ BNInitRepoPlugins};
+
+pub fn init() {
+ unsafe {
+ let path = binja_path().join("plugins").into_os_string();
+ let path = CString::new(path.into_string().unwrap()).unwrap();
+
+ BNSetBundledPluginDirectory(path.as_ptr());
+ BNInitCorePlugins();
+ BNInitUserPlugins();
+ BNInitRepoPlugins();
+ }
+}
diff --git a/rust/src/lib.rs b/rust/src/lib.rs
new file mode 100644
index 00000000..68df9948
--- /dev/null
+++ b/rust/src/lib.rs
@@ -0,0 +1,171 @@
+#[macro_use]
+extern crate log;
+#[cfg(feature = "rayon")]
+extern crate rayon;
+extern crate libc;
+extern crate binaryninjacore_sys;
+
+// 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)
+// headless wrapper for shutdown
+// platform cc
+
+#[macro_use]
+mod ffi;
+
+pub mod architecture;
+pub mod basicblock;
+pub mod binaryview;
+pub mod segment;
+pub mod section;
+pub mod symbol;
+pub mod callingconvention;
+pub mod command;
+pub mod fileaccessor;
+pub mod filemetadata;
+pub mod function;
+pub mod platform;
+pub mod llil;
+pub mod types;
+pub mod rc;
+pub mod headless;
+pub mod string;
+pub mod settings;
+
+pub use binaryninjacore_sys::BNEndianness as Endianness;
+pub use binaryninjacore_sys::BNBranchType as BranchType;
+
+pub mod logger {
+ use std::os::raw::{c_void, c_char};
+
+ use log;
+
+ use crate::string::BnStr;
+
+ use binaryninjacore_sys::{BNLogListener, BNUpdateLogListeners};
+ pub use binaryninjacore_sys::BNLogLevel as Level;
+
+ struct Logger;
+ static LOGGER: Logger = Logger;
+
+ impl log::Log for Logger {
+ fn enabled(&self, _metadata: &log::Metadata) -> bool {
+ true
+ }
+
+ fn log(&self, record: &log::Record) {
+ use binaryninjacore_sys::BNLog;
+ use std::ffi::CString;
+ use self::Level::*;
+ use log::Level;
+
+ let level = match record.level() {
+ Level::Error => ErrorLog,
+ Level::Warn => WarningLog,
+ Level::Info => InfoLog,
+ Level::Debug |
+ Level::Trace => DebugLog,
+ };
+
+ if let Ok(msg) = CString::new(format!("{}", record.args())) {
+ unsafe { BNLog(level, msg.as_ptr()); }
+ };
+ }
+
+ fn flush(&self) {}
+ }
+
+ /// Uses BinaryNinja's logging functionality as the sink for
+ /// Rust's `log` crate.
+ pub fn init(filter: log::LevelFilter) -> Result<(), log::SetLoggerError> {
+ log::set_max_level(filter);
+ log::set_logger(&LOGGER)
+ }
+
+ pub trait LogListener: 'static + Sync {
+ fn log(&self, level: Level, msg: &BnStr);
+ fn level(&self) -> Level;
+ fn close(&self) {}
+ }
+
+ pub struct LogGuard<L: LogListener> {
+ ctxt: *mut L,
+ }
+
+ impl<L: LogListener> Drop for LogGuard<L> {
+ fn drop(&mut self) {
+ use binaryninjacore_sys::BNUnregisterLogListener;
+
+ let mut bn_obj = BNLogListener {
+ context: self.ctxt as *mut _,
+ log: Some(cb_log::<L>),
+ close: Some(cb_close::<L>),
+ getLogLevel: Some(cb_level::<L>),
+ };
+
+ unsafe {
+ BNUnregisterLogListener(&mut bn_obj);
+ BNUpdateLogListeners();
+
+ let _listener = Box::from_raw(self.ctxt);
+ }
+ }
+ }
+
+ pub fn register_listener<L: LogListener>(listener: L) -> LogGuard<L> {
+ use binaryninjacore_sys::BNRegisterLogListener;
+
+ let raw = Box::into_raw(Box::new(listener));
+ let mut bn_obj = BNLogListener {
+ context: raw as *mut _,
+ log: Some(cb_log::<L>),
+ close: Some(cb_close::<L>),
+ getLogLevel: Some(cb_level::<L>),
+ };
+
+ unsafe {
+ BNRegisterLogListener(&mut bn_obj);
+ BNUpdateLogListeners();
+ }
+
+ LogGuard {
+ ctxt: raw,
+ }
+ }
+
+ extern "C" fn cb_log<L>(ctxt: *mut c_void, level: Level, msg: *const c_char)
+ where
+ L: LogListener,
+ {
+ ffi_wrap!("LogListener::log", unsafe {
+ let listener = &*(ctxt as *const L);
+ listener.log(level, BnStr::from_raw(msg));
+ })
+ }
+
+ extern "C" fn cb_close<L>(ctxt: *mut c_void)
+ where
+ L: LogListener,
+ {
+ ffi_wrap!("LogListener::close", unsafe {
+ let listener = &*(ctxt as *const L);
+ listener.close();
+ })
+ }
+
+ extern "C" fn cb_level<L>(ctxt: *mut c_void) -> Level
+ where
+ L: LogListener,
+ {
+ ffi_wrap!("LogListener::log", unsafe {
+ let listener = &*(ctxt as *const L);
+ listener.level()
+ })
+ }
+}
diff --git a/rust/src/llil/block.rs b/rust/src/llil/block.rs
new file mode 100644
index 00000000..543ab670
--- /dev/null
+++ b/rust/src/llil/block.rs
@@ -0,0 +1,91 @@
+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
new file mode 100644
index 00000000..1e1edda4
--- /dev/null
+++ b/rust/src/llil/expression.rs
@@ -0,0 +1,816 @@
+use binaryninjacore_sys::BNGetLowLevelILByIndex;
+use binaryninjacore_sys::BNLowLevelILInstruction;
+
+use std::marker::PhantomData;
+use std::fmt;
+
+use super::*;
+use super::operation;
+use super::operation::Operation;
+
+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 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_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_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_UNIMPL => ExprInfo::Unimpl(Operation::new(function, op)),
+ LLIL_UNIMPL_MEM => ExprInfo::UnimplMem(Operation::new(function, op)),
+
+ // TODO TEST_BIT ADD_OVERFLOW
+ _ => {
+ #[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) => {
+ 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) => {
+ 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) => {
+ 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_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_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>),
+ 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>),
+
+ 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>),
+
+ // 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) => 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) => 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) => 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) => &op.op,
+
+ Load(ref op) => &op.op,
+
+ Pop(ref op) => &op.op,
+
+ Reg(ref op) => &op.op,
+
+ Flag(ref op) => &op.op,
+
+ FlagBit(ref op) => &op.op,
+
+ Const(ref op) |
+ ConstPtr(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) => &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) => &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) => None,
+
+ Load(ref op) => op.flag_write(),
+
+ Pop(ref op) => op.flag_write(),
+
+ Reg(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(),
+
+ 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) => 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) => 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) => {
+ 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),
+ }
+ }
+
+ 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()),
+
+ 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) => {
+ 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) => {
+ 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/function.rs b/rust/src/llil/function.rs
new file mode 100644
index 00000000..ac50e72c
--- /dev/null
+++ b/rust/src/llil/function.rs
@@ -0,0 +1,198 @@
+use binaryninjacore_sys::BNLowLevelILFunction;
+use binaryninjacore_sys::BNNewLowLevelILFunctionReference;
+use binaryninjacore_sys::BNFreeLowLevelILFunction;
+
+use std::borrow::Borrow;
+use std::marker::PhantomData;
+
+use crate::basicblock::BasicBlock;
+use crate::rc::*;
+
+use super::*;
+
+#[derive(Copy, Clone, Debug)]
+pub struct Mutable;
+#[derive(Copy, Clone, Debug)]
+pub struct Finalized;
+
+pub trait FunctionMutability: 'static {}
+impl FunctionMutability for Mutable {}
+impl FunctionMutability for Finalized {}
+
+
+#[derive(Copy, Clone, Debug)]
+pub struct LiftedNonSSA;
+#[derive(Copy, Clone, Debug)]
+pub struct RegularNonSSA;
+
+pub trait NonSSAVariant: 'static {}
+impl NonSSAVariant for LiftedNonSSA {}
+impl NonSSAVariant for RegularNonSSA {}
+
+#[derive(Copy, Clone, Debug)]
+pub struct SSA;
+#[derive(Copy, Clone, Debug)]
+pub struct NonSSA<V: NonSSAVariant>(V);
+
+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(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.handle == rhs.handle
+ }
+}
+
+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.handle.hash(state);
+ }
+}
+
+impl<'func, A, M, F> Function<A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm
+{
+ pub(crate) unsafe fn from_raw(borrower: A::Handle, handle: *mut BNLowLevelILFunction) -> Self {
+ debug_assert!(!handle.is_null());
+
+ Self {
+ borrower,
+ handle,
+ _arch: PhantomData,
+ _mutability: PhantomData,
+ _form: PhantomData,
+ }
+ }
+
+ pub(crate) fn arch(&self) -> &A {
+ self.borrower.borrow()
+ }
+
+ pub fn instruction_at<L: Into<Location>>(&self, loc: L) -> Option<Instruction<A, M, F>> {
+ use binaryninjacore_sys::BNLowLevelILGetInstructionStart;
+ use binaryninjacore_sys::BNGetLowLevelILInstructionCount;
+
+ 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: 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: instr_idx,
+ }
+ }
+ }
+
+ pub fn instruction_count(&self) -> usize {
+ unsafe {
+ use binaryninjacore_sys::BNGetLowLevelILInstructionCount;
+ BNGetLowLevelILInstructionCount(self.handle)
+ }
+ }
+
+}
+
+// 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>
+where
+ A: 'func + Architecture,
+ F: FunctionForm
+{
+ pub fn basic_blocks(&self) -> Array<BasicBlock<LowLevelBlock<A, Finalized, F>>> {
+ use binaryninjacore_sys::BNGetLowLevelILBasicBlockList;
+
+ unsafe {
+ let mut count = 0;
+ let blocks = BNGetLowLevelILBasicBlockList(self.handle, &mut count);
+ let context = LowLevelBlock { function: self };
+
+ Array::new(blocks, count, context)
+ }
+ }
+}
+
+impl<'func, A, M, F> ToOwned for Function<A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm
+{
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+
+unsafe impl<'func, A, M, F> RefCountable for Function<A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm
+{
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ borrower: handle.borrower.clone(),
+ handle: BNNewLowLevelILFunctionReference(handle.handle),
+ _arch: PhantomData,
+ _mutability: PhantomData,
+ _form: PhantomData,
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeLowLevelILFunction(handle.handle);
+ }
+}
+
+impl<'func, A, M, F> fmt::Debug for Function<A, M, F>
+where
+ A: 'func + Architecture,
+ M: FunctionMutability,
+ F: FunctionForm,
+{
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "<llil func handle {:p}>", self.handle)
+ }
+}
diff --git a/rust/src/llil/instruction.rs b/rust/src/llil/instruction.rs
new file mode 100644
index 00000000..4d1554df
--- /dev/null
+++ b/rust/src/llil/instruction.rs
@@ -0,0 +1,175 @@
+use binaryninjacore_sys::BNGetLowLevelILByIndex;
+use binaryninjacore_sys::BNGetLowLevelILIndexForInstruction;
+use binaryninjacore_sys::BNLowLevelILInstruction;
+
+use std::marker::PhantomData;
+
+use super::*;
+use super::operation;
+use super::operation::Operation;
+
+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 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_SYSCALL => InstrInfo::Syscall(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 {
+ function: self.function,
+ expr_idx: expr_idx,
+ _ty: PhantomData,
+ };
+
+ 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) => visit!(fb, &op.target()),
+ _ => 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>),
+
+ 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>),
+ 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/lifting.rs b/rust/src/llil/lifting.rs
new file mode 100644
index 00000000..e50eaaf1
--- /dev/null
+++ b/rust/src/llil/lifting.rs
@@ -0,0 +1,1292 @@
+use std::marker::PhantomData;
+use std::mem;
+
+use crate::architecture::Register as ArchReg;
+use crate::architecture::{FlagWrite, Flag, FlagClass, FlagGroup, FlagRole, FlagCondition};
+use crate::architecture::Architecture;
+
+
+use super::*;
+
+pub trait Liftable<'func, A: 'func + Architecture> {
+ type Result: ExpressionResultType;
+
+ fn lift(il: &'func Function<A, Mutable, NonSSA<LiftedNonSSA>>, expr: Self)
+ -> Expression<'func, A, Mutable, NonSSA<LiftedNonSSA>, Self::Result>;
+}
+
+pub trait LiftableWithSize<'func, A: 'func + Architecture>: Liftable<'func, A, Result=ValueExpr> {
+ fn lift_with_size(il: &'func Function<A, Mutable, NonSSA<LiftedNonSSA>>, expr: Self, size: usize)
+ -> Expression<'func, A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr>;
+}
+
+use binaryninjacore_sys::BNRegisterOrConstant;
+
+#[derive(Copy, Clone)]
+pub enum RegisterOrConstant<R: ArchReg> {
+ Register(usize, Register<R>),
+ Constant(usize, u64),
+}
+
+impl<R: ArchReg> RegisterOrConstant<R> {
+ pub(crate) fn into_api(self) -> BNRegisterOrConstant {
+ match self {
+ RegisterOrConstant::Register(_, r) => BNRegisterOrConstant {
+ constant: false,
+ reg: r.id(),
+ value: 0,
+ },
+ RegisterOrConstant::Constant(_, value) => BNRegisterOrConstant {
+ constant: true,
+ reg: 0,
+ value: value,
+ }
+ }
+ }
+}
+
+// TODO flesh way out
+#[derive(Copy, Clone)]
+pub enum FlagWriteOp<R: ArchReg> {
+ SetReg(usize, RegisterOrConstant<R>),
+ SetRegSplit(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+
+ Sub(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+ Add(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+
+ Load(usize, RegisterOrConstant<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>),
+
+ Store(usize, RegisterOrConstant<R>, RegisterOrConstant<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>),
+
+ TestBit(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+ AddOverflow(usize, RegisterOrConstant<R>, RegisterOrConstant<R>),
+
+ Adc(usize, RegisterOrConstant<R>, RegisterOrConstant<R>, RegisterOrConstant<R>),
+ Sbb(usize, RegisterOrConstant<R>, RegisterOrConstant<R>, RegisterOrConstant<R>),
+ Rlc(usize, RegisterOrConstant<R>, RegisterOrConstant<R>, RegisterOrConstant<R>),
+ Rrc(usize, RegisterOrConstant<R>, RegisterOrConstant<R>, RegisterOrConstant<R>),
+
+ Pop(usize),
+
+ // TODO: floating point stuff, llil comparison ops that set flags, intrinsics
+}
+
+impl<R: ArchReg> FlagWriteOp<R> {
+ pub(crate) fn from_op<A>(arch: &A, size: usize, op: BNLowLevelILOperation, operands: &[BNRegisterOrConstant])
+ -> Option<Self>
+ where
+ A: Architecture<Register=R>,
+ R: ArchReg<InfoType=A::RegisterInfo>,
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::*;
+ use self::FlagWriteOp::*;
+
+ fn build_op<A, R>(arch: &A, size: usize, operand: &BNRegisterOrConstant) -> RegisterOrConstant<R>
+ where
+ A: Architecture<Register=R>,
+ R: ArchReg<InfoType=A::RegisterInfo>,
+ {
+ if operand.constant {
+ RegisterOrConstant::Constant(size, operand.value)
+ } else {
+ let il_reg = if 0x8000_0000 & operand.reg == 0 {
+ Register::ArchReg(arch.register_from_id(operand.reg).unwrap())
+ } else {
+ Register::Temp(operand.reg)
+ };
+
+ RegisterOrConstant::Register(size, il_reg)
+ }
+ }
+
+ macro_rules! op {
+ ($x:ident, $($ops:expr),*) => {
+ ( $x(size, $( build_op(arch, size, &operands[$ops]), )* ) )
+ };
+ }
+
+ Some(match (operands.len(), op) {
+ (1, LLIL_SET_REG) => op!(SetReg, 0),
+ (2, LLIL_SET_REG_SPLIT) => op!(SetRegSplit, 0, 1),
+
+ (2, LLIL_SUB) => op!(Sub, 0, 1),
+ (2, LLIL_ADD) => op!(Add, 0, 1),
+
+ (1, LLIL_LOAD) => op!(Load, 0),
+
+ (1, LLIL_PUSH) => op!(Push, 0),
+ (1, LLIL_NEG) => op!(Neg, 0),
+ (1, LLIL_NOT) => op!(Not, 0),
+ (1, LLIL_SX) => op!(Sx, 0),
+ (1, LLIL_ZX) => op!(Zx, 0),
+ (1, LLIL_LOW_PART) => op!(LowPart, 0),
+ (1, LLIL_BOOL_TO_INT) => op!(BoolToInt, 0),
+ (1, LLIL_FLOAT_TO_INT) => op!(FloatToInt, 0),
+
+ (2, LLIL_STORE) => op!(Store, 0, 1),
+
+ (2, LLIL_AND) => op!(And, 0, 1),
+ (2, LLIL_OR) => op!(Or, 0, 1),
+ (2, LLIL_XOR) => op!(Xor, 0, 1),
+ (2, LLIL_LSL) => op!(Lsl, 0, 1),
+ (2, LLIL_LSR) => op!(Lsr, 0, 1),
+ (2, LLIL_ASR) => op!(Asr, 0, 1),
+ (2, LLIL_ROL) => op!(Rol, 0, 1),
+ (2, LLIL_ROR) => op!(Ror, 0, 1),
+ (2, LLIL_MUL) => op!(Mul, 0, 1),
+ (2, LLIL_MULU_DP) => op!(MuluDp, 0, 1),
+ (2, LLIL_MULS_DP) => op!(MulsDp, 0, 1),
+ (2, LLIL_DIVU) => op!(Divu, 0, 1),
+ (2, LLIL_DIVS) => op!(Divs, 0, 1),
+ (2, LLIL_MODU) => op!(Modu, 0, 1),
+ (2, LLIL_MODS) => op!(Mods, 0, 1),
+ (2, LLIL_DIVU_DP) => op!(DivuDp, 0, 1),
+ (2, LLIL_DIVS_DP) => op!(DivsDp, 0, 1),
+ (2, LLIL_MODU_DP) => op!(ModuDp, 0, 1),
+ (2, LLIL_MODS_DP) => op!(ModsDp, 0, 1),
+
+ (2, LLIL_TEST_BIT) => op!(TestBit, 0, 1),
+ (2, LLIL_ADD_OVERFLOW) => op!(AddOverflow, 0, 1),
+
+ (3, LLIL_ADC) => op!(Adc, 0, 1, 2),
+ (3, LLIL_SBB) => op!(Sbb, 0, 1, 2),
+ (3, LLIL_RLC) => op!(Rlc, 0, 1, 2),
+ (3, LLIL_RRC) => op!(Rrc, 0, 1, 2),
+
+ (0, LLIL_POP) => op!(Pop, ),
+
+ _ => return None,
+ })
+ }
+
+ pub(crate) fn size_and_op(&self) -> (usize, BNLowLevelILOperation) {
+ use binaryninjacore_sys::BNLowLevelILOperation::*;
+ use self::FlagWriteOp::*;
+
+ match *self {
+ SetReg(size, ..) => (size, LLIL_SET_REG),
+ SetRegSplit(size, ..) => (size, LLIL_SET_REG_SPLIT),
+
+ Sub(size, ..) => (size, LLIL_SUB),
+ Add(size, ..) => (size, LLIL_ADD),
+
+ Load(size, ..) => (size, LLIL_LOAD),
+
+ Push(size, ..) => (size, LLIL_PUSH),
+ Neg(size, ..) => (size, LLIL_NEG),
+ Not(size, ..) => (size, LLIL_NOT),
+ Sx(size, ..) => (size, LLIL_SX),
+ Zx(size, ..) => (size, LLIL_ZX),
+ LowPart(size, ..) => (size, LLIL_LOW_PART),
+ BoolToInt(size, ..) => (size, LLIL_BOOL_TO_INT),
+ FloatToInt(size, ..) => (size, LLIL_FLOAT_TO_INT),
+
+ Store(size, ..) => (size, LLIL_STORE),
+
+ And(size, ..) => (size, LLIL_AND),
+ Or(size, ..) => (size, LLIL_OR),
+ Xor(size, ..) => (size, LLIL_XOR),
+ Lsl(size, ..) => (size, LLIL_LSL),
+ Lsr(size, ..) => (size, LLIL_LSR),
+ Asr(size, ..) => (size, LLIL_ASR),
+ Rol(size, ..) => (size, LLIL_ROL),
+ Ror(size, ..) => (size, LLIL_ROR),
+ Mul(size, ..) => (size, LLIL_MUL),
+ MuluDp(size, ..) => (size, LLIL_MULU_DP),
+ MulsDp(size, ..) => (size, LLIL_MULS_DP),
+ Divu(size, ..) => (size, LLIL_DIVU),
+ Divs(size, ..) => (size, LLIL_DIVS),
+ Modu(size, ..) => (size, LLIL_MODU),
+ Mods(size, ..) => (size, LLIL_MODS),
+ DivuDp(size, ..) => (size, LLIL_DIVU_DP),
+ DivsDp(size, ..) => (size, LLIL_DIVS_DP),
+ ModuDp(size, ..) => (size, LLIL_MODU_DP),
+ ModsDp(size, ..) => (size, LLIL_MODS_DP),
+
+ TestBit(size, ..) => (size, LLIL_TEST_BIT),
+ AddOverflow(size, ..) => (size, LLIL_ADD_OVERFLOW),
+
+ Adc(size, ..) => (size, LLIL_ADC),
+ Sbb(size, ..) => (size, LLIL_SBB),
+ Rlc(size, ..) => (size, LLIL_RLC),
+ Rrc(size, ..) => (size, LLIL_RRC),
+
+ Pop(size) => (size, LLIL_POP),
+ }
+ }
+
+ pub(crate) fn api_operands(&self) -> (usize, [BNRegisterOrConstant; 5]) {
+ use self::FlagWriteOp::*;
+
+ let mut operands: [BNRegisterOrConstant; 5] = unsafe { mem::zeroed() };
+
+ let count = match *self {
+ Pop(_) => 0,
+
+ SetReg(_, op0) |
+ Load(_, op0) |
+ Push(_, op0) |
+ Neg(_, op0) |
+ Not(_, op0) |
+ Sx(_, op0) |
+ Zx(_, op0) |
+ LowPart(_, op0) |
+ BoolToInt(_, op0) |
+ FloatToInt(_, op0) => {
+ operands[0] = op0.into_api();
+ 1
+ }
+
+ SetRegSplit(_, op0, op1) |
+ Sub(_, op0, op1) |
+ Add(_, op0, op1) |
+ Store(_, op0, op1) |
+ And(_, op0, op1) |
+ Or(_, op0, op1) |
+ Xor(_, op0, op1) |
+ Lsl(_, op0, op1) |
+ Lsr(_, op0, op1) |
+ Asr(_, op0, op1) |
+ Rol(_, op0, op1) |
+ Ror(_, op0, op1) |
+ Mul(_, op0, op1) |
+ MuluDp(_, op0, op1) |
+ MulsDp(_, op0, op1) |
+ Divu(_, op0, op1) |
+ Divs(_, op0, op1) |
+ Modu(_, op0, op1) |
+ Mods(_, op0, op1) |
+ DivuDp(_, op0, op1) |
+ DivsDp(_, op0, op1) |
+ ModuDp(_, op0, op1) |
+ ModsDp(_, op0, op1) |
+ TestBit(_, op0, op1) |
+ AddOverflow(_, op0, op1) => {
+ operands[0] = op0.into_api();
+ operands[1] = op1.into_api();
+ 2
+ }
+
+ Adc(_, op0, op1, op2) |
+ 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();
+ 3
+ }
+ };
+
+ (count, operands)
+ }
+}
+
+
+pub fn get_default_flag_write_llil<'func, A>(arch: &A, role: FlagRole, op: FlagWriteOp<A::Register>, il: &'func Lifter<A>)
+ -> LiftedExpr<'func, A>
+where
+ A: 'func + Architecture
+{
+ let (size, operation) = op.size_and_op();
+ let (count, operands) = op.api_operands();
+
+ let expr_idx = unsafe {
+ use binaryninjacore_sys::BNGetDefaultArchitectureFlagWriteLowLevelIL;
+ BNGetDefaultArchitectureFlagWriteLowLevelIL(arch.as_ref().0, operation, size, role,
+ operands.as_ptr() as *mut _, count, il.handle)
+ };
+
+ Expression {
+ function: il,
+ expr_idx: expr_idx,
+ _ty: PhantomData,
+ }
+}
+
+pub fn get_default_flag_cond_llil<'func, A>(arch: &A, cond: FlagCondition, class: Option<A::FlagClass>, il: &'func Lifter<A>)
+ -> LiftedExpr<'func, A>
+where
+ A: 'func + Architecture
+{
+ use binaryninjacore_sys::BNGetDefaultArchitectureFlagConditionLowLevelIL;
+
+ let handle = arch.as_ref();
+ let class_id = class.map(|c| c.id()).unwrap_or(0);
+
+ unsafe {
+ let expr_idx = BNGetDefaultArchitectureFlagConditionLowLevelIL(handle.0, cond, class_id, il.handle);
+
+ Expression {
+ function: il,
+ expr_idx: expr_idx,
+ _ty: PhantomData,
+ }
+ }
+}
+
+macro_rules! prim_int_lifter {
+ ($x:ty) => {
+ impl<'a, A: 'a + Architecture> Liftable<'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>
+ {
+ il.const_int(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>
+ {
+ let raw = val as i64;
+
+ #[cfg(debug_assertions)]
+ {
+ let is_safe = match raw.overflowing_shr(size as u32 * 8) {
+ (_, true) => true,
+ (res, false) => [-1, 0].contains(&res),
+ };
+
+ if !is_safe {
+ error!("il @ {:x} attempted to lift constant 0x{:x} as {} byte expr (won't fit!)",
+ il.current_address(), val, size);
+ }
+ }
+
+ il.const_int(size, raw as u64)
+ }
+ }
+ }
+}
+
+prim_int_lifter!(i8);
+prim_int_lifter!(i16);
+prim_int_lifter!(i32);
+prim_int_lifter!(i64);
+
+prim_int_lifter!(u8);
+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>
+ where R: Liftable<'a, A, Result=ValueExpr> + Into<Register<R>>
+{
+ type Result = ValueExpr;
+
+ fn lift(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, reg: Self)
+ -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, Self::Result>
+ {
+ match reg {
+ Register::ArchReg(r) => R::lift(il, r),
+ Register::Temp(t) => il.reg(il.arch().default_integer_size(), Register::Temp(t)),
+ }
+ }
+}
+
+impl<'a, R: ArchReg, A: 'a + Architecture> LiftableWithSize<'a, A> for Register<R>
+ where R: LiftableWithSize<'a, A> + Into<Register<R>>
+{
+ fn lift_with_size(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, reg: Self, size: usize)
+ -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr>
+ {
+ match reg {
+ Register::ArchReg(r) => R::lift_with_size(il, r, size),
+ Register::Temp(t) => il.reg(size, Register::Temp(t)),
+ }
+ }
+}
+
+
+impl<'a, R: ArchReg, A: 'a + Architecture> Liftable<'a, A> for RegisterOrConstant<R>
+ where R: LiftableWithSize<'a, A, Result=ValueExpr> + Into<Register<R>>
+{
+ type Result = ValueExpr;
+
+ fn lift(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, reg: Self)
+ -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, 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),
+ }
+ }
+}
+
+impl<'a, R: ArchReg, A: 'a + Architecture> LiftableWithSize<'a, A> for RegisterOrConstant<R>
+ where R: LiftableWithSize<'a, A> + Into<Register<R>>
+{
+ fn lift_with_size(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, reg: Self, size: usize)
+ -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, 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),
+ }
+ }
+}
+
+
+impl<'a, A, R> Liftable<'a, A> for Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R>
+where
+ A: 'a + Architecture,
+ R: ExpressionResultType,
+{
+ type Result = R;
+
+ fn lift(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, expr: Self)
+ -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, 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> {
+ fn lift_with_size(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, expr: Self, _size: usize)
+ -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, Self::Result>
+ {
+ #[cfg(debug_assertions)]
+ {
+ if let Some(expr_size) = expr.info().size() {
+ if expr_size != _size {
+ warn!("il @ {:x} attempted to lift {} byte expression as {} bytes",
+ il.current_address(), expr_size, _size);
+ }
+ }
+ }
+
+ Liftable::lift(il, expr)
+ }
+}
+
+
+impl<'func, A, R> Expression<'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)
+ }
+
+ self
+ }
+
+ pub fn append(self) {
+ let il = self.function;
+ il.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>>,
+ op: BNLowLevelILOperation,
+ size: usize,
+ flags: u32,
+ op1: u64,
+ op2: u64,
+ op3: u64,
+ op4: u64,
+ _ty: PhantomData<R>,
+}
+
+impl<'a, A, R> ExpressionBuilder<'a, A, R>
+where
+ A: 'a + Architecture,
+ R: ExpressionResultType,
+{
+ pub fn with_flag_write(mut self, flag_write: A::FlagWrite) -> Self {
+ // TODO verify valid id
+ self.flags = flag_write.id();
+ self
+ }
+
+ pub fn into_expr(self) -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R> {
+ self.into()
+ }
+
+ pub fn with_source_operand(self, op: u32) -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R> {
+ let expr = self.into_expr();
+ expr.with_source_operand(op)
+ }
+
+ pub fn append(self) {
+ let expr = self.into_expr();
+ let il = expr.function;
+
+ il.instruction(expr);
+ }
+}
+
+impl<'a, A, R> Into<Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R>> for ExpressionBuilder<'a, A, R>
+where
+ A: 'a + Architecture,
+ R: ExpressionResultType,
+{
+ fn into(self) -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, R> {
+ use binaryninjacore_sys::BNLowLevelILAddExpr;
+
+ let expr_idx = unsafe {
+ BNLowLevelILAddExpr(self.function.handle,
+ self.op, self.size, self.flags,
+ self.op1, self.op2, self.op3, self.op4)
+ };
+
+ Expression {
+ function: self.function,
+ expr_idx: expr_idx,
+ _ty: PhantomData,
+ }
+ }
+}
+
+impl<'a, A, R> Liftable<'a, A> for ExpressionBuilder<'a, A, R>
+where
+ A: 'a + Architecture,
+ R: ExpressionResultType,
+{
+ type Result = R;
+
+ fn lift(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, expr: Self)
+ -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, Self::Result>
+ {
+ debug_assert!(expr.function.handle == il.handle);
+
+ expr.into()
+ }
+}
+
+impl<'a, A> LiftableWithSize<'a, A> for ExpressionBuilder<'a, A, ValueExpr>
+where
+ A: 'a + Architecture,
+{
+ fn lift_with_size(il: &'a Function<A, Mutable, NonSSA<LiftedNonSSA>>, expr: Self, _size: usize)
+ -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, 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!("il @ {:x} attempted to lift {} byte expression builder as {} bytes",
+ il.current_address(), expr.size, _size);
+ }
+ }
+
+ Liftable::lift(il, expr)
+ }
+}
+
+
+macro_rules! no_arg_lifter {
+ ($name:ident, $op:ident, $result:ty) => {
+ pub fn $name(&self) -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, $result> {
+ use binaryninjacore_sys::BNLowLevelILOperation::$op;
+ use binaryninjacore_sys::BNLowLevelILAddExpr;
+
+ let expr_idx = unsafe {
+ BNLowLevelILAddExpr(self.handle, $op,
+ 0, 0, 0, 0, 0, 0)
+ };
+
+ Expression {
+ function: self,
+ expr_idx: expr_idx,
+ _ty: PhantomData,
+ }
+ }
+ }
+}
+
+macro_rules! sized_no_arg_lifter {
+ ($name:ident, $op:ident, $result:ty) => {
+ pub fn $name(&self, size: usize) -> ExpressionBuilder<A, $result>
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::$op;
+
+ ExpressionBuilder {
+ function: self,
+ op: $op,
+ size: size,
+ flags: 0,
+ op1: 0,
+ op2: 0,
+ op3: 0,
+ op4: 0,
+ _ty: PhantomData,
+ }
+ }
+ }
+}
+
+macro_rules! unsized_unary_op_lifter {
+ ($name:ident, $op:ident, $result:ty) => {
+ pub fn $name<'a, E>(&'a self, expr: E)
+ -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, $result>
+ where
+ E: Liftable<'a, A, Result=ValueExpr>
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::$op;
+ use binaryninjacore_sys::BNLowLevelILAddExpr;
+
+ let expr = E::lift(self, expr);
+
+ let expr_idx = unsafe {
+ BNLowLevelILAddExpr(self.handle, $op, 0, 0,
+ expr.expr_idx as u64, 0, 0, 0)
+ };
+
+ Expression {
+ function: self,
+ expr_idx: expr_idx,
+ _ty: PhantomData,
+ }
+ }
+ }
+}
+
+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>
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::$op;
+
+ let expr = E::lift_with_size(self, expr, size);
+
+ ExpressionBuilder {
+ function: self,
+ op: $op,
+ size: size,
+ flags: 0,
+ op1: expr.expr_idx as u64,
+ op2: 0,
+ op3: 0,
+ op4: 0,
+ _ty: PhantomData,
+ }
+ }
+ }
+}
+
+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>
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::$op;
+
+ let expr = E::lift(self, expr);
+
+ ExpressionBuilder {
+ function: self,
+ op: $op,
+ size: size,
+ flags: 0,
+ op1: expr.expr_idx as u64,
+ op2: 0,
+ op3: 0,
+ op4: 0,
+ _ty: PhantomData,
+ }
+ }
+ }
+}
+
+macro_rules! binary_op_lifter {
+ ($name:ident, $op:ident) => {
+ pub fn $name<'a, L, R>(&'a self, size: usize, left: L, right: R)
+ -> ExpressionBuilder<'a, A, ValueExpr>
+ where
+ L: LiftableWithSize<'a, A>,
+ R: LiftableWithSize<'a, A>
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::$op;
+
+ let left = L::lift_with_size(self, left, size);
+ let right = R::lift_with_size(self, right, size);
+
+ ExpressionBuilder {
+ function: self,
+ op: $op,
+ size: size,
+ flags: 0,
+ op1: left.expr_idx as u64,
+ op2: right.expr_idx as u64,
+ op3: 0,
+ op4: 0,
+ _ty: PhantomData,
+ }
+ }
+ }
+}
+
+macro_rules! binary_op_carry_lifter {
+ ($name:ident, $op:ident) => {
+ pub fn $name<'a, L, R, C>(&'a self, size: usize, left: L, right: R, carry: C)
+ -> ExpressionBuilder<'a, A, ValueExpr>
+ where
+ L: LiftableWithSize<'a, A>,
+ R: LiftableWithSize<'a, A>,
+ C: LiftableWithSize<'a, A>,
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::$op;
+
+ let left = L::lift_with_size(self, left, size);
+ let right = R::lift_with_size(self, right, size);
+ let carry = C::lift_with_size(self, carry, 1); // TODO 0?
+
+ ExpressionBuilder {
+ function: self,
+ op: $op,
+ size: size,
+ flags: 0,
+ op1: left.expr_idx as u64,
+ op2: right.expr_idx as u64,
+ op3: carry.expr_idx as u64,
+ op4: 0,
+ _ty: PhantomData,
+ }
+ }
+ }
+}
+
+impl<A> Function<A, Mutable, NonSSA<LiftedNonSSA>>
+where
+ A: Architecture,
+{
+ pub fn expression<'a, E: Liftable<'a, A>>(&'a self, expr: E)
+ -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, E::Result>
+ {
+ E::lift(self, expr)
+ }
+
+ pub fn instruction<'a, E: Liftable<'a, A>>(&'a self, expr: E)
+ {
+ let expr = self.expression(expr);
+
+ unsafe {
+ use binaryninjacore_sys::BNLowLevelILAddInstruction;
+ BNLowLevelILAddInstruction(self.handle, expr.expr_idx);
+ }
+ }
+
+
+ pub unsafe fn replace_expression<'a, E: Liftable<'a, A>>(
+ &'a self,
+ replaced_expr_index: usize,
+ replacement: E)
+ {
+ unsafe {
+ use binaryninjacore_sys::BNReplaceLowLevelILExpr;
+ use binaryninjacore_sys::BNGetLowLevelILExprCount;
+
+ if replaced_expr_index >= BNGetLowLevelILExprCount(self.handle) {
+ panic!("bad expr idx used: {} exceeds function bounds", replaced_expr_index);
+ }
+
+ let expr = self.expression(replacement);
+ BNReplaceLowLevelILExpr(self.handle, replaced_expr_index, expr.expr_idx);
+ }
+ }
+
+ pub fn const_int(&self, size: usize, val: u64)
+ -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr>
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_CONST;
+ use binaryninjacore_sys::BNLowLevelILAddExpr;
+
+ let expr_idx = unsafe {
+ BNLowLevelILAddExpr(self.handle, LLIL_CONST, size, 0,
+ val, 0, 0, 0)
+ };
+
+ Expression {
+ function: self,
+ expr_idx: expr_idx,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn const_ptr_sized(&self, size: usize, val: u64)
+ -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr>
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_CONST_PTR;
+ use binaryninjacore_sys::BNLowLevelILAddExpr;
+
+ let expr_idx = unsafe {
+ BNLowLevelILAddExpr(self.handle, LLIL_CONST_PTR, size, 0,
+ val, 0, 0, 0)
+ };
+
+ Expression {
+ function: self,
+ expr_idx: expr_idx,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn const_ptr(&self, val: u64)
+ -> Expression<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>
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_TRAP;
+ use binaryninjacore_sys::BNLowLevelILAddExpr;
+
+ let expr_idx = unsafe {
+ BNLowLevelILAddExpr(self.handle, LLIL_TRAP, 0, 0,
+ val, 0, 0, 0)
+ };
+
+ Expression {
+ function: self,
+ expr_idx: expr_idx,
+ _ty: PhantomData,
+ }
+ }
+
+ no_arg_lifter!(unimplemented, LLIL_UNIMPL, ValueExpr);
+ no_arg_lifter!(undefined, LLIL_UNDEF, VoidExpr);
+ no_arg_lifter!(nop, LLIL_NOP, VoidExpr);
+
+ no_arg_lifter!(no_ret, LLIL_NORET, VoidExpr);
+ no_arg_lifter!(syscall, LLIL_SYSCALL, VoidExpr);
+ no_arg_lifter!(bp, LLIL_BP, VoidExpr);
+
+ 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
+
+ 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>
+ where
+ C: Liftable<'b, A, Result=ValueExpr>,
+ {
+ use binaryninjacore_sys::BNLowLevelILIf;
+
+ let cond = C::lift(self, cond);
+
+ let expr_idx = unsafe {
+ BNLowLevelILIf(self.handle, cond.expr_idx as u64,
+ &t.0 as *const _ as *mut _,
+ &f.0 as *const _ as *mut _)
+ };
+
+ Expression {
+ function: self,
+ expr_idx: expr_idx,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn goto<'a: 'b, 'b>(&'a self, l: &'b Label)
+ -> Expression<'a, A, Mutable, NonSSA<LiftedNonSSA>, VoidExpr>
+ {
+ use binaryninjacore_sys::BNLowLevelILGoto;
+
+ let expr_idx = unsafe {
+ BNLowLevelILGoto(self.handle, &l.0 as *const _ as *mut _)
+ };
+
+ Expression {
+ function: self,
+ expr_idx: expr_idx,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn reg<R: Into<Register<A::Register>>>(&self, size: usize, reg: R)
+ -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr>
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_REG;
+ use binaryninjacore_sys::BNLowLevelILAddExpr;
+
+ // TODO verify valid id
+ let reg = match reg.into() {
+ Register::ArchReg(r) => r.id(),
+ Register::Temp(r) => 0x8000_0000 | r,
+ };
+
+ let expr_idx = unsafe {
+ BNLowLevelILAddExpr(self.handle, LLIL_REG, size, 0,
+ reg as u64, 0, 0, 0)
+ };
+
+ Expression {
+ function: self,
+ expr_idx: expr_idx,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn set_reg<'a, R, E>(&'a self, size: usize, dest_reg: R, expr: E)
+ -> ExpressionBuilder<'a, A, VoidExpr>
+ where
+ R: Into<Register<A::Register>>,
+ E: LiftableWithSize<'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 expr = E::lift_with_size(self, expr, size);
+
+ ExpressionBuilder {
+ function: self,
+ op: LLIL_SET_REG,
+ size: size,
+ flags: 0,
+ op1: dest_reg as u64,
+ op2: expr.expr_idx as u64,
+ op3: 0,
+ op4: 0,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn set_reg_split<'a, H, L, E>(&'a self, size: usize, hi_reg: H, lo_reg: L, expr: E)
+ -> ExpressionBuilder<'a, A, VoidExpr>
+ where
+ H: Into<Register<A::Register>>,
+ L: Into<Register<A::Register>>,
+ E: LiftableWithSize<'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 expr = E::lift_with_size(self, expr, size);
+
+ ExpressionBuilder {
+ function: self,
+ op: LLIL_SET_REG_SPLIT,
+ size: size,
+ flags: 0,
+ op1: hi_reg as u64,
+ op2: lo_reg as u64,
+ op3: expr.expr_idx as u64,
+ op4: 0,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn flag(&self, flag: A::Flag)
+ -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr>
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_FLAG;
+ use binaryninjacore_sys::BNLowLevelILAddExpr;
+
+ // TODO verify valid id
+ let expr_idx = unsafe {
+ BNLowLevelILAddExpr(self.handle, LLIL_FLAG, 0, 0,
+ flag.id() as u64, 0, 0, 0)
+ };
+
+ Expression {
+ function: self,
+ expr_idx: expr_idx,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn flag_cond(&self, cond: FlagCondition)
+ -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr>
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_FLAG_COND;
+ use binaryninjacore_sys::BNLowLevelILAddExpr;
+
+ // TODO verify valid id
+ let expr_idx = unsafe {
+ BNLowLevelILAddExpr(self.handle, LLIL_FLAG_COND, 0, 0,
+ cond as u64, 0, 0, 0)
+ };
+
+ Expression {
+ function: self,
+ expr_idx: expr_idx,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn flag_group(&self, group: A::FlagGroup)
+ -> Expression<A, Mutable, NonSSA<LiftedNonSSA>, ValueExpr>
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_FLAG_GROUP;
+ use binaryninjacore_sys::BNLowLevelILAddExpr;
+
+ // TODO verify valid id
+ let expr_idx = unsafe {
+ BNLowLevelILAddExpr(self.handle, LLIL_FLAG_GROUP, 0, 0,
+ group.id() as u64, 0, 0, 0)
+ };
+
+ Expression {
+ function: self,
+ expr_idx: expr_idx,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn set_flag<'a, E>(&'a self, dest_flag: A::Flag, expr: E)
+ -> ExpressionBuilder<'a, A, VoidExpr>
+ where
+ E: LiftableWithSize<'a, A>
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_SET_FLAG;
+
+ // TODO verify valid id
+
+ let expr = E::lift_with_size(self, expr, 0);
+
+ ExpressionBuilder {
+ function: self,
+ op: LLIL_SET_FLAG,
+ size: 0,
+ flags: 0,
+ op1: dest_flag.id() as u64,
+ op2: expr.expr_idx as u64,
+ op3: 0,
+ op4: 0,
+ _ty: PhantomData,
+ }
+ }
+
+ /*
+ * TODO
+ FlagBit(usize, Flag<A>, u64),
+ */
+
+ pub fn load<'a, E>(&'a self, size: usize, source_mem: E)
+ -> ExpressionBuilder<'a, A, ValueExpr>
+ where
+ E: Liftable<'a, A, Result=ValueExpr>
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_LOAD;
+
+ let expr = E::lift(self, source_mem);
+
+ ExpressionBuilder {
+ function: self,
+ op: LLIL_LOAD,
+ size: size,
+ flags: 0,
+ op1: expr.expr_idx as u64,
+ op2: 0,
+ op3: 0,
+ op4: 0,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn store<'a, D, V>(&'a self, size: usize, dest_mem: D, value: V)
+ -> ExpressionBuilder<'a, A, VoidExpr>
+ where
+ D: Liftable<'a, A, Result=ValueExpr>,
+ V: LiftableWithSize<'a, A>
+ {
+ use binaryninjacore_sys::BNLowLevelILOperation::LLIL_STORE;
+
+ let dest_mem = D::lift(self, dest_mem);
+ let value = V::lift_with_size(self, value, size);
+
+ ExpressionBuilder {
+ function: self,
+ op: LLIL_STORE,
+ size: size,
+ flags: 0,
+ op1: dest_mem.expr_idx as u64,
+ op2: value.expr_idx as u64,
+ op3: 0,
+ op4: 0,
+ _ty: PhantomData,
+ }
+ }
+
+ sized_unary_op_lifter!(push, LLIL_PUSH, VoidExpr);
+ sized_no_arg_lifter!(pop, LLIL_POP, ValueExpr);
+
+ size_changing_unary_op_lifter!(unimplemented_mem, LLIL_UNIMPL_MEM, ValueExpr);
+
+ sized_unary_op_lifter!(neg, LLIL_NEG, ValueExpr);
+ sized_unary_op_lifter!(not, LLIL_NOT, ValueExpr);
+
+ size_changing_unary_op_lifter!(sx, LLIL_SX, ValueExpr);
+ size_changing_unary_op_lifter!(zx, LLIL_ZX, ValueExpr);
+ size_changing_unary_op_lifter!(low_part, LLIL_LOW_PART, ValueExpr);
+
+ binary_op_lifter!(add, LLIL_ADD);
+ binary_op_lifter!(add_overflow, LLIL_ADD_OVERFLOW);
+ binary_op_lifter!(sub, LLIL_SUB);
+ binary_op_lifter!(and, LLIL_AND);
+ binary_op_lifter!(or, LLIL_OR);
+ binary_op_lifter!(xor, LLIL_XOR);
+ binary_op_lifter!(lsl, LLIL_LSL);
+ binary_op_lifter!(lsr, LLIL_LSR);
+ binary_op_lifter!(asr, LLIL_ASR);
+
+ binary_op_lifter!(rol, LLIL_ROL);
+ binary_op_lifter!(rlc, LLIL_RLC);
+ binary_op_lifter!(ror, LLIL_ROR);
+ binary_op_lifter!(rrc, LLIL_RRC);
+ binary_op_lifter!(mul, LLIL_MUL);
+ binary_op_lifter!(muls_dp, LLIL_MULS_DP);
+ binary_op_lifter!(mulu_dp, LLIL_MULU_DP);
+ binary_op_lifter!(divs, LLIL_DIVS);
+ binary_op_lifter!(divu, LLIL_DIVU);
+ binary_op_lifter!(mods, LLIL_MODS);
+ binary_op_lifter!(modu, LLIL_MODU);
+
+ binary_op_carry_lifter!(adc, LLIL_ADC);
+ binary_op_carry_lifter!(sbb, LLIL_SBB);
+
+
+ /*
+ DivsDp(usize, Expr, Expr, Expr, Option<A::FlagWrite>),
+ DivuDp(usize, Expr, Expr, Expr, Option<A::FlagWrite>),
+ ModsDp(usize, Expr, Expr, Expr, Option<A::FlagWrite>),
+ ModuDp(usize, Expr, Expr, Expr, Option<A::FlagWrite>),
+ */
+
+ // FlagCond(u32), // TODO
+
+ binary_op_lifter!(cmp_e, LLIL_CMP_E);
+ binary_op_lifter!(cmp_ne, LLIL_CMP_NE);
+ binary_op_lifter!(cmp_slt, LLIL_CMP_SLT);
+ binary_op_lifter!(cmp_ult, LLIL_CMP_ULT);
+ binary_op_lifter!(cmp_sle, LLIL_CMP_SLE);
+ binary_op_lifter!(cmp_ule, LLIL_CMP_ULE);
+ binary_op_lifter!(cmp_sge, LLIL_CMP_SGE);
+ binary_op_lifter!(cmp_uge, LLIL_CMP_UGE);
+ binary_op_lifter!(cmp_sgt, LLIL_CMP_SGT);
+ binary_op_lifter!(cmp_ugt, LLIL_CMP_UGT);
+ binary_op_lifter!(test_bit, LLIL_TEST_BIT);
+
+ // TODO no flags
+ size_changing_unary_op_lifter!(bool_to_int, LLIL_BOOL_TO_INT, ValueExpr);
+
+ pub fn current_address(&self) -> u64 {
+ use binaryninjacore_sys::BNLowLevelILGetCurrentAddress;
+ unsafe {
+ BNLowLevelILGetCurrentAddress(self.handle)
+ }
+ }
+
+ pub fn set_current_address<L: Into<Location>>(&self, loc: L) {
+ use binaryninjacore_sys::BNLowLevelILSetCurrentAddress;
+
+ let loc: Location = loc.into();
+ let arch = loc.arch.unwrap_or_else(|| *self.arch().as_ref());
+
+ unsafe { BNLowLevelILSetCurrentAddress(self.handle, arch.0, loc.addr); }
+ }
+
+ pub fn label_for_address<L: Into<Location>>(&self, loc: L) -> Option<&Label> {
+ use binaryninjacore_sys::BNGetLowLevelILLabelForAddress;
+
+ let loc: Location = loc.into();
+ let arch = loc.arch.unwrap_or_else(|| *self.arch().as_ref());
+
+ let res = unsafe {
+ BNGetLowLevelILLabelForAddress(self.handle, arch.0, loc.addr)
+ };
+
+ if res.is_null() {
+ None
+ } else {
+ Some(unsafe { &*(res as *mut Label) })
+ }
+ }
+
+ pub fn mark_label(&self, label: &mut Label) {
+ use binaryninjacore_sys::BNLowLevelILMarkLabel;
+
+ unsafe {
+ BNLowLevelILMarkLabel(self.handle, &mut label.0 as *mut _);
+ }
+ }
+}
+
+use binaryninjacore_sys::BNLowLevelILLabel;
+
+#[repr(C)]
+pub struct Label(BNLowLevelILLabel);
+impl Label {
+ pub fn new() -> Self {
+ use binaryninjacore_sys::BNLowLevelILInitLabel;
+
+ unsafe {
+ let mut res = Label(mem::uninitialized());
+ BNLowLevelILInitLabel(&mut res.0 as *mut _);
+ res
+ }
+ }
+}
+
+
diff --git a/rust/src/llil/mod.rs b/rust/src/llil/mod.rs
new file mode 100644
index 00000000..c3b1b350
--- /dev/null
+++ b/rust/src/llil/mod.rs
@@ -0,0 +1,80 @@
+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;
+use crate::function::Location;
+
+mod function;
+mod instruction;
+mod expression;
+mod lifting;
+mod block;
+pub mod operation;
+
+pub use self::function::*;
+pub use self::instruction::*;
+pub use self::expression::*;
+pub use self::lifting::{Liftable, LiftableWithSize, Label, ExpressionBuilder, FlagWriteOp, RegisterOrConstant};
+pub use self::lifting::get_default_flag_write_llil;
+pub use self::lifting::get_default_flag_cond_llil;
+
+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)]
+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
+ }
+ }
+}
+
+pub enum VisitorAction {
+ Descend,
+ Sibling,
+ Halt,
+}
+
diff --git a/rust/src/llil/operation.rs b/rust/src/llil/operation.rs
new file mode 100644
index 00000000..81d72d57
--- /dev/null
+++ b/rust/src/llil/operation.rs
@@ -0,0 +1,765 @@
+use binaryninjacore_sys::BNLowLevelILInstruction;
+
+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: function,
+ op: 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_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 {
+ function: self.function,
+ expr_idx: self.op.operands[1] as usize,
+ _ty: PhantomData,
+ }
+ }
+}
+
+
+// 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 {
+ function: self.function,
+ expr_idx: self.op.operands[2] as usize,
+ _ty: PhantomData,
+ }
+ }
+}
+
+
+
+
+// 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 {
+ function: self.function,
+ expr_idx: self.op.operands[1] as usize,
+ _ty: PhantomData,
+ }
+ }
+}
+
+
+// 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 {
+ function: self.function,
+ expr_idx: self.op.operands[0] as usize,
+ _ty: PhantomData,
+ }
+ }
+}
+
+
+
+// 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 {
+ function: self.function,
+ expr_idx: self.op.operands[0] as usize,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn source_expr(&self) -> Expression<'func, A, M, NonSSA<V>, ValueExpr> {
+ Expression {
+ function: self.function,
+ expr_idx: self.op.operands[1] as usize,
+ _ty: PhantomData,
+ }
+ }
+}
+
+
+
+// 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_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 {
+ function: self.function,
+ expr_idx: self.op.operands[0] as usize,
+ _ty: PhantomData,
+ }
+ }
+}
+
+
+
+// LLIL_JUMP_TO
+pub struct JumpTo;
+
+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 {
+ function: self.function,
+ expr_idx: self.op.operands[0] as usize,
+ _ty: PhantomData,
+ }
+ }
+ // TODO target list
+}
+
+
+
+// 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 {
+ function: self.function,
+ expr_idx: self.op.operands[0] as usize,
+ _ty: PhantomData,
+ }
+ }
+
+ 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 {
+ function: self.function,
+ expr_idx: self.op.operands[0] as usize,
+ _ty: PhantomData,
+ }
+ }
+}
+
+
+
+// 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 {
+ function: self.function,
+ expr_idx: self.op.operands[0] as usize,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn true_target(&self) -> Instruction<'func, A, M, F> {
+ Instruction {
+ function: self.function,
+ instr_idx: 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,
+ }
+ }
+}
+
+
+
+// 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,
+ }
+ }
+}
+
+
+
+// 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_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 {
+ function: self.function,
+ expr_idx: self.op.operands[0] as usize,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn right(&self) -> Expression<'func, A, M, F, ValueExpr> {
+ Expression {
+ function: self.function,
+ expr_idx: self.op.operands[1] as usize,
+ _ty: PhantomData,
+ }
+ }
+}
+
+
+
+// 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 {
+ function: self.function,
+ expr_idx: self.op.operands[0] as usize,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn right(&self) -> Expression<'func, A, M, F, ValueExpr> {
+ Expression {
+ function: self.function,
+ expr_idx: self.op.operands[1] as usize,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn carry(&self) -> Expression<'func, A, M, F, ValueExpr> {
+ Expression {
+ function: self.function,
+ expr_idx: self.op.operands[2] as usize,
+ _ty: PhantomData,
+ }
+ }
+}
+
+
+
+// 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 {
+ function: self.function,
+ expr_idx: self.op.operands[0] as usize,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn low(&self) -> Expression<'func, A, M, F, ValueExpr> {
+ Expression {
+ function: self.function,
+ expr_idx: self.op.operands[1] as usize,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn right(&self) -> Expression<'func, A, M, F, ValueExpr> {
+ Expression {
+ function: self.function,
+ expr_idx: self.op.operands[2] as usize,
+ _ty: PhantomData,
+ }
+ }
+}
+
+
+
+// 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 {
+ function: self.function,
+ expr_idx: self.op.operands[0] as usize,
+ _ty: PhantomData,
+ }
+ }
+}
+
+
+
+// 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 {
+ function: self.function,
+ expr_idx: self.op.operands[0] as usize,
+ _ty: PhantomData,
+ }
+ }
+
+ pub fn right(&self) -> Expression<'func, A, M, F, ValueExpr> {
+ Expression {
+ function: self.function,
+ expr_idx: self.op.operands[1] as usize,
+ _ty: PhantomData,
+ }
+ }
+}
+
+
+// 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 {
+ function: self.function,
+ expr_idx: self.op.operands[0] as usize,
+ _ty: PhantomData,
+ }
+ }
+}
+
+// TODO TEST_BIT
+
+pub trait OperationArguments: 'static {}
+
+impl OperationArguments for NoArgs {}
+impl OperationArguments for Pop {}
+impl OperationArguments for Syscall {}
+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 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 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/platform.rs b/rust/src/platform.rs
new file mode 100644
index 00000000..3343bece
--- /dev/null
+++ b/rust/src/platform.rs
@@ -0,0 +1,227 @@
+use std::borrow::Borrow;
+
+use binaryninjacore_sys::*;
+
+use crate::architecture::{Architecture, CoreArchitecture};
+use crate::callingconvention::CallingConvention;
+use crate::types::QualifiedNameAndType;
+use crate::rc::*;
+use crate::string::*;
+
+#[derive(PartialEq, Eq, Hash)]
+pub struct Platform {
+ pub(crate) handle: *mut BNPlatform,
+}
+
+unsafe impl Send for Platform {}
+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>>> {
+ let arch = self.arch();
+
+ unsafe {
+ let cc = $get_api(self.handle);
+
+ if cc.is_null() {
+ None
+ } else {
+ Some(Ref::new(CallingConvention::from_raw(cc, arch)))
+ }
+ }
+ }
+
+ pub fn $set_name<A: Architecture>(&self, cc: &CallingConvention<A>) {
+ let arch = self.arch();
+
+ assert!(cc.arch_handle.borrow().as_ref().0 == arch.0, "use of calling convention with non-matching Platform architecture!");
+
+ unsafe {
+ $set_api(self.handle, cc.handle);
+ }
+ }
+ }
+}
+
+impl Platform {
+ pub(crate) unsafe fn from_raw(handle: *mut BNPlatform) -> Self {
+ debug_assert!(!handle.is_null());
+
+ Self { handle }
+ }
+
+ pub fn by_name<S: BnStrCompatible>(name: S) -> Option<Ref<Self>> {
+ let raw_name = name.as_bytes_with_nul();
+ unsafe {
+ let res = BNGetPlatformByName(raw_name.as_ref().as_ptr() as *mut _);
+
+ if res.is_null() {
+ None
+ } else {
+ Some(Ref::new(Self { handle: res }))
+ }
+ }
+ }
+
+ pub fn list_all() -> Array<Platform> {
+ unsafe {
+ let mut count = 0;
+ let handles = BNGetPlatformList(&mut count);
+
+ Array::new(handles, count, ())
+ }
+ }
+
+ pub fn list_by_arch(arch: &CoreArchitecture) -> Array<Platform> {
+ unsafe {
+ let mut count = 0;
+ let handles = BNGetPlatformListByArchitecture(arch.0, &mut count);
+
+ Array::new(handles, count, ())
+ }
+ }
+
+ pub fn list_by_os<S: BnStrCompatible>(name: S) -> Array<Platform> {
+ let raw_name = name.as_bytes_with_nul();
+
+ unsafe {
+ let mut count = 0;
+ let handles = BNGetPlatformListByOS(raw_name.as_ref().as_ptr() as *mut _, &mut count);
+
+ Array::new(handles, count, ())
+ }
+ }
+
+ pub fn list_by_os_and_arch<S: BnStrCompatible>(name: S, arch: &CoreArchitecture) -> Array<Platform> {
+ let raw_name = name.as_bytes_with_nul();
+
+ unsafe {
+ let mut count = 0;
+ let handles = BNGetPlatformListByOSAndArchitecture(raw_name.as_ref().as_ptr() as *mut _,
+ arch.0, &mut count);
+
+ Array::new(handles, count, ())
+ }
+ }
+
+ pub fn list_available_os() -> Array<BnString> {
+ unsafe {
+ let mut count = 0;
+ let list = BNGetPlatformOSList(&mut count);
+
+ Array::new(list, count, ())
+ }
+ }
+
+ pub fn new<A: Architecture, S: BnStrCompatible>(arch: &A, name: S) -> Ref<Self> {
+ let name = name.as_bytes_with_nul();
+ unsafe {
+ let handle = BNCreatePlatform(arch.as_ref().0, name.as_ref().as_ptr() as *mut _);
+
+ assert!(!handle.is_null());
+
+ Ref::new(Self { handle })
+ }
+ }
+
+ pub fn name(&self) -> BnString {
+ unsafe {
+ let raw_name = BNGetPlatformName(self.handle);
+ BnString::from_raw(raw_name)
+ }
+ }
+
+ pub fn arch(&self) -> CoreArchitecture {
+ unsafe {
+ CoreArchitecture::from_raw(BNGetPlatformArchitecture(self.handle))
+ }
+ }
+
+ pub fn register_os<S: BnStrCompatible>(&self, os: S) {
+ let os = os.as_bytes_with_nul();
+
+ unsafe {
+ BNRegisterPlatform(os.as_ref().as_ptr() as *mut _, self.handle);
+ }
+ }
+
+ cc_func!(get_default_calling_convention, BNGetPlatformDefaultCallingConvention,
+ set_default_calling_convention, BNRegisterPlatformDefaultCallingConvention);
+
+ cc_func!(get_cdecl_calling_convention, BNGetPlatformCdeclCallingConvention,
+ set_cdecl_calling_convention, BNRegisterPlatformCdeclCallingConvention);
+
+ cc_func!(get_stdcall_calling_convention, BNGetPlatformStdcallCallingConvention,
+ set_stdcall_calling_convention, BNRegisterPlatformStdcallCallingConvention);
+
+ cc_func!(get_fastcall_calling_convention, BNGetPlatformFastcallCallingConvention,
+ set_fastcall_calling_convention, BNRegisterPlatformFastcallCallingConvention);
+
+ cc_func!(get_syscall_convention, BNGetPlatformSystemCallConvention,
+ set_syscall_convention, BNSetPlatformSystemCallConvention);
+
+ pub fn types(&self) -> Array<QualifiedNameAndType> {
+ unsafe {
+ let mut count = 0;
+ let handles = BNGetPlatformTypes(self.handle, &mut count);
+
+ Array::new(handles, count, ())
+ }
+ }
+
+ pub fn variables(&self) -> Array<QualifiedNameAndType> {
+ unsafe {
+ let mut count = 0;
+ let handles = BNGetPlatformVariables(self.handle, &mut count);
+
+ Array::new(handles, count, ())
+ }
+ }
+
+ pub fn functions(&self) -> Array<QualifiedNameAndType> {
+ unsafe {
+ let mut count = 0;
+ let handles = BNGetPlatformFunctions(self.handle, &mut count);
+
+ Array::new(handles, count, ())
+ }
+ }
+}
+
+impl ToOwned for Platform {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for Platform {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: BNNewPlatformReference(handle.handle),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreePlatform(handle.handle);
+ }
+}
+
+unsafe impl CoreOwnedArrayProvider for Platform {
+ type Raw = *mut BNPlatform;
+ type Context = ();
+
+ unsafe fn free(raw: *mut *mut BNPlatform, count: usize, _context: &()) {
+ BNFreePlatformList(raw, count);
+ }
+}
+
+unsafe impl<'a> CoreOwnedArrayWrapper<'a> for Platform {
+ type Wrapped = Guard<'a, Platform>;
+
+ unsafe fn wrap_raw(raw: &'a *mut BNPlatform, context: &'a ()) -> Guard<'a, Platform> {
+ Guard::new(Platform::from_raw(*raw), context)
+ }
+}
diff --git a/rust/src/rc.rs b/rust/src/rc.rs
new file mode 100644
index 00000000..3302fb3f
--- /dev/null
+++ b/rust/src/rc.rs
@@ -0,0 +1,361 @@
+use std::marker::PhantomData;
+use std::borrow::Borrow;
+use std::ops::{Deref, DerefMut};
+use std::slice;
+use std::mem;
+use std::ptr;
+
+// RefCountable provides an abstraction over the various
+// core-allocated refcounted resources.
+//
+// It is important that consumers don't acquire ownership
+// of a `RefCountable` object directly -- they should only
+// ever get their hands on a `Ref<T>` or `&T`, otherwise it
+// would be possible for the allocation in the core to
+// be trivially leaked, as `T` does not have the `Drop` impl
+//
+// `T` does not have the `Drop` impl in order to allow more
+// efficient handling of core owned objects we receive pointers
+// to in callbacks
+pub unsafe trait RefCountable: ToOwned<Owned=Ref<Self>> + Sized {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self>;
+ unsafe fn dec_ref(handle: &Self);
+}
+
+// Represents an 'owned' reference tracked by the core
+// that we are responsible for cleaning up once we're
+// done with the encapsulated value.
+pub struct Ref<T: RefCountable> {
+ contents: T,
+}
+
+impl<T: RefCountable> Ref<T> {
+ pub(crate) unsafe fn new(contents: T) -> Self {
+ Self { contents }
+ }
+
+ pub(crate) unsafe fn into_raw(obj: Self) -> T {
+ let res = ptr::read(&obj.contents);
+
+ mem::forget(obj);
+
+ res
+ }
+}
+
+impl<T: RefCountable> AsRef<T> for Ref<T> {
+ fn as_ref(&self) -> &T {
+ &self.contents
+ }
+}
+
+impl<T: RefCountable> Deref for Ref<T> {
+ type Target = T;
+
+ fn deref(&self) -> &T {
+ &self.contents
+ }
+}
+
+impl<T: RefCountable> DerefMut for Ref<T> {
+ fn deref_mut(&mut self) -> &mut T {
+ &mut self.contents
+ }
+}
+
+impl<T: RefCountable> Borrow<T> for Ref<T> {
+ fn borrow(&self) -> &T {
+ &self.contents
+ }
+}
+
+impl<T: RefCountable> Drop for Ref<T> {
+ fn drop(&mut self) {
+ unsafe { RefCountable::dec_ref(&self.contents); }
+ }
+}
+
+impl <T: RefCountable> Clone for Ref<T> {
+ fn clone(&self) -> Self {
+ unsafe { RefCountable::inc_ref(&self.contents) }
+ }
+}
+
+// Guard provides access to a core-allocated resource whose
+// reference is held indirectly (e.g. a core-allocated array
+// of raw `*mut BNRawT`).
+//
+// This wrapper is necessary because `binja-rs` wrappers around
+// core objects can be bigger than the raw pointer to the core
+// object. This lets us create the full wrapper object and ensure
+// that it does not outlive the core-allocated array (or similar)
+// that our object came from.
+pub struct Guard<'a, T> {
+ contents: T,
+ _guard: PhantomData<&'a ()>,
+}
+
+impl<'a, T> Guard<'a, T> {
+ pub(crate) unsafe fn new<O: 'a>(contents: T, _owner: &O) -> Self {
+ Self {
+ contents,
+ _guard: PhantomData
+ }
+ }
+}
+
+impl<'a, T> AsRef<T> for Guard<'a, T> {
+ fn as_ref(&self) -> &T {
+ &self.contents
+ }
+}
+
+impl<'a, T> Deref for Guard<'a, T> {
+ type Target = T;
+
+ fn deref(&self) -> &T {
+ &self.contents
+ }
+}
+
+impl<'a, T> DerefMut for Guard<'a, T> {
+ fn deref_mut(&mut self) -> &mut T {
+ &mut self.contents
+ }
+}
+
+impl<'a, T> Borrow<T> for Guard<'a, T> {
+ fn borrow(&self) -> &T {
+ &self.contents
+ }
+}
+
+pub unsafe trait CoreOwnedArrayProvider {
+ type Raw;
+ type Context;
+
+ unsafe fn free(raw: *mut Self::Raw, count: usize, context: &Self::Context);
+}
+
+pub unsafe trait CoreOwnedArrayWrapper<'a>: CoreOwnedArrayProvider
+where
+ Self::Raw: 'a,
+ Self::Context: 'a,
+{
+ type Wrapped: 'a;
+
+ unsafe fn wrap_raw(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped;
+}
+
+pub struct Array<P>
+where
+ P: CoreOwnedArrayProvider
+{
+ contents: *mut P::Raw,
+ count: usize,
+ context: P::Context,
+}
+
+unsafe impl<P> Sync for Array<P> where P: CoreOwnedArrayProvider, P::Context: Sync {}
+unsafe impl<P> Send for Array<P> where P: CoreOwnedArrayProvider, P::Context: Send {}
+
+impl<P: CoreOwnedArrayProvider> Array<P> {
+ pub(crate) unsafe fn new(raw: *mut P::Raw, count: usize, context: P::Context) -> Self {
+ Self {
+ contents: raw,
+ count: count,
+ context: context,
+ }
+ }
+
+ #[inline]
+ pub fn len(&self) -> usize {
+ self.count
+ }
+}
+
+impl<'a, P: 'a + CoreOwnedArrayWrapper<'a>> Array<P> {
+ #[inline]
+ pub fn get(&'a self, index: usize) -> P::Wrapped {
+ unsafe {
+ let backing = slice::from_raw_parts(self.contents, self.count);
+ P::wrap_raw(&backing[index], &self.context)
+ }
+ }
+
+ pub fn iter(&'a self) -> ArrayIter<'a, P> {
+ ArrayIter {
+ it: unsafe { slice::from_raw_parts(self.contents, self.count).iter() },
+ context: &self.context
+ }
+ }
+}
+
+
+impl<'a, P: 'a + CoreOwnedArrayWrapper<'a>> IntoIterator for &'a Array<P> {
+ type Item = P::Wrapped;
+ type IntoIter = ArrayIter<'a, P>;
+
+ fn into_iter(self) -> Self::IntoIter {
+ self.iter()
+ }
+}
+
+
+impl<P: CoreOwnedArrayProvider> Drop for Array<P> {
+ fn drop(&mut self) {
+ unsafe { P::free(self.contents, self.count, &self.context); }
+ }
+}
+
+pub struct ArrayIter<'a, P>
+where
+ P: 'a + CoreOwnedArrayWrapper<'a>
+{
+ it: slice::Iter<'a, P::Raw>,
+ context: &'a P::Context,
+}
+
+unsafe impl<'a, P> Send for ArrayIter<'a, P> where P: CoreOwnedArrayWrapper<'a>, P::Context: Sync {}
+
+impl<'a, P> Iterator for ArrayIter<'a, P>
+where
+ P: 'a + CoreOwnedArrayWrapper<'a>
+{
+ type Item = P::Wrapped;
+
+ #[inline]
+ fn next(&mut self) -> Option<P::Wrapped> {
+ self.it.next().map(|r| unsafe { P::wrap_raw(r, &self.context) })
+ }
+
+ #[inline]
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ self.it.size_hint()
+ }
+}
+
+impl<'a, P> ExactSizeIterator for ArrayIter<'a, P>
+where
+ P: 'a + CoreOwnedArrayWrapper<'a>
+{
+ #[inline]
+ fn len(&self) -> usize {
+ self.it.len()
+ }
+}
+
+impl<'a, P> DoubleEndedIterator for ArrayIter<'a, P>
+where
+ P: 'a + CoreOwnedArrayWrapper<'a>
+{
+ #[inline]
+ fn next_back(&mut self) -> Option<P::Wrapped> {
+ self.it.next_back().map(|r| unsafe { P::wrap_raw(r, &self.context) })
+ }
+}
+
+#[cfg(feature = "rayon")]
+use rayon::prelude::*;
+
+#[cfg(feature = "rayon")]
+use rayon::iter::plumbing::*;
+
+#[cfg(feature = "rayon")]
+impl<'a, P> Array<P>
+where
+ P: 'a + CoreOwnedArrayWrapper<'a>,
+ P::Context: Sync,
+ P::Wrapped: Send,
+{
+ pub fn par_iter(&'a self) -> ParArrayIter<'a, P> {
+ ParArrayIter {
+ it: self.iter(),
+ }
+ }
+}
+#[cfg(feature = "rayon")]
+pub struct ParArrayIter<'a, P>
+where
+ P: 'a + CoreOwnedArrayWrapper<'a>,
+ ArrayIter<'a, P>: Send,
+{
+ it: ArrayIter<'a, P>,
+}
+
+#[cfg(feature = "rayon")]
+impl<'a, P> ParallelIterator for ParArrayIter<'a, P>
+where
+ P: 'a + CoreOwnedArrayWrapper<'a>,
+ P::Wrapped: Send,
+ ArrayIter<'a, P>: Send,
+{
+ type Item = P::Wrapped;
+
+ fn drive_unindexed<C>(self, consumer: C) -> C::Result
+ where
+ C: UnindexedConsumer<Self::Item>
+ {
+ bridge(self, consumer)
+ }
+
+ fn opt_len(&self) -> Option<usize> {
+ Some(self.it.len())
+ }
+}
+
+#[cfg(feature = "rayon")]
+impl<'a, P> IndexedParallelIterator for ParArrayIter<'a, P>
+where
+ P: 'a + CoreOwnedArrayWrapper<'a>,
+ P::Wrapped: Send,
+ ArrayIter<'a, P>: Send,
+{
+ fn drive<C>(self, consumer: C) -> C::Result
+ where
+ C: Consumer<Self::Item>
+ {
+ bridge(self, consumer)
+ }
+
+ fn len(&self) -> usize {
+ self.it.len()
+ }
+
+ fn with_producer<CB>(self, callback: CB) -> CB::Output
+ where
+ CB: ProducerCallback<Self::Item>
+ {
+ callback.callback(ArrayIterProducer { it: self.it })
+ }
+}
+
+#[cfg(feature = "rayon")]
+struct ArrayIterProducer<'a, P>
+where
+ P: 'a + CoreOwnedArrayWrapper<'a>,
+ ArrayIter<'a, P>: Send,
+{
+ it: ArrayIter<'a, P>,
+}
+
+#[cfg(feature = "rayon")]
+impl<'a, P> Producer for ArrayIterProducer<'a, P>
+where
+ P: 'a + CoreOwnedArrayWrapper<'a>,
+ ArrayIter<'a, P>: Send,
+{
+ type Item = P::Wrapped;
+ type IntoIter = ArrayIter<'a, P>;
+
+ fn into_iter(self) -> ArrayIter<'a, P> {
+ self.it
+ }
+
+ fn split_at(self, index: usize) -> (Self, Self) {
+ let (l, r) = self.it.it.as_slice().split_at(index);
+
+ (Self { it: ArrayIter { it: l.iter(), context: self.it.context } },
+ Self { it: ArrayIter { it: r.iter(), context: self.it.context } })
+ }
+}
diff --git a/rust/src/section.rs b/rust/src/section.rs
new file mode 100644
index 00000000..d936e227
--- /dev/null
+++ b/rust/src/section.rs
@@ -0,0 +1,251 @@
+use std::fmt;
+use std::ops::Range;
+
+use binaryninjacore_sys::*;
+
+use crate::binaryview::BinaryView;
+use crate::string::*;
+use crate::rc::*;
+
+pub enum Semantics {
+ DefaultSection,
+ ReadOnlyCode,
+ ReadOnlyData,
+ ReadWriteData,
+ External,
+}
+
+impl From<BNSectionSemantics> for Semantics {
+ fn from(bn: BNSectionSemantics) -> Self {
+ use self::BNSectionSemantics::*;
+
+ match bn {
+ DefaultSectionSemantics => Semantics::DefaultSection,
+ ReadOnlyCodeSectionSemantics => Semantics::ReadOnlyCode,
+ ReadOnlyDataSectionSemantics => Semantics::ReadOnlyData,
+ ReadWriteDataSectionSemantics => Semantics::ReadWriteData,
+ ExternalSectionSemantics => Semantics::External,
+ }
+ }
+}
+
+impl Into<BNSectionSemantics> for Semantics {
+ fn into(self) -> BNSectionSemantics {
+ use self::BNSectionSemantics::*;
+
+ match self {
+ Semantics::DefaultSection => DefaultSectionSemantics,
+ Semantics::ReadOnlyCode => ReadOnlyCodeSectionSemantics,
+ Semantics::ReadOnlyData => ReadOnlyDataSectionSemantics,
+ Semantics::ReadWriteData => ReadWriteDataSectionSemantics,
+ Semantics::External => ExternalSectionSemantics,
+ }
+ }
+}
+
+#[derive(PartialEq, Eq, Hash)]
+pub struct Section {
+ handle: *mut BNSection,
+}
+
+impl Section {
+ pub(crate) unsafe fn from_raw(raw: *mut BNSection) -> Self {
+ Self { handle: raw }
+ }
+
+ pub fn new<S: BnStrCompatible>(name: S, range: Range<u64>) -> SectionBuilder<S> {
+ SectionBuilder {
+ is_auto: false,
+ name: name,
+ range: range,
+ semantics: Semantics::DefaultSection,
+ _ty: None,
+ align: 1,
+ entry_size: 1,
+ linked_section: None,
+ info_section: None,
+ info_data: 0
+ }
+ }
+
+ pub fn name(&self) -> BnString {
+ unsafe { BnString::from_raw(BNSectionGetName(self.handle)) }
+ }
+
+ pub fn section_type(&self) -> BnString {
+ unsafe { BnString::from_raw(BNSectionGetType(self.handle)) }
+ }
+
+ pub fn start(&self) -> u64 {
+ unsafe { BNSectionGetStart(self.handle) }
+ }
+
+ pub fn end(&self) -> u64 {
+ unsafe { BNSectionGetEnd(self.handle) }
+ }
+
+ pub fn len(&self) -> usize {
+ unsafe { BNSectionGetLength(self.handle) as usize }
+ }
+
+ pub fn address_range(&self) -> Range<u64> {
+ self.start() .. self.end()
+ }
+
+ pub fn semantics(&self) -> Semantics {
+ unsafe { BNSectionGetSemantics(self.handle).into() }
+ }
+
+ pub fn linked_section(&self) -> BnString {
+ unsafe { BnString::from_raw(BNSectionGetLinkedSection(self.handle)) }
+ }
+
+ pub fn info_section(&self) -> BnString {
+ unsafe { BnString::from_raw(BNSectionGetInfoSection(self.handle)) }
+ }
+
+ pub fn info_data(&self) -> u64 {
+ unsafe { BNSectionGetInfoData(self.handle) }
+ }
+
+ pub fn align(&self) -> u64 {
+ unsafe { BNSectionGetAlign(self.handle) }
+ }
+
+ pub fn entry_size(&self) -> usize {
+ unsafe { BNSectionGetEntrySize(self.handle) as usize }
+ }
+
+ pub fn auto_defined(&self) -> bool {
+ unsafe { BNSectionIsAutoDefined(self.handle) }
+ }
+}
+
+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())
+ }
+}
+
+impl ToOwned for Section {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for Section {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: BNNewSectionReference(handle.handle),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeSection(handle.handle);
+ }
+}
+
+unsafe impl CoreOwnedArrayProvider for Section {
+ type Raw = *mut BNSection;
+ type Context = ();
+
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeSectionList(raw, count);
+ }
+}
+
+unsafe impl<'a> CoreOwnedArrayWrapper<'a> for Section {
+ type Wrapped = Guard<'a, Section>;
+
+ unsafe fn wrap_raw(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped {
+ Guard::new(Section::from_raw(*raw), context)
+ }
+}
+
+#[must_use]
+pub struct SectionBuilder<S: BnStrCompatible> {
+ is_auto: bool,
+ name: S,
+ range: Range<u64>,
+ semantics: Semantics,
+ _ty: Option<S>,
+ align: u64,
+ entry_size: u64,
+ linked_section: Option<S>,
+ info_section: Option<S>,
+ info_data: u64,
+}
+
+impl<S: BnStrCompatible> SectionBuilder<S> {
+ pub fn semantics(mut self, semantics: Semantics) -> Self {
+ self.semantics = semantics;
+ self
+ }
+
+ pub fn section_type(mut self, ty: S) -> Self {
+ self._ty = Some(ty);
+ self
+ }
+
+ pub fn align(mut self, align: u64) -> Self {
+ self.align = align;
+ self
+ }
+
+ pub fn entry_size(mut self, entry_size: u64) -> Self {
+ self.entry_size = entry_size;
+ self
+ }
+
+ pub fn linked_section(mut self, linked_section: S) -> Self {
+ self.linked_section = Some(linked_section);
+ self
+ }
+
+ pub fn info_section(mut self, info_section: S) -> Self {
+ self.info_section = Some(info_section);
+ self
+ }
+
+ pub fn info_data(mut self, info_data: u64) -> Self {
+ self.info_data = info_data;
+ self
+ }
+
+ pub fn is_auto(mut self, is_auto: bool) -> Self {
+ self.is_auto = is_auto;
+ self
+ }
+
+ pub(crate) fn create(self, view: &BinaryView) {
+ let name = self.name.as_bytes_with_nul();
+ let ty = self._ty.map(|s| s.as_bytes_with_nul());
+ let linked_section = self.linked_section.map(|s| s.as_bytes_with_nul());
+ let info_section = self.info_section.map(|s| s.as_bytes_with_nul());
+
+ let start = self.range.start;
+ let len = self.range.end.wrapping_sub(start);
+
+ unsafe {
+ use std::ffi::CStr;
+
+ let nul_str = CStr::from_bytes_with_nul_unchecked(b"\x00").as_ptr();
+ let name_ptr = name.as_ref().as_ptr() as *mut _;
+ let ty_ptr = ty.as_ref().map_or(nul_str, |s| s.as_ref().as_ptr() as *mut _);
+ let linked_section_ptr = linked_section.as_ref().map_or(nul_str, |s| s.as_ref().as_ptr() as *mut _);
+ let info_section_ptr = info_section.as_ref().map_or(nul_str, |s| s.as_ref().as_ptr() as *mut _);
+
+ if self.is_auto {
+ BNAddAutoSection(view.handle, name_ptr, start, len, self.semantics.into(),
+ ty_ptr, self.align, self.entry_size, linked_section_ptr,
+ info_section_ptr, self.info_data);
+ } else {
+ BNAddUserSection(view.handle, name_ptr, start, len, self.semantics.into(),
+ ty_ptr, self.align, self.entry_size, linked_section_ptr,
+ info_section_ptr, self.info_data);
+ }
+ }
+ }
+}
diff --git a/rust/src/segment.rs b/rust/src/segment.rs
new file mode 100644
index 00000000..0985d0d3
--- /dev/null
+++ b/rust/src/segment.rs
@@ -0,0 +1,190 @@
+use binaryninjacore_sys::*;
+
+
+use std::ops::Range;
+
+use crate::binaryview::BinaryView;
+use crate::rc::*;
+
+fn set_bit(val: u32, bit_mask: u32, new_val: bool) -> u32 {
+ (val & !bit_mask) | if new_val { bit_mask } else { 0 }
+}
+
+#[must_use]
+pub struct SegmentBuilder {
+ ea: Range<u64>,
+ parent_backing: Option<Range<u64>>,
+ flags: u32,
+ is_auto: bool,
+}
+
+impl SegmentBuilder {
+ pub fn parent_backing(mut self, parent_backing: Range<u64>) -> Self {
+ self.parent_backing = Some(parent_backing);
+ self
+ }
+
+ pub fn executable(mut self, executable: bool) -> Self {
+ self.flags = set_bit(self.flags, 0x01, executable);
+ self
+ }
+
+ pub fn writable(mut self, writable: bool) -> Self {
+ self.flags = set_bit(self.flags, 0x02, writable);
+ self
+ }
+
+ pub fn readable(mut self, readable: bool) -> Self {
+ self.flags = set_bit(self.flags, 0x04, readable);
+ self
+ }
+
+ pub fn contains_data(mut self, contains_data: bool) -> Self {
+ self.flags = set_bit(self.flags, 0x08, contains_data);
+ self
+ }
+
+ pub fn contains_code(mut self, contains_code: bool) -> Self {
+ self.flags = set_bit(self.flags, 0x10, contains_code);
+ self
+ }
+
+ pub fn deny_write(mut self, deny_write: bool) -> Self {
+ self.flags = set_bit(self.flags, 0x20, deny_write);
+ self
+ }
+
+ pub fn deny_execute(mut self, deny_execute: bool) -> Self {
+ self.flags = set_bit(self.flags, 0x40, deny_execute);
+ self
+ }
+
+ pub fn is_auto(mut self, is_auto: bool) -> Self {
+ self.is_auto = is_auto;
+ self
+ }
+
+ pub(crate) fn create(self, view: &BinaryView) {
+ let ea_start = self.ea.start;
+ let ea_len = self.ea.end.wrapping_sub(ea_start);
+ let (b_start, b_len) = self.parent_backing.map_or((0, 0), |s| (s.start, s.end.wrapping_sub(s.start)));
+
+ unsafe {
+ if self.is_auto {
+ BNAddAutoSegment(view.handle, ea_start, ea_len, b_start, b_len, self.flags);
+ } else {
+ BNAddUserSegment(view.handle, ea_start, ea_len, b_start, b_len, self.flags);
+ }
+ }
+ }
+}
+
+#[derive(PartialEq, Eq, Hash)]
+pub struct Segment {
+ handle: *mut BNSegment,
+}
+
+impl Segment {
+ pub(crate) unsafe fn from_raw(raw: *mut BNSegment) -> Self {
+ Self { handle: raw }
+ }
+
+ pub fn new(ea_range: Range<u64>) -> SegmentBuilder {
+ SegmentBuilder {
+ ea: ea_range,
+ parent_backing: None,
+ flags: 0,
+ is_auto: false,
+ }
+ }
+
+ pub fn address_range(&self) -> Range<u64> {
+ let start = unsafe { BNSegmentGetStart(self.handle) };
+ let end = unsafe { BNSegmentGetEnd(self.handle) };
+ start .. end
+ }
+
+ pub fn parent_backing(&self) -> Option<Range<u64>> {
+ let start = unsafe { BNSegmentGetDataOffset(self.handle) };
+ let end = unsafe { BNSegmentGetDataEnd(self.handle) };
+
+ if start != end {
+ Some(start .. end)
+ } else {
+ None
+ }
+ }
+
+ fn flags(&self) -> u32 {
+ unsafe { BNSegmentGetFlags(self.handle) }
+ }
+
+ pub fn executable(&self) -> bool {
+ self.flags() & 0x01 != 0
+ }
+
+ pub fn writable(&self) -> bool {
+ self.flags() & 0x02 != 0
+ }
+
+ pub fn readable(&self) -> bool {
+ self.flags() & 0x04 != 0
+ }
+
+ pub fn contains_data(&self) -> bool {
+ self.flags() & 0x08 != 0
+ }
+
+ pub fn contains_code(&self) -> bool {
+ self.flags() & 0x10 != 0
+ }
+
+ pub fn deny_write(&self) -> bool {
+ self.flags() & 0x20 != 0
+ }
+
+ pub fn deny_execute(&self) -> bool {
+ self.flags() & 0x40 != 0
+ }
+
+ pub fn auto_defined(&self) -> bool {
+ unsafe { BNSegmentIsAutoDefined(self.handle) }
+ }
+}
+
+impl ToOwned for Segment {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for Segment {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: BNNewSegmentReference(handle.handle),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeSegment(handle.handle);
+ }
+}
+
+unsafe impl CoreOwnedArrayProvider for Segment {
+ type Raw = *mut BNSegment;
+ type Context = ();
+
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeSegmentList(raw, count);
+ }
+}
+
+unsafe impl<'a> CoreOwnedArrayWrapper<'a> for Segment {
+ type Wrapped = Guard<'a, Segment>;
+
+ unsafe fn wrap_raw(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped {
+ Guard::new(Segment::from_raw(*raw), context)
+ }
+}
diff --git a/rust/src/settings.rs b/rust/src/settings.rs
new file mode 100644
index 00000000..4f4ae58e
--- /dev/null
+++ b/rust/src/settings.rs
@@ -0,0 +1,49 @@
+use binaryninjacore_sys::*;
+
+pub use binaryninjacore_sys::BNSettingsScope as SettingsScope;
+
+use crate::rc::*;
+
+#[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) -> Self {
+ debug_assert!(!handle.is_null());
+
+ Self { handle }
+ }
+}
+
+impl AsRef<Settings> for Settings {
+ fn as_ref(&self) -> &Self {
+ self
+ }
+}
+
+impl ToOwned for Settings {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for Settings {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: BNNewSettingsReference(handle.handle),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeSettings(handle.handle);
+ }
+}
+
+
diff --git a/rust/src/string.rs b/rust/src/string.rs
new file mode 100644
index 00000000..1b2e0913
--- /dev/null
+++ b/rust/src/string.rs
@@ -0,0 +1,201 @@
+use std::fmt;
+use std::borrow::Borrow;
+use std::ops::Deref;
+use std::ffi::{CStr, CString};
+use std::os::raw;
+use std::mem;
+
+use crate::rc::*;
+
+#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
+#[repr(C)]
+pub struct BnStr {
+ raw: [u8]
+}
+
+impl BnStr {
+ pub(crate) unsafe fn from_raw<'a>(ptr: *const raw::c_char) -> &'a Self {
+ mem::transmute(CStr::from_ptr(ptr).to_bytes_with_nul())
+ }
+
+ pub fn as_str(&self) -> &str {
+ self.as_cstr().to_str().unwrap()
+ }
+
+ pub fn as_cstr(&self) -> &CStr {
+ unsafe { CStr::from_bytes_with_nul_unchecked(&self.raw) }
+ }
+}
+
+impl Deref for BnStr {
+ type Target = str;
+
+ fn deref(&self) -> &str {
+ self.as_str()
+ }
+}
+
+impl AsRef<[u8]> for BnStr {
+ fn as_ref(&self) -> &[u8] {
+ &self.raw
+ }
+}
+
+impl AsRef<str> for BnStr {
+ fn as_ref(&self) -> &str {
+ self.as_str()
+ }
+}
+
+impl Borrow<str> for BnStr {
+ fn borrow(&self) -> &str {
+ self.as_str()
+ }
+}
+
+impl fmt::Display for BnStr {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}", self.as_cstr().to_string_lossy())
+ }
+}
+
+#[repr(C)]
+pub struct BnString {
+ raw: *mut raw::c_char,
+}
+
+/// A nul-terminated C string allocated by the core.
+///
+/// Received from a variety of core function calls, and
+/// must be used when giving strings to the core from many
+/// core-invoked callbacks.
+impl BnString {
+ pub fn new<S: BnStrCompatible>(s: S) -> Self {
+ use binaryninjacore_sys::BNAllocString;
+
+ let raw = s.as_bytes_with_nul();
+
+ unsafe {
+ let ptr = raw.as_ref().as_ptr() as *mut _;
+
+ Self { raw: BNAllocString(ptr) }
+ }
+ }
+
+ pub(crate) unsafe fn from_raw(raw: *mut raw::c_char) -> Self {
+ Self { raw }
+ }
+
+ pub(crate) fn into_raw(self) -> *mut raw::c_char {
+ let res = self.raw;
+
+ // we're surrendering ownership over the *mut c_char to
+ // the core, so ensure we don't free it
+ mem::forget(self);
+
+ res
+ }
+}
+
+impl Drop for BnString {
+ fn drop(&mut self) {
+ use binaryninjacore_sys::BNFreeString;
+
+ unsafe {
+ BNFreeString(self.raw);
+ }
+ }
+}
+
+impl Deref for BnString {
+ type Target = BnStr;
+
+ fn deref(&self) -> &BnStr {
+ unsafe { BnStr::from_raw(self.raw) }
+ }
+}
+
+impl AsRef<[u8]> for BnString {
+ fn as_ref(&self) -> &[u8] {
+ self.as_cstr().to_bytes_with_nul()
+ }
+}
+
+impl fmt::Display for BnString {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}", self.as_cstr().to_string_lossy())
+ }
+}
+
+unsafe impl CoreOwnedArrayProvider for BnString {
+ type Raw = *mut raw::c_char;
+ type Context = ();
+
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ use binaryninjacore_sys::BNFreeStringList;
+ BNFreeStringList(raw, count);
+ }
+}
+
+unsafe impl<'a> CoreOwnedArrayWrapper<'a> for BnString {
+ type Wrapped = &'a BnStr;
+
+ unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
+ BnStr::from_raw(*raw)
+ }
+}
+
+pub unsafe trait BnStrCompatible {
+ type Result: AsRef<[u8]>;
+ fn as_bytes_with_nul(self) -> Self::Result;
+}
+
+unsafe impl<'a> BnStrCompatible for &'a BnStr {
+ type Result = &'a [u8];
+
+ fn as_bytes_with_nul(self) -> Self::Result {
+ self.as_cstr().to_bytes_with_nul()
+ }
+}
+
+unsafe impl BnStrCompatible for BnString {
+ type Result = Self;
+
+ fn as_bytes_with_nul(self) -> Self::Result {
+ self
+ }
+}
+
+unsafe impl<'a> BnStrCompatible for &'a CStr {
+ type Result = &'a [u8];
+
+ fn as_bytes_with_nul(self) -> Self::Result {
+ self.to_bytes_with_nul()
+ }
+}
+
+unsafe impl BnStrCompatible for CString {
+ type Result = Vec<u8>;
+
+ fn as_bytes_with_nul(self) -> Self::Result {
+ self.into_bytes_with_nul()
+ }
+}
+
+unsafe impl<'a> BnStrCompatible for &'a str {
+ type Result = Vec<u8>;
+
+ fn as_bytes_with_nul(self) -> Self::Result {
+ let ret = CString::new(self).expect("can't pass strings with internal nul bytes to core!");
+ ret.into_bytes_with_nul()
+ }
+}
+
+unsafe impl BnStrCompatible for String {
+ type Result = Vec<u8>;
+
+ fn as_bytes_with_nul(self) -> Self::Result {
+ let ret = CString::new(self).expect("can't pass strings with internal nul bytes to core!");
+ ret.into_bytes_with_nul()
+ }
+}
diff --git a/rust/src/symbol.rs b/rust/src/symbol.rs
new file mode 100644
index 00000000..44021426
--- /dev/null
+++ b/rust/src/symbol.rs
@@ -0,0 +1,241 @@
+use std::fmt;
+use std::ptr;
+
+use binaryninjacore_sys::*;
+use crate::string::*;
+use crate::rc::*;
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub enum SymType {
+ Function,
+ LibraryFunction,
+ ImportAddress,
+ ImportedFunction,
+ Data,
+ ImportedData,
+ External,
+}
+
+impl From<BNSymbolType> for SymType {
+ fn from(bn: BNSymbolType) -> SymType {
+ use self::BNSymbolType::*;
+
+ match bn {
+ FunctionSymbol => SymType::Function,
+ LibraryFunctionSymbol => SymType::LibraryFunction,
+ ImportAddressSymbol => SymType::ImportAddress,
+ ImportedFunctionSymbol => SymType::ImportedFunction,
+ DataSymbol => SymType::Data,
+ ImportedDataSymbol => SymType::ImportedData,
+ ExternalSymbol => SymType::External,
+ }
+ }
+}
+
+impl Into<BNSymbolType> for SymType {
+ fn into(self) -> BNSymbolType {
+ use self::BNSymbolType::*;
+
+ match self {
+ SymType::Function => FunctionSymbol,
+ SymType::LibraryFunction => LibraryFunctionSymbol,
+ SymType::ImportAddress => ImportAddressSymbol,
+ SymType::ImportedFunction => ImportedFunctionSymbol,
+ SymType::Data => DataSymbol,
+ SymType::ImportedData => ImportedDataSymbol,
+ SymType::External => ExternalSymbol,
+ }
+ }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
+pub enum Binding {
+ None,
+ Local,
+ Global,
+ Weak,
+}
+
+impl From<BNSymbolBinding> for Binding {
+ fn from(bn: BNSymbolBinding) -> Binding {
+ use self::BNSymbolBinding::*;
+
+ match bn {
+ NoBinding => Binding::None,
+ LocalBinding => Binding::Local,
+ GlobalBinding => Binding::Global,
+ WeakBinding => Binding::Weak,
+ }
+ }
+}
+
+impl Into<BNSymbolBinding> for Binding {
+ fn into(self) -> BNSymbolBinding {
+ use self::BNSymbolBinding::*;
+
+ match self {
+ Binding::None => NoBinding,
+ Binding::Local => LocalBinding,
+ Binding::Global => GlobalBinding,
+ Binding::Weak => WeakBinding,
+ }
+ }
+}
+
+#[must_use]
+pub struct SymbolBuilder<S: BnStrCompatible> {
+ ty: SymType,
+ binding: Binding,
+ addr: u64,
+ raw_name: S,
+ short_name: Option<S>,
+ full_name: Option<S>,
+ ordinal: u64,
+}
+
+impl<S: BnStrCompatible> SymbolBuilder<S> {
+ pub fn binding(mut self, binding: Binding) -> Self {
+ self.binding = binding;
+ self
+ }
+
+ pub fn short_name(mut self, short_name: S) -> Self {
+ self.short_name = Some(short_name);
+ self
+ }
+
+ pub fn full_name(mut self, full_name: S) -> Self {
+ self.full_name = Some(full_name);
+ self
+ }
+
+ pub fn ordinal(mut self, ordinal: u64) -> Self {
+ self.ordinal = ordinal;
+ self
+ }
+
+ pub fn create(self) -> Ref<Symbol> {
+ let raw_name = self.raw_name.as_bytes_with_nul();
+ let short_name = self.short_name.map(|s| s.as_bytes_with_nul());
+ let full_name = self.full_name.map(|s| s.as_bytes_with_nul());
+
+ unsafe {
+ let raw_name = raw_name.as_ref().as_ptr() as *mut _;
+ let short_name = short_name.as_ref().map_or(raw_name, |s| s.as_ref().as_ptr() as *mut _);
+ let full_name = full_name.as_ref().map_or(raw_name, |s| s.as_ref().as_ptr() as *mut _);
+
+ let res = BNCreateSymbol(self.ty.into(),
+ short_name, full_name, raw_name,
+ self.addr, self.binding.into(), ptr::null_mut(),
+ self.ordinal);
+
+ Ref::new(Symbol::from_raw(res))
+ }
+ }
+}
+
+#[derive(PartialEq, Eq, Hash)]
+pub struct Symbol {
+ pub(crate) handle: *mut BNSymbol,
+}
+
+unsafe impl Send for Symbol {}
+unsafe impl Sync for Symbol {}
+
+impl Symbol {
+ pub(crate) unsafe fn from_raw(raw: *mut BNSymbol) -> Self {
+ Self { handle: raw }
+ }
+
+ pub fn new<S: BnStrCompatible>(ty: SymType, raw_name: S, addr: u64) -> SymbolBuilder<S> {
+ SymbolBuilder {
+ ty: ty,
+ binding: Binding::None,
+ addr: addr,
+ raw_name: raw_name,
+ short_name: None,
+ full_name: None,
+ ordinal: 0,
+ }
+ }
+
+ pub fn sym_type(&self) -> SymType {
+ unsafe { BNGetSymbolType(self.handle).into() }
+ }
+
+ pub fn binding(&self) -> Binding {
+ unsafe { BNGetSymbolBinding(self.handle).into() }
+ }
+
+ pub fn name(&self) -> BnString {
+ unsafe {
+ let name = BNGetSymbolFullName(self.handle);
+ BnString::from_raw(name)
+ }
+ }
+
+ pub fn short_name(&self) -> BnString {
+ unsafe {
+ let name = BNGetSymbolShortName(self.handle);
+ BnString::from_raw(name)
+ }
+ }
+
+ pub fn raw_name(&self) -> BnString {
+ unsafe {
+ let name = BNGetSymbolRawName(self.handle);
+ BnString::from_raw(name)
+ }
+ }
+
+ pub fn address(&self) -> u64 {
+ unsafe { BNGetSymbolAddress(self.handle) }
+ }
+
+ pub fn auto_defined(&self) -> bool {
+ unsafe { BNIsSymbolAutoDefined(self.handle) }
+ }
+}
+
+impl fmt::Debug for Symbol {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "<sym {:?} '{}' @ {:x} (handle: {:?})>", self.sym_type(), self.name(), self.address(), self.handle)
+ }
+}
+
+impl ToOwned for Symbol {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for Symbol {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: BNNewSymbolReference(handle.handle),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeSymbol(handle.handle);
+ }
+}
+
+unsafe impl CoreOwnedArrayProvider for Symbol {
+ type Raw = *mut BNSymbol;
+ type Context = ();
+
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeSymbolList(raw, count);
+ }
+}
+
+unsafe impl<'a> CoreOwnedArrayWrapper<'a> for Symbol {
+ type Wrapped = Guard<'a, Symbol>;
+
+ unsafe fn wrap_raw(raw: &'a Self::Raw, context: &'a Self::Context) -> Self::Wrapped {
+ Guard::new(Symbol::from_raw(*raw), context)
+ }
+}
diff --git a/rust/src/types.rs b/rust/src/types.rs
new file mode 100644
index 00000000..80046f87
--- /dev/null
+++ b/rust/src/types.rs
@@ -0,0 +1,109 @@
+use std::mem;
+use std::slice;
+use binaryninjacore_sys::*;
+
+use crate::rc::*;
+
+#[derive(PartialEq, Eq, Hash)]
+pub struct Type {
+ pub(crate) handle: *mut BNType,
+}
+
+unsafe impl Send for Type {}
+unsafe impl Sync for Type {}
+
+impl Type {
+ pub(crate) unsafe fn from_raw(handle: *mut BNType) -> Self {
+ debug_assert!(!handle.is_null());
+
+ Self { handle }
+ }
+}
+
+impl ToOwned for Type {
+ type Owned = Ref<Self>;
+
+ fn to_owned(&self) -> Self::Owned {
+ unsafe { RefCountable::inc_ref(self) }
+ }
+}
+
+unsafe impl RefCountable for Type {
+ unsafe fn inc_ref(handle: &Self) -> Ref<Self> {
+ Ref::new(Self {
+ handle: BNNewTypeReference(handle.handle),
+ })
+ }
+
+ unsafe fn dec_ref(handle: &Self) {
+ BNFreeType(handle.handle);
+ }
+}
+
+#[repr(C)]
+pub struct QualifiedName {
+ object: BNQualifiedName,
+}
+
+impl QualifiedName {
+ pub fn string(&self) -> String {
+ use std::ffi::CStr;
+
+ unsafe {
+ slice::from_raw_parts(self.object.name, self.object.nameCount)
+ .iter()
+ .map(|c| CStr::from_ptr(*c).to_string_lossy())
+ .collect::<Vec<_>>()
+ .join("::")
+ }
+ }
+}
+
+impl Drop for QualifiedName {
+ fn drop(&mut self) {
+ unsafe { BNFreeQualifiedName(&mut self.object); }
+ }
+}
+
+#[repr(C)]
+pub struct QualifiedNameAndType {
+ object: BNQualifiedNameAndType,
+}
+
+impl QualifiedNameAndType {
+ pub fn name(&self) -> &QualifiedName {
+ unsafe {
+ mem::transmute(&self.object.name)
+ }
+ }
+
+ pub fn type_object(&self) -> Guard<Type> {
+ unsafe {
+ Guard::new(Type::from_raw(self.object.type_), self)
+ }
+ }
+}
+
+impl Drop for QualifiedNameAndType {
+ fn drop(&mut self) {
+ unsafe { BNFreeQualifiedNameAndType(&mut self.object); }
+ }
+}
+
+unsafe impl CoreOwnedArrayProvider for QualifiedNameAndType {
+ type Raw = BNQualifiedNameAndType;
+ type Context = ();
+
+ unsafe fn free(raw: *mut Self::Raw, count: usize, _context: &Self::Context) {
+ BNFreeTypeList(raw, count);
+ }
+}
+
+unsafe impl<'a> CoreOwnedArrayWrapper<'a> for QualifiedNameAndType {
+ type Wrapped = &'a QualifiedNameAndType;
+
+ unsafe fn wrap_raw(raw: &'a Self::Raw, _context: &'a Self::Context) -> Self::Wrapped {
+ mem::transmute(raw)
+ }
+}
+