summaryrefslogtreecommitdiff
path: root/rust
diff options
context:
space:
mode:
authorKyleMiles <krm504@nyu.edu>2021-05-03 18:45:59 -0400
committerKyleMiles <krm504@nyu.edu>2021-05-11 16:53:24 -0400
commit4bd1f6b1477b910b580a0bcfcde118bcefc1ddc0 (patch)
treeb8e18454053a22c3f97645c6c52a0e6b0bc6a4e3 /rust
parenteb7a52bf4bd3b19c22f2eadda444ab045c674fe3 (diff)
Easier Building with Rust (now requires nightly)
Diffstat (limited to 'rust')
-rw-r--r--rust/.cargo/config.toml2
-rw-r--r--rust/Cargo.toml3
-rw-r--r--rust/README.md42
-rw-r--r--rust/binaryninjacore-sys/Cargo.toml5
-rw-r--r--rust/binaryninjacore-sys/build.rs210
-rw-r--r--rust/binaryninjacore-sys/linkhack/linkhack.c20
6 files changed, 98 insertions, 184 deletions
diff --git a/rust/.cargo/config.toml b/rust/.cargo/config.toml
new file mode 100644
index 00000000..ac01e73c
--- /dev/null
+++ b/rust/.cargo/config.toml
@@ -0,0 +1,2 @@
+[unstable]
+extra-link-arg = true
diff --git a/rust/Cargo.toml b/rust/Cargo.toml
index 69b34dce..c2543922 100644
--- a/rust/Cargo.toml
+++ b/rust/Cargo.toml
@@ -9,6 +9,3 @@ log = "0.4"
libc = "0.2"
rayon = { version = "1.0", optional = true }
binaryninjacore-sys = { path = "binaryninjacore-sys" }
-
-[features]
-headless = ["binaryninjacore-sys/headless"]
diff --git a/rust/README.md b/rust/README.md
index 8fca9c07..533494d6 100644
--- a/rust/README.md
+++ b/rust/README.md
@@ -2,10 +2,15 @@
> :warning: **These bindings are in a very early beta, only have partial support for the core APIs and are still actively under development. Compatibility _will_ break and conventions _will_ change! They are being used for core Binary Ninja features however, so we expect much of what is already there to be reliable enough to build on, just don't be surprised if your plugins/scripts need to hit a moving target.**
+> :warning: This project requires Rust Nightly to build with those fancy linker arguments
+
+
## Dependencies
Having BinaryNinja installed (and your license registered)
Clang
+Rust **Nightly**
+
## How to use
@@ -18,40 +23,49 @@ crate-type = ["cdylib"]
[dependencies]
binaryninja = {git = "https://github.com/Vector35/binaryninja-api.git", branch = "dev"}
-
```
`src/main.rs`:
-See the `./examples/`
+See the `./examples/`. Plugin registration commands are in `binaryninja::command::*`
+
-### To write a headless script
+### To write a headless script:
`Cargo.toml`:
```
[dependencies]
-binaryninja = { git = "https://github.com/Vector35/binaryninja-api.git", branch = "dev", features = ["headless"] }
-
+binaryninja = { git = "https://github.com/Vector35/binaryninja-api.git", branch = "dev"}
```
`src/main.rs`:
```
use binaryninja::version;
+use binaryninja::architecture::Architecture;
+use binaryninja::binaryview::{BinaryViewBase, BinaryViewExt};
fn main() {
println!("BinaryNinja Version: `{}`", version());
-}
-```
-## Contributing
+ println!("Loading plugins..."); // This loads all the core architecture, platform, etc plugins
+ binaryninja::headless::init();
+
+ println!("Loading binary...");
+ let bv = binaryninja::open_view("/bin/cat").expect("Couldn't open `/bin/cat`");
-If you want to advance the binaryninjacore.h reference, simply change the commit ID in `build.rs`
+ println!("Filename: `{}`", bv.metadata().filename());
+ println!("File size: `{:#x}`", bv.len());
+ println!("Function count: {}", bv.functions().len());
-## WIP : TODO
+ for func in &bv.functions() {
+ println!(" `{}`:", func.symbol().full_name());
+ }
+
+ // Important! You need to call shutdown or your script will hang forever
+ binaryninja::headless::shutdown();
+}
+```
- - Logging needs to be redone
- - Update libc requirement on binaryninja::
- - Rename `section_by_name` to `get_section_by_name` and similar
-many other todos are still scattered in the codebase
+All headless scripts should call both `binaryninja::headless::init()` and `binaryninja::headless::shutdown()`.
---
diff --git a/rust/binaryninjacore-sys/Cargo.toml b/rust/binaryninjacore-sys/Cargo.toml
index e06c2db3..4abf89f3 100644
--- a/rust/binaryninjacore-sys/Cargo.toml
+++ b/rust/binaryninjacore-sys/Cargo.toml
@@ -5,7 +5,4 @@ authors = ["Ryan Snyder <ryan@vector35.com>", "Kyle Martin <kyle@vector35.com>"]
build = "build.rs"
[build-dependencies]
-bindgen = "0.57"
-
-[features]
-headless = []
+bindgen = "0.58.1"
diff --git a/rust/binaryninjacore-sys/build.rs b/rust/binaryninjacore-sys/build.rs
index 9457c250..d3b02818 100644
--- a/rust/binaryninjacore-sys/build.rs
+++ b/rust/binaryninjacore-sys/build.rs
@@ -1,26 +1,24 @@
extern crate bindgen;
use std::env;
-use std::path::PathBuf;
-
-#[cfg(any(not(target_os = "linux"), feature = "headless"))]
use std::fs::File;
-#[cfg(any(not(target_os = "linux"), feature = "headless"))]
-use std::io::prelude::*;
-#[cfg(any(not(target_os = "linux"), feature = "headless"))]
+use std::io::BufRead;
use std::io::BufReader;
+use std::path::PathBuf;
#[cfg(target_os = "macos")]
static LASTRUN_PATH: (&str, &str) = ("HOME", "Library/Application Support/Binary Ninja/lastrun");
-#[cfg(all(target_os = "linux", feature = "headless"))]
+#[cfg(target_os = "linux")]
static LASTRUN_PATH: (&str, &str) = ("HOME", ".binaryninja/lastrun");
#[cfg(windows)]
static LASTRUN_PATH: (&str, &str) = ("APPDATA", "Binary Ninja\\lastrun");
-#[cfg(any(not(target_os = "linux"), feature = "headless"))]
+// Check last run location for path to BinaryNinja; Otherwise check the default install locations
fn link_path() -> PathBuf {
+ use std::io::prelude::*;
+
let home = PathBuf::from(env::var(LASTRUN_PATH.0).unwrap());
let lastrun = PathBuf::from(&home).join(LASTRUN_PATH.1);
@@ -48,167 +46,93 @@ fn link_path() -> PathBuf {
fn main() {
println!("cargo:rerun-if-changed=../../binaryninjacore.h");
- // Allow the library search path to be overridden for internal dev builds, but
- // otherwise search the usual install paths
+ //Cargo's output directory
let out_dir = env::var("OUT_DIR").unwrap();
+
+ // Detect for custom Clang or LLVM installations (BN devs/build server)
let llvm_dir = env::var("LIBCLANG_PATH");
let llvm_version = env::var("LLVM_VERSION");
let llvm_install_dir = env::var("LLVM_INSTALL_DIR");
- #[cfg(any(not(target_os = "linux"), feature = "headless"))]
- let link_path = env::var("BINARYNINJADIR")
+ // Use BINARYNINJADIR first for custom BN builds/configurations (BN devs/build server), fallback on defaults
+ let install_path = env::var("BINARYNINJADIR")
.map(PathBuf::from)
.unwrap_or_else(|_| link_path());
- #[cfg(all(unix, feature = "headless"))]
- {
- use std::process::Command;
+ #[cfg(target_os = "linux")]
+ println!(
+ "cargo:rustc-link-arg=-Wl,-rpath,{},-L{},-l:libbinaryninjacore.so.1",
+ install_path.to_str().unwrap(),
+ install_path.to_str().unwrap(),
+ );
- // 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 ON 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(not(target_os = "linux"))]
+ println!(
+ "cargo:rustc-link-arg=-Wl,-rpath,{},-L{},-lbinaryninjacore",
+ install_path.to_str().unwrap(),
+ install_path.to_str().unwrap(),
+ );
+ // TODO : Clean this up even more
+ #[warn(unused_assignments)]
+ let is_mac = {
#[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()
- )) // TODO : Check mac to see if I need to remove the extra slash as well
- .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");
- }
+ true
}
+ #[cfg(not(target_os = "macos"))]
+ {
+ false
+ }
+ };
- println!("cargo:rustc-link-lib=linkhack");
- println!("cargo:rustc-link-search={}", out_dir);
- }
-
- #[cfg(any(not(target_os = "linux"), feature = "headless"))]
- {
- println!("cargo:rustc-link-lib=binaryninjacore");
- println!("cargo:rustc-link-search={}", link_path.to_str().unwrap());
- }
-
- #[warn(unused_assignments)]
- let mut is_mac = false;
- #[cfg(target_os = "macos")]
- {
- is_mac = true;
+ let current_line = "#define BN_CURRENT_UI_ABI_VERSION ";
+ let minimum_line = "#define BN_MINIMUM_UI_ABI_VERSION ";
+ let mut current_version = "0".to_string();
+ let mut minimum_version = "0".to_string();
+ let file = File::open("../../ui/uitypes.h").expect("Couldn't open uitypes.h");
+ for line in BufReader::new(file).lines() {
+ let line = line.unwrap();
+ if line.starts_with(current_line) {
+ current_version = (&line[current_line.len()..]).to_owned();
+ } else if line.starts_with(minimum_line) {
+ minimum_version = (&line[minimum_line.len()..]).to_owned();
+ }
}
// Difference between global LLVM/Clang install and custom LLVM/Clang install...
// First option is for the build server, second option is being nice to our dev who have `LLVM_INSTALL_DIR` set, third is for people with "normal" setups (and Macs)
- let bindings;
+ let mut bindings = bindgen::builder()
+ .header("../../binaryninjacore.h")
+ .clang_arg("-std=c++17")
+ .clang_arg("-x")
+ .clang_arg("c++")
+ .size_t_is_usize(true)
+ .generate_comments(false)
+ .allowlist_function("BN.*")
+ .allowlist_var("BN_CURRENT_CORE_ABI_VERSION")
+ .allowlist_var("BN_MINIMUM_CORE_ABI_VERSION")
+ .raw_line(format!(
+ "pub const BN_CURRENT_UI_ABI_VERSION: u32 = {};",
+ current_version
+ ))
+ .raw_line(format!(
+ "pub const BN_MINIMUM_UI_ABI_VERSION: u32 = {};",
+ minimum_version
+ ))
+ .rustified_enum("BN.*");
if let (false, Ok(llvm_dir), Ok(llvm_version)) = (is_mac, llvm_dir, llvm_version) {
let llvm_include_path = format!("-I{}/clang/{}/include", llvm_dir, llvm_version);
- bindings = bindgen::builder()
- .header("../../binaryninjacore.h")
- .clang_arg("-std=c++17")
- .clang_arg("-x")
- .clang_arg("c++")
- .clang_arg(llvm_include_path)
- .size_t_is_usize(true)
- .generate_comments(false)
- .whitelist_function("BN.*")
- .whitelist_var("BN_CURRENT_CORE_ABI_VERSION")
- .whitelist_var("BN_MINIMUM_CORE_ABI_VERSION")
- .rustified_enum("BN.*")
- .generate()
- .expect("Unable to generate bindings");
+ bindings = bindings.clang_arg(llvm_include_path);
} else if let (false, Ok(llvm_install_dir)) = (is_mac, llvm_install_dir) {
let llvm_include_path = format!("-I{}/11.0.0/lib/clang/11.0.0/include", llvm_install_dir);
env::set_var("LIBCLANG_PATH", format!("{}/11.0.0/lib", llvm_install_dir));
- bindings = bindgen::builder()
- .header("../../binaryninjacore.h")
- .clang_arg("-std=c++17")
- .clang_arg("-x")
- .clang_arg("c++")
- .clang_arg(llvm_include_path)
- .size_t_is_usize(true)
- .generate_comments(false)
- .whitelist_function("BN.*")
- .whitelist_var("BN_CURRENT_CORE_ABI_VERSION")
- .whitelist_var("BN_MINIMUM_CORE_ABI_VERSION")
- .rustified_enum("BN.*")
- .generate()
- .expect("Unable to generate bindings");
- } else {
- bindings = bindgen::builder()
- .header("../../binaryninjacore.h")
- .clang_arg("-std=c++17")
- .clang_arg("-x")
- .clang_arg("c++")
- .size_t_is_usize(true)
- .generate_comments(false)
- .whitelist_function("BN.*")
- .whitelist_var("BN_CURRENT_CORE_ABI_VERSION")
- .whitelist_var("BN_MINIMUM_CORE_ABI_VERSION")
- .rustified_enum("BN.*")
- .generate()
- .expect("Unable to generate bindings");
+ bindings = bindings.clang_arg(llvm_include_path);
}
bindings
+ .generate()
+ .expect("Unable to generate 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
deleted file mode 100644
index 6d4b3f37..00000000
--- a/rust/binaryninjacore-sys/linkhack/linkhack.c
+++ /dev/null
@@ -1,20 +0,0 @@
-// 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() {}