Skip to content

Commit f53caf8

Browse files
committed
[Rust] Prepare rust bindings for publishing
1 parent e8e6151 commit f53caf8

5 files changed

Lines changed: 47 additions & 14 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ rust/**/*.swp
8686
rust/**/*.rs.bk
8787
rust/target/
8888
rust/binaryninjacore-sys/target/
89+
rust/binaryninjacore-sys/vendor/
8990
rust/examples/*/target/
9091
rust/examples/dwarf/*/target/
9192
# Allow the test binaries

rust/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ authors = ["Ryan Snyder <ryan@vector35.com>", "Kyle Martin <kyle@vector35.com>"]
55
edition = "2021"
66
rust-version = "1.91.1"
77
license = "Apache-2.0"
8+
description = "Binary Ninja bindings"
9+
keywords = ["binary", "binja", "analysis", "program", "reverse-engineering"]
10+
categories = ["security"]
11+
repository = "https://github.com/Vector35/binaryninja-api"
12+
homepage = "https://binary.ninja"
13+
exclude = ["fixtures"]
814

915
[features]
1016
default = ["core"]

rust/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Official Rust bindings for [Binary Ninja].
1010
## WARNING
1111

1212
These bindings are still actively under development. Compatibility _will_ break and conventions _will_ change!
13+
1314
It is encouraged that you reference a specific commit to avoid having your plugin/application break when the API changes.
1415
To specify a specific commit, see the cargo documentation [here](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html#choice-of-commit).
1516

rust/binaryninjacore-sys/Cargo.toml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ build = "build.rs"
66
edition = "2021"
77
links = "binaryninjacore"
88
license = "Apache-2.0"
9+
description = "FFI Bindings to Binary Ninja"
10+
keywords = ["binary", "binja", "analysis", "program", "reverse-engineering"]
11+
categories = ["security"]
12+
repository = "https://github.com/Vector35/binaryninja-api"
13+
homepage = "https://binary.ninja"
14+
include = ["**/**"]
915

1016
[features]
1117
default = ["core"]

rust/binaryninjacore-sys/build.rs

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
use std::env;
21
use std::fs::File;
32
use std::io::BufRead;
43
use std::io::BufReader;
5-
use std::path::PathBuf;
4+
use std::path::{Path, PathBuf};
65

76
#[cfg(target_os = "macos")]
87
static LASTRUN_PATH: (&str, &str) = ("HOME", "Library/Application Support/Binary Ninja/lastrun");
@@ -13,11 +12,11 @@ static LASTRUN_PATH: (&str, &str) = ("HOME", ".binaryninja/lastrun");
1312
#[cfg(windows)]
1413
static LASTRUN_PATH: (&str, &str) = ("APPDATA", "Binary Ninja\\lastrun");
1514

16-
// Check last run location for path to BinaryNinja; Otherwise check the default install locations
15+
/// Check the last run location for the path to binary ninja core, falling back to default install locations.
1716
fn link_path() -> PathBuf {
1817
use std::io::prelude::*;
1918

20-
let home = PathBuf::from(env::var(LASTRUN_PATH.0).unwrap());
19+
let home = PathBuf::from(std::env::var(LASTRUN_PATH.0).unwrap());
2120
let lastrun = PathBuf::from(&home).join(LASTRUN_PATH.1);
2221

2322
File::open(lastrun)
@@ -41,33 +40,45 @@ fn link_path() -> PathBuf {
4140
})
4241
}
4342

44-
// Generate and compile stub shared library and return the path to the folder containing the built stub library
43+
/// Generate and compile a stubbed out shared library and return the path to the folder containing
44+
/// the built stub library, this allows us to link without actually having the real core library.
4545
fn generate_stubs() -> PathBuf {
4646
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set");
47-
48-
let api_base_path = std::path::PathBuf::from(manifest_dir)
47+
let api_base_path = PathBuf::from(manifest_dir)
4948
.join("../../")
5049
.canonicalize()
5150
.unwrap();
52-
5351
let stubs_path = api_base_path.join("stubs");
54-
5552
// TODO: does visual studio add the config name as a subdirectory?
5653
cmake::Config::new(stubs_path)
5754
.generator("Ninja")
5855
.build()
5956
.join("build")
6057
}
6158

59+
/// Find a header file for parsing, checking the vendored directory, falling back to the default location.
60+
///
61+
/// The `vendor` directory is used when publishing the crate, as the headers must be contained within
62+
/// the packaged crate, for more information see https://doc.rust-lang.org/cargo/reference/publishing.html#packaging-a-crate
63+
fn find_header(name: &str, fallback_path: &str) -> PathBuf {
64+
let manifest_dir = std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR not set");
65+
let vendored = Path::new(&manifest_dir).join("vendor").join(name);
66+
if vendored.exists() {
67+
vendored
68+
} else {
69+
PathBuf::from(fallback_path)
70+
}
71+
}
72+
6273
fn main() {
6374
println!("cargo:rerun-if-env-changed=BINARYNINJADIR");
6475
println!("cargo:rerun-if-changed=../../binaryninjacore.h");
6576

6677
//Cargo's output directory
67-
let out_dir = env::var("OUT_DIR").unwrap();
78+
let out_dir = std::env::var("OUT_DIR").unwrap();
6879

69-
let link_path = match env::var("CARGO_FEATURE_CORE") {
70-
Ok(_) => env::var("BINARYNINJADIR")
80+
let link_path = match std::env::var("CARGO_FEATURE_CORE") {
81+
Ok(_) => std::env::var("BINARYNINJADIR")
7182
.map(PathBuf::from)
7283
.unwrap_or_else(|_| link_path()),
7384
Err(_) => generate_stubs(),
@@ -93,11 +104,19 @@ fn main() {
93104
// Emit the path to binaryninjacore consumers of this crate should pass to the linker.
94105
println!("cargo:path={}", link_path.to_str().unwrap());
95106

107+
// We need to vendor the headers when publishing the crate, otherwise the headers will be missing
108+
// from the packaged crate, and building will fail.
109+
let header_path = find_header("binaryninjacore.h", "../../binaryninjacore.h");
110+
let ui_header_path = find_header("uitypes.h", "../../ui/uitypes.h");
111+
96112
let current_line = "#define BN_CURRENT_UI_ABI_VERSION ";
97113
let minimum_line = "#define BN_MINIMUM_UI_ABI_VERSION ";
98114
let mut current_version = "0".to_string();
99115
let mut minimum_version = "0".to_string();
100-
let file = File::open("../../ui/uitypes.h").expect("Couldn't open uitypes.h");
116+
let file = File::open(&ui_header_path).expect(&format!(
117+
"Couldn't open '{}'",
118+
ui_header_path.to_string_lossy()
119+
));
101120
for line in BufReader::new(file).lines() {
102121
let line = line.unwrap();
103122
if let Some(version) = line.strip_prefix(current_line) {
@@ -108,7 +127,7 @@ fn main() {
108127
}
109128

110129
bindgen::builder()
111-
.header("../../binaryninjacore.h")
130+
.header(header_path.to_string_lossy())
112131
.clang_arg("-std=c++20")
113132
.clang_arg("-x")
114133
.clang_arg("c++")

0 commit comments

Comments
 (0)