Skip to content

Commit

Permalink
Resolves Vector35#2521; Rust API Linking Errors
Browse files Browse the repository at this point in the history
  • Loading branch information
ElykDeer authored and verylazyguy committed Apr 11, 2022
1 parent 6d2b50a commit 95f819e
Show file tree
Hide file tree
Showing 11 changed files with 551 additions and 136 deletions.
55 changes: 2 additions & 53 deletions rust/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

> :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
> :warning: This project requires **Rust Nightly** to build with those fancy linker arguments

## Dependencies
Expand All @@ -16,58 +16,7 @@ Rust **Nightly**

## How to use

### To write a plugin:

`Cargo.toml`:
```
[lib]
crate-type = ["cdylib"]
[dependencies]
binaryninja = {git = "https://github.com/Vector35/binaryninja-api.git", branch = "dev"}
```

`src/main.rs`:
See the `./examples/`. Plugin registration commands are in `binaryninja::command::*`


### To write a headless script:

`Cargo.toml`:
```
[dependencies]
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());
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`");
println!("Filename: `{}`", bv.metadata().filename());
println!("File size: `{:#x}`", bv.len());
println!("Function count: {}", bv.functions().len());
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();
}
```

All headless scripts should call both `binaryninja::headless::init()` and `binaryninja::headless::shutdown()`.
See [`examples/template`](examples/template).

---

Expand Down
102 changes: 19 additions & 83 deletions rust/binaryninjacore-sys/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,86 +6,12 @@ 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(target_os = "linux")]
static LASTRUN_PATH: (&str, &str) = ("HOME", ".binaryninja/lastrun");

#[cfg(windows)]
static LASTRUN_PATH: (&str, &str) = ("APPDATA", "Binary Ninja\\lastrun");

// 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);

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() {
println!("cargo:rerun-if-changed=../../binaryninjacore.h");

//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");

// 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(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(),
);

#[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")]
{
true
}
#[cfg(not(target_os = "macos"))]
{
false
}
};

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();
Expand All @@ -100,8 +26,6 @@ fn main() {
}
}

// 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 mut bindings = bindgen::builder()
.header("../../binaryninjacore.h")
.clang_arg("-std=c++17")
Expand All @@ -121,13 +45,25 @@ fn main() {
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 = 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{}/12.0.0/lib/clang/12.0.0/include", llvm_install_dir);
env::set_var("LIBCLANG_PATH", format!("{}/12.0.0/lib", llvm_install_dir));
bindings = bindings.clang_arg(llvm_include_path);

// 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, default is for people with "normal" setups (and Macs)
#[cfg(not(target_os = "macos"))]
{
// 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");

if let (Ok(llvm_dir), Ok(llvm_version)) = (llvm_dir, llvm_version) {
let llvm_include_path = format!("-I{}/clang/{}/include", llvm_dir, llvm_version);
bindings = bindings.clang_arg(llvm_include_path);
} else if let Ok(llvm_install_dir) = llvm_install_dir {
let llvm_include_path =
format!("-I{}/12.0.0/lib/clang/12.0.0/include", llvm_install_dir);
env::set_var("LIBCLANG_PATH", format!("{}/12.0.0/lib", llvm_install_dir));
bindings = bindings.clang_arg(llvm_include_path);
}
}

bindings
Expand Down
94 changes: 94 additions & 0 deletions rust/examples/basic_script/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
cmake_minimum_required(VERSION 3.9 FATAL_ERROR)

project(test_headless)

file(GLOB PLUGIN_SOURCES
${PROJECT_SOURCE_DIR}/Cargo.toml
${PROJECT_SOURCE_DIR}/src/*.rs)

file(GLOB API_SOURCES
${PROJECT_SOURCE_DIR}/../../../binaryninjacore.h
${PROJECT_SOURCE_DIR}/../../binaryninjacore-sys/build.rs
${PROJECT_SOURCE_DIR}/../../binaryninjacore-sys/Cargo.toml
${PROJECT_SOURCE_DIR}/../../binaryninjacore-sys/src/*
${PROJECT_SOURCE_DIR}/../../Cargo.toml
${PROJECT_SOURCE_DIR}/../../src/*.rs)

if(CMAKE_BUILD_TYPE MATCHES Debug)
set(TARGET_DIR ${PROJECT_BINARY_DIR}/target/debug)
set(CARGO_OPTS --target-dir=${PROJECT_BINARY_DIR}/target)
else()
set(TARGET_DIR ${PROJECT_BINARY_DIR}/target/release)
set(CARGO_OPTS --target-dir=${PROJECT_BINARY_DIR}/target --release)
endif()

set(OUTPUT_FILE basic_script${CMAKE_EXECUTABLE_SUFFIX})
set(OUTPUT_PATH ${CMAKE_BINARY_DIR}/out/bin/${OUTPUT_FILE})

add_custom_target(test_headless ALL DEPENDS ${OUTPUT_PATH})
add_dependencies(test_headless binaryninjaapi)

find_program(RUSTUP_PATH rustup REQUIRED HINTS ~/.cargo/bin)
set(INSTALL_UPDATE_NIGHTLY ${RUSTUP_PATH} install nightly)

if(APPLE)
if(UNIVERSAL)
if(CMAKE_BUILD_TYPE MATCHES Debug)
set(AARCH64_LIB_PATH ${PROJECT_BINARY_DIR}/target/aarch64-apple-darwin/debug/${OUTPUT_FILE})
set(X86_64_LIB_PATH ${PROJECT_BINARY_DIR}/target/x86_64-apple-darwin/debug/${OUTPUT_FILE})
else()
set(AARCH64_LIB_PATH ${PROJECT_BINARY_DIR}/target/aarch64-apple-darwin/release/${OUTPUT_FILE})
set(X86_64_LIB_PATH ${PROJECT_BINARY_DIR}/target/x86_64-apple-darwin/release/${OUTPUT_FILE})
endif()

add_custom_command(
OUTPUT ${OUTPUT_PATH}
COMMAND ${INSTALL_UPDATE_NIGHTLY}
COMMAND ${CMAKE_COMMAND} -E env
MACOSX_DEPLOYMENT_TARGET=10.14 LIBCLANG_PATH=${LLVM_PATH}/lib LLVM_VERSION=${LLVM_VERSION} BINARYNINJADIR=${BN_CORE_OUTPUT_DIR}
${RUSTUP_PATH} run nightly cargo build --target=aarch64-apple-darwin ${CARGO_OPTS}
COMMAND ${CMAKE_COMMAND} -E env
MACOSX_DEPLOYMENT_TARGET=10.14 LIBCLANG_PATH=${LLVM_PATH}/lib LLVM_VERSION=${LLVM_VERSION} BINARYNINJADIR=${BN_CORE_OUTPUT_DIR}
${RUSTUP_PATH} run nightly cargo build --target=x86_64-apple-darwin ${CARGO_OPTS}
COMMAND cp ${AARCH64_LIB_PATH} ${OUTPUT_PATH}-aarch
COMMAND cp ${X86_64_LIB_PATH} ${OUTPUT_PATH}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES})
else()
if(CMAKE_BUILD_TYPE MATCHES Debug)
set(X86_64_LIB_PATH ${PROJECT_BINARY_DIR}/target/x86_64-apple-darwin/debug/${OUTPUT_FILE})
else()
set(X86_64_LIB_PATH ${PROJECT_BINARY_DIR}/target/x86_64-apple-darwin/release/${OUTPUT_FILE})
endif()

add_custom_command(
OUTPUT ${OUTPUT_PATH}
COMMAND ${INSTALL_UPDATE_NIGHTLY}
COMMAND ${CMAKE_COMMAND} -E env
MACOSX_DEPLOYMENT_TARGET=10.14 LIBCLANG_PATH=${LLVM_PATH}/lib LLVM_VERSION=${LLVM_VERSION} BINARYNINJADIR=${BN_CORE_OUTPUT_DIR}
${RUSTUP_PATH} run nightly cargo build --target=x86_64-apple-darwin ${CARGO_OPTS}
COMMAND cp ${X86_64_LIB_PATH} ${OUTPUT_PATH}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES})
endif()
elseif(WIN32)
add_custom_command(
OUTPUT ${OUTPUT_PATH}
COMMAND ${INSTALL_UPDATE_NIGHTLY}
COMMAND ${CMAKE_COMMAND} -E env
LIBCLANG_PATH=${LLVM_PATH}/lib LLVM_VERSION=${LLVM_VERSION} BINARYNINJADIR=${BN_CORE_OUTPUT_DIR}
${RUSTUP_PATH} run nightly cargo build ${CARGO_OPTS}
COMMAND cp ${TARGET_DIR}/${OUTPUT_FILE} ${OUTPUT_PATH}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES})
else()
add_custom_command(
OUTPUT ${OUTPUT_PATH}
COMMAND ${INSTALL_UPDATE_NIGHTLY}
COMMAND ${CMAKE_COMMAND} -E env
LIBCLANG_PATH=${LLVM_PATH}/lib LLVM_VERSION=${LLVM_VERSION} BINARYNINJADIR=${BN_CORE_OUTPUT_DIR}
${RUSTUP_PATH} run nightly cargo build ${CARGO_OPTS}
COMMAND cp ${TARGET_DIR}/${OUTPUT_FILE} ${OUTPUT_PATH}
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
DEPENDS ${PLUGIN_SOURCES} ${API_SOURCES})
endif()
67 changes: 67 additions & 0 deletions rust/examples/basic_script/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
use std::env;
use std::fs::File;
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(target_os = "linux")]
static LASTRUN_PATH: (&str, &str) = ("HOME", ".binaryninja/lastrun");

#[cfg(windows)]
static LASTRUN_PATH: (&str, &str) = ("APPDATA", "Binary Ninja\\lastrun");

// 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);

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() {
// 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(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(),
);

#[cfg(target_os = "macos")]
println!(
"cargo:rustc-link-arg=-Wl,-rpath,{},-L{},-lbinaryninjacore",
install_path.to_str().unwrap(),
install_path.to_str().unwrap(),
);

#[cfg(target_os = "windows")]
{
println!("cargo:rustc-link-lib=binaryninjacore");
println!("cargo:rustc-link-search={}", install_path.to_str().unwrap());
}
}
Loading

0 comments on commit 95f819e

Please sign in to comment.