Squashed commit of the following:
commit 520c6647c32f02d83083d969d416154aa95e922c
Merge: 6bb29b12 b999f507
Author: mio <mio@lazym.io>
Date: Sun Apr 13 00:14:23 2025 +0800
merge dev
commit 6bb29b12f1d9f452365cc9cb5bc2d65ef376af30
Author: mio <mio@lazym.io>
Date: Sun Apr 13 00:13:12 2025 +0800
enable test
commit bcb8b363ef12ac295cf4fe4f1645416e5f0ea6ae
Author: mio <mio@lazym.io>
Date: Sun Apr 13 00:13:06 2025 +0800
also logging
commit 5972fc156b7379d09582c745d6d597e07555f2f4
Author: mio <mio@lazym.io>
Date: Sun Apr 13 00:12:58 2025 +0800
no unlimited translation
commit 7d600feebf9055505918e50d0af8b529a3eba542
Author: mio <mio@lazym.io>
Date: Sun Apr 13 00:12:47 2025 +0800
Ignore bindings.rs
commit dde4d50f2c7713156ac3bc284287480e4d92005f
Author: Amaan Qureshi <amaanq12@gmail.com>
Date: Sun Apr 6 03:26:22 2025 -0400
alias `uc_mips_reg` to `UC_MIPS_REG`
commit 04234ae01ba7c82d9717eaae64cdda289ce3b832
Author: Amaan Qureshi <amaanq12@gmail.com>
Date: Sun Apr 6 01:13:00 2025 -0400
remove bindings.rs
commit edec1300cd7c2d8ef4babbd51f6bcba2e126bdd7
Author: Amaan Qureshi <amaanq12@gmail.com>
Date: Sat Apr 5 14:29:40 2025 -0400
address review
commit feb157b28b6c262c5dc3d810ec54de55a25bcd6e
Author: Amaan Qureshi <amaanq12@gmail.com>
Date: Sat Mar 29 22:40:53 2025 -0400
ci(rust): rework workflow
The notable changes are migrating to
`actions-rust-lang/setup-rust-toolchain` for setting up Rust as it's
maintained, and using `katyo/publish-crates` for publishing crates in a
workspace
commit c1c7a8f8ed841b6ec5b4abe57013a1c2c9748c60
Author: Amaan Qureshi <amaanq12@gmail.com>
Date: Sat Mar 29 22:40:06 2025 -0400
build(rust): set `rust-version` to 1.85
commit 8df938c9f8b478160213707674157103b0893caf
Author: Amaan Qureshi <amaanq12@gmail.com>
Date: Sat Mar 29 21:53:21 2025 -0400
fix(rust): correct unsound pointer cast
The size of `T` is not guaranteed to be the size of `i32` - all we know
is that `T` is `Into<i32>`, so we should first copy them over into an
`i32` array
commit 3059b2583a60aa0cac9278afc945ed87f7ddb65e
Author: Amaan Qureshi <amaanq12@gmail.com>
Date: Sat Mar 29 20:13:26 2025 -0400
docs(rust): update readme
commit 7db69a888e58a4bda20083e4e0771d26a327ad13
Author: Amaan Qureshi <amaanq12@gmail.com>
Date: Sat Mar 29 13:58:30 2025 -0400
feat(rust): add comprehensive tests
These tests are copied over from the C tests
commit 78f2207f0e0481aef4de6d5908f8dc699a39a8d5
Author: Amaan Qureshi <amaanq12@gmail.com>
Date: Sat Mar 29 13:57:27 2025 -0400
feat(rust): add tcg hook
commit 46e53328531ec3279dadbf18c16b493432227b31
Author: Amaan Qureshi <amaanq12@gmail.com>
Date: Sat Mar 29 13:56:55 2025 -0400
feat(rust): add a hook for arm64 sys instructions
commit d1b58ee8282bf1eeeefbf68c87c2cf7c50c90320
Author: Amaan Qureshi <amaanq12@gmail.com>
Date: Sat Mar 29 13:56:35 2025 -0400
feat(rust): add the ability to read the arm coprocessor register
commit d304da18b9e6741042b2a70657437be8f39f5c7c
Author: Amaan Qureshi <amaanq12@gmail.com>
Date: Sat Mar 29 13:55:29 2025 -0400
feat(rust): add missing `Context` methods
commit 0dd87833081ac9db1feaf5bae8c839a7a2ae4947
Author: Amaan Qureshi <amaanq12@gmail.com>
Date: Sat Mar 29 13:44:51 2025 -0400
refactor(rust): remove unnecessary code
`unicorn-engine-sys` will provide the necessary constants & types
commit da3d2fa7c3ecd3ae8fdb6672b6c5ea23da4570ff
Author: Amaan Qureshi <amaanq12@gmail.com>
Date: Sat Mar 29 13:43:57 2025 -0400
feat(rust): add a workspace `Cargo.toml`, and use `unicorn-engine-sys`
commit b27a2a93e4ac43aa2079e936df4dd30a1f8f329a
Author: Amaan Qureshi <amaanq12@gmail.com>
Date: Sat Mar 29 13:38:06 2025 -0400
feat(rust): introduce `unicorn-engine-sys` crate
This crate contains generated Rust bindings to the C library via
bindgen. It is independent from the main `unicorn-engine` bindings,
which will leverage this
commit bcec87a3f6e316e328683c303ccfa89e530a6c56
Author: Amaan Qureshi <amaanq12@gmail.com>
Date: Sat Mar 29 13:31:24 2025 -0400
test(m68k): actually assert an expectation
This test did not actually test for anything before
commit bc7e65ca96164496eb2e250b1f296a33a8aa58ee
Author: Amaan Qureshi <amaanq12@gmail.com>
Date: Sat Mar 29 13:31:09 2025 -0400
style(test): use bitflag shorthands
commit 0ab4b7fefb3ca17b0b5977d7b204291c5de184ad
Author: Amaan Qureshi <amaanq12@gmail.com>
Date: Sat Mar 29 13:22:13 2025 -0400
fix(mips): lowercase enum name `uc_mips_reg`
This aligns with other architectures
Co-authored-by: Amaan Qureshi <amaanq12@gmail.com>
This commit is contained in:
55
bindings/rust/sys/Cargo.toml
Normal file
55
bindings/rust/sys/Cargo.toml
Normal file
@@ -0,0 +1,55 @@
|
||||
[package]
|
||||
name = "unicorn-engine-sys"
|
||||
version.workspace = true
|
||||
authors.workspace = true
|
||||
keywords.workspace = true
|
||||
categories = [
|
||||
"api-bindings",
|
||||
"emulators",
|
||||
"external-ffi-bindings",
|
||||
"no-std",
|
||||
"virtualization",
|
||||
]
|
||||
documentation.workspace = true
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
readme = "README.md"
|
||||
repository.workspace = true
|
||||
description.workspace = true
|
||||
links = "unicorn"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.71.1"
|
||||
cc = { version = "1.2.17" }
|
||||
cmake = { version = "0.1.54" }
|
||||
heck = "0.5.0"
|
||||
pkg-config = { version = "0.3.32" }
|
||||
|
||||
[features]
|
||||
default = ["arch_all"]
|
||||
dynamic_linkage = []
|
||||
arch_all = [
|
||||
"arch_x86",
|
||||
"arch_arm",
|
||||
"arch_aarch64",
|
||||
"arch_riscv",
|
||||
"arch_mips",
|
||||
"arch_sparc",
|
||||
"arch_m68k",
|
||||
"arch_ppc",
|
||||
"arch_s390x",
|
||||
"arch_tricore",
|
||||
]
|
||||
arch_x86 = []
|
||||
arch_arm = []
|
||||
arch_aarch64 = ["arch_arm"]
|
||||
arch_riscv = []
|
||||
arch_mips = []
|
||||
arch_sparc = []
|
||||
arch_m68k = []
|
||||
arch_ppc = []
|
||||
arch_s390x = []
|
||||
arch_tricore = []
|
||||
61
bindings/rust/sys/README.md
Normal file
61
bindings/rust/sys/README.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# Unicorn-engine-sys
|
||||
|
||||
Low-level Rust bindings for the [Unicorn](http://www.unicorn-engine.org/) emulator. This crate only exposes the C API of Unicorn.
|
||||
|
||||
Checkout Unicorn2 source code at [dev branch](https://github.com/unicorn-engine/unicorn/tree/dev).
|
||||
|
||||
```rust
|
||||
use unicorn_engine_sys::{
|
||||
Arch, Mode, uc_close, uc_emu_start, uc_engine, uc_mem_map, uc_mem_write, uc_open,
|
||||
uc_reg_read, uc_reg_write,
|
||||
};
|
||||
|
||||
fn main() {
|
||||
let mut uc_engine: *mut uc_engine = std::ptr::null_mut();
|
||||
let err = unsafe { uc_open(Arch::ARM, Mode::ARM, &raw mut uc_engine) };
|
||||
assert_eq!(err, uc_error::OK, "Failed to open Unicorn engine");
|
||||
|
||||
let code: [u8; 4] = [0x17, 0x00, 0x40, 0xe2]; // sub r0, #23
|
||||
let err = unsafe { uc_mem_map(uc_engine, CODE_START, 0x1000, Prot::ALL.0) };
|
||||
assert_eq!(err, uc_error::OK, "Failed to map memory");
|
||||
|
||||
let err = unsafe { uc_mem_write(uc_engine, CODE_START, code.as_ptr().cast(), code.len()) };
|
||||
assert_eq!(err, uc_error::OK, "Failed to write memory");
|
||||
|
||||
let mut r0: u64 = 123;
|
||||
let err = unsafe { uc_reg_write(uc_engine, RegisterARM::R0 as i32, (&raw mut r0).cast()) };
|
||||
assert_eq!(err, uc_error::OK, "Failed to write R0");
|
||||
|
||||
let mut r5: u64 = 1337;
|
||||
let err = unsafe { uc_reg_write(uc_engine, RegisterARM::R5 as i32, (&raw mut r5).cast()) };
|
||||
assert_eq!(err, uc_error::OK, "Failed to write R5");
|
||||
|
||||
let err = unsafe { uc_emu_start(uc_engine, CODE_START, CODE_START + code.len() as u64, 0, 0) };
|
||||
assert_eq!(err, uc_error::OK, "Failed to start emulation");
|
||||
|
||||
r0 = 0;
|
||||
let err = unsafe { uc_reg_read(uc_engine, RegisterARM::R0 as i32, (&raw mut r0).cast()) };
|
||||
assert_eq!(err, uc_error::OK, "Failed to read R0");
|
||||
|
||||
r5 = 0;
|
||||
let err = unsafe { uc_reg_read(uc_engine, RegisterARM::R5 as i32, (&raw mut r5).cast()) };
|
||||
assert_eq!(err, uc_error::OK, "Failed to read R5");
|
||||
|
||||
assert_eq!(r0, 100);
|
||||
assert_eq!(r5, 1337);
|
||||
|
||||
// Clean up
|
||||
unsafe { uc_close(uc_engine) };
|
||||
}
|
||||
```
|
||||
|
||||
Further sample code can be found in [tests](./src/tests).
|
||||
|
||||
## Usage
|
||||
|
||||
Add this to your `Cargo.toml`:
|
||||
|
||||
```
|
||||
[dependencies]
|
||||
unicorn-engine-sys = "2.2.0"
|
||||
```
|
||||
356
bindings/rust/sys/build.rs
Normal file
356
bindings/rust/sys/build.rs
Normal file
@@ -0,0 +1,356 @@
|
||||
use std::{env, path::PathBuf, process::Command};
|
||||
|
||||
use bindgen::callbacks::{EnumVariantValue, ParseCallbacks};
|
||||
use heck::ToUpperCamelCase;
|
||||
|
||||
fn ninja_available() -> bool {
|
||||
Command::new("ninja").arg("--version").spawn().is_ok()
|
||||
}
|
||||
|
||||
fn msvc_cmake_tools_available() -> bool {
|
||||
Command::new("cmake").arg("--version").spawn().is_ok() && ninja_available()
|
||||
}
|
||||
|
||||
fn get_tool_paths_msvc(compiler: &cc::Tool) -> Option<(PathBuf, PathBuf)> {
|
||||
// If tools are already available, don't need to find them
|
||||
if msvc_cmake_tools_available() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let target = env::var("TARGET").unwrap();
|
||||
let devenv = cc::windows_registry::find_tool(target.as_str(), "devenv");
|
||||
let tool_root = devenv.map_or_else(
|
||||
|| {
|
||||
// if devenv (i.e. Visual Studio) was not found, assume compiler is
|
||||
// from standalone Build Tools and look there instead.
|
||||
let tools_name = std::ffi::OsStr::new("BuildTools");
|
||||
let compiler_path = compiler.path().to_path_buf();
|
||||
compiler_path
|
||||
.iter()
|
||||
.find(|x| *x == tools_name)
|
||||
.expect("Failed to find devenv or Build Tools");
|
||||
compiler_path
|
||||
.iter()
|
||||
.take_while(|x| *x != tools_name)
|
||||
.collect::<PathBuf>()
|
||||
.join(tools_name)
|
||||
.join(r"Common7\IDE")
|
||||
},
|
||||
|devenv_tool| devenv_tool.path().parent().unwrap().to_path_buf(),
|
||||
);
|
||||
let cmake_pkg_dir = tool_root.join(r"CommonExtensions\Microsoft\CMake");
|
||||
let cmake_path = cmake_pkg_dir.join(r"CMake\bin\cmake.exe");
|
||||
let ninja_path = cmake_pkg_dir.join(r"Ninja\ninja.exe");
|
||||
|
||||
assert!(cmake_path.is_file(), "missing cmake");
|
||||
assert!(ninja_path.is_file(), "missing ninja");
|
||||
|
||||
Some((cmake_path, ninja_path))
|
||||
}
|
||||
|
||||
fn build_with_cmake() {
|
||||
let current_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
|
||||
let uc_dir = current_dir
|
||||
.parent()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap()
|
||||
.parent()
|
||||
.unwrap();
|
||||
let compiler = cc::Build::new().get_compiler();
|
||||
|
||||
// Initialize configuration
|
||||
let mut config = cmake::Config::new(uc_dir);
|
||||
|
||||
// Check for tools and set up configuration
|
||||
let has_ninja = if compiler.is_like_msvc() {
|
||||
// MSVC-specific setup
|
||||
if let Some((cmake_path, ninja_path)) = get_tool_paths_msvc(&compiler) {
|
||||
// Tell Cargo where to find the tools instead of modifying PATH
|
||||
println!("cargo:rustc-env=CMAKE_PATH={}", cmake_path.display());
|
||||
println!("cargo:rustc-env=NINJA_PATH={}", ninja_path.display());
|
||||
|
||||
// Set cmake path for the cmake crate
|
||||
config.define("CMAKE_PROGRAM", cmake_path.to_str().unwrap());
|
||||
}
|
||||
|
||||
// MSVC-specific linker flags
|
||||
println!("cargo:rustc-link-arg=/FORCE:MULTIPLE");
|
||||
true
|
||||
} else {
|
||||
// Non-MSVC setup
|
||||
ninja_available()
|
||||
};
|
||||
|
||||
// GNU-specific linker flags (but not on macOS)
|
||||
if compiler.is_like_gnu() && env::consts::OS != "macos" {
|
||||
println!("cargo:rustc-link-arg=-Wl,-allow-multiple-definition");
|
||||
}
|
||||
|
||||
// Configure build generator
|
||||
if has_ninja {
|
||||
config.generator("Ninja");
|
||||
}
|
||||
|
||||
let mut archs = String::new();
|
||||
|
||||
if std::env::var("CARGO_FEATURE_ARCH_X86").is_ok() {
|
||||
archs.push_str("x86;");
|
||||
}
|
||||
if std::env::var("CARGO_FEATURE_ARCH_ARM").is_ok() {
|
||||
archs.push_str("arm;");
|
||||
}
|
||||
if std::env::var("CARGO_FEATURE_ARCH_AARCH64").is_ok() {
|
||||
archs.push_str("aarch64;");
|
||||
}
|
||||
if std::env::var("CARGO_FEATURE_ARCH_RISCV").is_ok() {
|
||||
archs.push_str("riscv;");
|
||||
}
|
||||
if std::env::var("CARGO_FEATURE_ARCH_MIPS").is_ok() {
|
||||
archs.push_str("mips;");
|
||||
}
|
||||
if std::env::var("CARGO_FEATURE_ARCH_SPARC").is_ok() {
|
||||
archs.push_str("sparc;");
|
||||
}
|
||||
if std::env::var("CARGO_FEATURE_ARCH_M68K").is_ok() {
|
||||
archs.push_str("m68k;");
|
||||
}
|
||||
if std::env::var("CARGO_FEATURE_ARCH_PPC").is_ok() {
|
||||
archs.push_str("ppc;");
|
||||
}
|
||||
if std::env::var("CARGO_FEATURE_ARCH_S390X").is_ok() {
|
||||
archs.push_str("s390x;");
|
||||
}
|
||||
if std::env::var("CARGO_FEATURE_ARCH_TRICORE").is_ok() {
|
||||
archs.push_str("tricore;");
|
||||
}
|
||||
|
||||
if !archs.is_empty() {
|
||||
archs.pop();
|
||||
}
|
||||
|
||||
if config.get_profile() == "Debug" {
|
||||
config.define("UNICORN_LOGGING", "ON");
|
||||
}
|
||||
|
||||
// need to clear build target and append "build" to the path because
|
||||
// unicorn's CMakeLists.txt doesn't properly support 'install', so we use
|
||||
// the build artifacts from the build directory, which cmake crate sets
|
||||
// to "<out_dir>/build/"
|
||||
let dst = config
|
||||
.define("UNICORN_BUILD_TESTS", "OFF")
|
||||
.define("UNICORN_INSTALL", "OFF")
|
||||
.define("UNICORN_ARCH", archs)
|
||||
.no_build_target(true)
|
||||
.build();
|
||||
|
||||
println!(
|
||||
"cargo:rustc-link-search=native={}",
|
||||
dst.join("build").display()
|
||||
);
|
||||
|
||||
// Lazymio(@wtdcode): Dynamic link may break. See: https://github.com/rust-lang/cargo/issues/5077
|
||||
if cfg!(feature = "dynamic_linkage") {
|
||||
if compiler.is_like_msvc() {
|
||||
println!("cargo:rustc-link-lib=dylib=unicorn-import");
|
||||
} else {
|
||||
println!("cargo:rustc-link-lib=dylib=unicorn");
|
||||
}
|
||||
} else {
|
||||
println!("cargo:rustc-link-lib=static=unicorn");
|
||||
}
|
||||
if !compiler.is_like_msvc() {
|
||||
println!("cargo:rustc-link-lib=pthread");
|
||||
println!("cargo:rustc-link-lib=m");
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Renamer;
|
||||
|
||||
impl ParseCallbacks for Renamer {
|
||||
fn item_name(&self, original_item_name: &str) -> Option<String> {
|
||||
// Special case for error type
|
||||
if original_item_name == "uc_err" {
|
||||
return Some(String::from("uc_error"));
|
||||
}
|
||||
|
||||
if original_item_name.contains("_cpu_") {
|
||||
return original_item_name
|
||||
.strip_prefix("uc_cpu_")
|
||||
.map(|suffix| format!("{}CpuModel", suffix.to_upper_camel_case()));
|
||||
}
|
||||
|
||||
if original_item_name.ends_with("_reg") {
|
||||
return original_item_name
|
||||
.strip_prefix("uc_")
|
||||
.and_then(|suffix| suffix.strip_suffix("_reg"))
|
||||
.map(|suffix| format!("Register{}", suffix.to_uppercase()));
|
||||
}
|
||||
|
||||
if original_item_name.ends_with("_insn") {
|
||||
return original_item_name
|
||||
.strip_prefix("uc_")
|
||||
.and_then(|suffix| suffix.strip_suffix("_insn"))
|
||||
.map(|suffix| format!("{}Insn", suffix.to_upper_camel_case()));
|
||||
}
|
||||
|
||||
if original_item_name.contains("_mode_") {
|
||||
return original_item_name
|
||||
.strip_prefix("uc_mode_")
|
||||
.map(|suffix| format!("{}Mode", suffix.to_upper_camel_case()));
|
||||
}
|
||||
|
||||
// Map various specific types to more idiomatic Rust names
|
||||
match original_item_name {
|
||||
"uc_query_type" => Some(String::from("Query")),
|
||||
"uc_tlb_type" => Some(String::from("TlbType")),
|
||||
"uc_mem_type" => Some(String::from("MemType")),
|
||||
"uc_tb" => Some(String::from("TranslationBlock")),
|
||||
"uc_arch" => Some(String::from("Arch")),
|
||||
"uc_mode" => Some(String::from("Mode")),
|
||||
"uc_mem_region" => Some(String::from("MemRegion")),
|
||||
"uc_prot" => Some(String::from("Prot")),
|
||||
"uc_hook_type" => Some(String::from("HookType")),
|
||||
"uc_tlb_entry" => Some(String::from("TlbEntry")),
|
||||
"uc_control_type" => Some(String::from("ControlType")),
|
||||
"uc_context_content" => Some(String::from("ContextMode")),
|
||||
"uc_tcg_op_code" => Some(String::from("TcgOpCode")),
|
||||
"uc_tcg_op_flag" => Some(String::from("TcgOpFlag")),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn enum_variant_name(
|
||||
&self,
|
||||
enum_name: Option<&str>,
|
||||
original_variant_name: &str,
|
||||
_variant_value: EnumVariantValue,
|
||||
) -> Option<String> {
|
||||
if let Some(enum_name) = enum_name {
|
||||
if enum_name.starts_with("enum uc_") {
|
||||
// Prefix to strip from enum variant names
|
||||
let prefix = match enum_name.strip_prefix("enum uc_").unwrap() {
|
||||
"query_type" => "UC_QUERY",
|
||||
"tlb_type" => "UC_TLB",
|
||||
"control_type" => "UC_CTL",
|
||||
"context_content" => "UC_CTL_CONTEXT",
|
||||
"err" => "UC_ERR",
|
||||
"mem_type" | "mem_region" => "UC_MEM",
|
||||
"arch" => "UC_ARCH",
|
||||
"mode" => "UC_MODE",
|
||||
"prot" => "UC_PROT",
|
||||
"hook_type" => "UC_HOOK",
|
||||
"x86_insn" => "UC_X86_INS",
|
||||
"tcg_op_code" => "UC_TCG_OP",
|
||||
"tcg_op_flag" => "UC_TCG_OP_FLAG",
|
||||
other => format!("UC_{}", other.to_uppercase()).leak(),
|
||||
}
|
||||
.to_string()
|
||||
+ "_";
|
||||
|
||||
// Strip prefix
|
||||
let mut fixed = original_variant_name
|
||||
.strip_prefix(&prefix)
|
||||
.map(str::to_uppercase);
|
||||
|
||||
// Special handling for numeric register names in PPC and MIPS
|
||||
if (enum_name == "enum uc_ppc_reg" || enum_name == "enum uc_mips_reg")
|
||||
&& fixed.as_ref().is_some_and(|s| s.parse::<u32>().is_ok())
|
||||
{
|
||||
fixed = fixed.map(|s| format!("R{s}"));
|
||||
}
|
||||
|
||||
// Special handling for CPU variants that start with a number
|
||||
if enum_name.contains("cpu")
|
||||
&& fixed
|
||||
.as_ref()
|
||||
.is_some_and(|s| s.chars().next().unwrap().is_ascii_digit())
|
||||
{
|
||||
fixed = fixed.map(|s| format!("Model_{s}"));
|
||||
}
|
||||
|
||||
// Special handling for mode values
|
||||
if enum_name == "enum uc_mode" {
|
||||
fixed = fixed.map(|s| match s.as_str() {
|
||||
"16" | "32" | "64" => format!("MODE_{s}"),
|
||||
_ => s,
|
||||
});
|
||||
}
|
||||
|
||||
return fixed;
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn generate_bindings() {
|
||||
const HEADER_PATH: &str = concat!(
|
||||
env!("CARGO_MANIFEST_DIR"),
|
||||
"/../../../include/unicorn/unicorn.h"
|
||||
);
|
||||
println!("cargo:rerun-if-changed={HEADER_PATH}");
|
||||
|
||||
let bitflag_enums = [
|
||||
"uc_hook_type",
|
||||
"uc_tcg_op_flag",
|
||||
"uc_prot",
|
||||
"uc_mode",
|
||||
"uc_context_content",
|
||||
"uc_control_type",
|
||||
];
|
||||
|
||||
let bindings = bindgen::Builder::default()
|
||||
.header(HEADER_PATH)
|
||||
.layout_tests(false)
|
||||
.allowlist_type("^uc.*")
|
||||
.allowlist_function("^uc_.*")
|
||||
.allowlist_var("^uc.*")
|
||||
.rustified_enum("^uc.*")
|
||||
.prepend_enum_name(false)
|
||||
.parse_callbacks(Box::new(Renamer))
|
||||
.bitfield_enum(bitflag_enums.join("|"))
|
||||
.derive_ord(true)
|
||||
.derive_eq(true)
|
||||
.use_core()
|
||||
.generate()
|
||||
.expect("Failed to generate bindings");
|
||||
|
||||
let bindings_rs = "src/bindings.rs";
|
||||
bindings
|
||||
.write_to_file(bindings_rs)
|
||||
.unwrap_or_else(|_| panic!("Failed to write bindings into path: {bindings_rs:?}"));
|
||||
}
|
||||
|
||||
fn main() {
|
||||
generate_bindings();
|
||||
|
||||
match pkg_config::Config::new()
|
||||
.atleast_version("2")
|
||||
.cargo_metadata(false)
|
||||
.probe("unicorn")
|
||||
{
|
||||
Ok(lib) => {
|
||||
for dir in lib.link_paths {
|
||||
println!("cargo:rustc-link-search=native={}", dir.to_str().unwrap());
|
||||
}
|
||||
if cfg!(feature = "dynamic_linkage") {
|
||||
if cc::Build::new().get_compiler().is_like_msvc() {
|
||||
println!("cargo:rustc-link-lib=dylib=unicorn-import");
|
||||
} else {
|
||||
println!("cargo:rustc-link-lib=dylib=unicorn");
|
||||
}
|
||||
} else {
|
||||
println!("cargo:rustc-link-arg=-Wl,-allow-multiple-definition");
|
||||
println!("cargo:rustc-link-lib=static=unicorn");
|
||||
println!("cargo:rustc-link-lib=pthread");
|
||||
println!("cargo:rustc-link-lib=m");
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
build_with_cmake();
|
||||
}
|
||||
}
|
||||
}
|
||||
342
bindings/rust/sys/src/lib.rs
Normal file
342
bindings/rust/sys/src/lib.rs
Normal file
@@ -0,0 +1,342 @@
|
||||
#![allow(non_camel_case_types)]
|
||||
|
||||
mod bindings;
|
||||
pub use bindings::*;
|
||||
|
||||
impl uc_error {
|
||||
/// Calls `op` if the result is Ok, otherwise returns the [`Err`] value of `self`.
|
||||
pub fn and_then<U, F: FnOnce() -> Result<U, Self>>(self, op: F) -> Result<U, Self> {
|
||||
if self == Self::OK { op() } else { Err(self) }
|
||||
}
|
||||
|
||||
/// Returns `res` if the result is Ok, otherwise returns the [`Err`] value of `self`.
|
||||
/// Arguments passed to this are eagerly evaluated; if you are passing the result
|
||||
/// of a function call, it is recommended to use [`uc_error::and_then`] instead, as it's lazily
|
||||
/// evaluated.
|
||||
pub fn and<U>(self, res: Result<U, Self>) -> Result<U, Self> {
|
||||
if self == Self::OK { res } else { Err(self) }
|
||||
}
|
||||
}
|
||||
|
||||
impl From<uc_error> for Result<(), uc_error> {
|
||||
fn from(value: uc_error) -> Self {
|
||||
if value == uc_error::OK {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<usize> for Arch {
|
||||
type Error = uc_error;
|
||||
|
||||
fn try_from(v: usize) -> Result<Self, Self::Error> {
|
||||
match v {
|
||||
x if x == Self::ARM as usize => Ok(Self::ARM),
|
||||
x if x == Self::ARM64 as usize => Ok(Self::ARM64),
|
||||
x if x == Self::MIPS as usize => Ok(Self::MIPS),
|
||||
x if x == Self::X86 as usize => Ok(Self::X86),
|
||||
x if x == Self::PPC as usize => Ok(Self::PPC),
|
||||
x if x == Self::SPARC as usize => Ok(Self::SPARC),
|
||||
x if x == Self::M68K as usize => Ok(Self::M68K),
|
||||
x if x == Self::RISCV as usize => Ok(Self::RISCV),
|
||||
x if x == Self::S390X as usize => Ok(Self::S390X),
|
||||
x if x == Self::TRICORE as usize => Ok(Self::TRICORE),
|
||||
x if x == Self::MAX as usize => Ok(Self::MAX),
|
||||
_ => Err(uc_error::ARCH),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<i32> for Mode {
|
||||
type Error = uc_error;
|
||||
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
fn try_from(v: i32) -> Result<Self, Self::Error> {
|
||||
match v {
|
||||
x if x == Self::LITTLE_ENDIAN.0 as i32 => Ok(Self::LITTLE_ENDIAN),
|
||||
x if x == Self::BIG_ENDIAN.0 as i32 => Ok(Self::BIG_ENDIAN),
|
||||
x if x == Self::ARM.0 as i32 => Ok(Self::ARM),
|
||||
x if x == Self::THUMB.0 as i32 => Ok(Self::THUMB),
|
||||
x if x == Self::MCLASS.0 as i32 => Ok(Self::MCLASS),
|
||||
x if x == Self::V8.0 as i32 => Ok(Self::V8),
|
||||
x if x == Self::ARMBE8.0 as i32 => Ok(Self::ARMBE8),
|
||||
x if x == Self::ARM926.0 as i32 => Ok(Self::ARM926),
|
||||
x if x == Self::ARM946.0 as i32 => Ok(Self::ARM946),
|
||||
x if x == Self::ARM1176.0 as i32 => Ok(Self::ARM1176),
|
||||
x if x == Self::MICRO.0 as i32 => Ok(Self::MICRO),
|
||||
x if x == Self::MIPS3.0 as i32 => Ok(Self::MIPS3),
|
||||
x if x == Self::MIPS32R6.0 as i32 => Ok(Self::MIPS32R6),
|
||||
x if x == Self::MIPS32.0 as i32 => Ok(Self::MIPS32),
|
||||
x if x == Self::MIPS64.0 as i32 => Ok(Self::MIPS64),
|
||||
x if x == Self::MODE_16.0 as i32 => Ok(Self::MODE_16),
|
||||
x if x == Self::MODE_32.0 as i32 => Ok(Self::MODE_32),
|
||||
x if x == Self::MODE_64.0 as i32 => Ok(Self::MODE_64),
|
||||
x if x == Self::PPC32.0 as i32 => Ok(Self::PPC32),
|
||||
x if x == Self::PPC64.0 as i32 => Ok(Self::PPC64),
|
||||
x if x == Self::QPX.0 as i32 => Ok(Self::QPX),
|
||||
x if x == Self::SPARC32.0 as i32 => Ok(Self::SPARC32),
|
||||
x if x == Self::SPARC64.0 as i32 => Ok(Self::SPARC64),
|
||||
x if x == Self::V9.0 as i32 => Ok(Self::V9),
|
||||
x if x == Self::RISCV32.0 as i32 => Ok(Self::RISCV32),
|
||||
x if x == Self::RISCV64.0 as i32 => Ok(Self::RISCV64),
|
||||
_ => Err(uc_error::MODE),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl HookType {
|
||||
pub const MEM_UNMAPPED: Self =
|
||||
Self(Self::MEM_READ_UNMAPPED.0 | Self::MEM_WRITE_UNMAPPED.0 | Self::MEM_FETCH_UNMAPPED.0);
|
||||
|
||||
pub const MEM_PROT: Self =
|
||||
Self(Self::MEM_READ_PROT.0 | Self::MEM_WRITE_PROT.0 | Self::MEM_FETCH_PROT.0);
|
||||
|
||||
pub const MEM_VALID: Self = Self(Self::MEM_READ.0 | Self::MEM_WRITE.0 | Self::MEM_FETCH.0);
|
||||
|
||||
pub const MEM_READ_INVALID: Self = Self(Self::MEM_READ_UNMAPPED.0 | Self::MEM_READ_PROT.0);
|
||||
|
||||
pub const MEM_WRITE_INVALID: Self = Self(Self::MEM_WRITE_UNMAPPED.0 | Self::MEM_WRITE_PROT.0);
|
||||
|
||||
pub const MEM_FETCH_INVALID: Self = Self(Self::MEM_FETCH_UNMAPPED.0 | Self::MEM_FETCH_PROT.0);
|
||||
|
||||
pub const MEM_INVALID: Self =
|
||||
Self(Self::MEM_READ_INVALID.0 | Self::MEM_WRITE_INVALID.0 | Self::MEM_FETCH_INVALID.0);
|
||||
|
||||
pub const MEM_ALL: Self = Self(Self::MEM_VALID.0 | Self::MEM_INVALID.0);
|
||||
}
|
||||
|
||||
impl ControlType {
|
||||
pub const IO_READ: Self = Self(1 << 31);
|
||||
|
||||
pub const IO_WRITE: Self = Self(1 << 30);
|
||||
}
|
||||
|
||||
impl From<M68kCpuModel> for i32 {
|
||||
fn from(value: M68kCpuModel) -> Self {
|
||||
value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&M68kCpuModel> for i32 {
|
||||
fn from(value: &M68kCpuModel) -> Self {
|
||||
*value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<X86CpuModel> for i32 {
|
||||
fn from(value: X86CpuModel) -> Self {
|
||||
value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&X86CpuModel> for i32 {
|
||||
fn from(value: &X86CpuModel) -> Self {
|
||||
*value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ArmCpuModel> for i32 {
|
||||
fn from(value: ArmCpuModel) -> Self {
|
||||
value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&ArmCpuModel> for i32 {
|
||||
fn from(value: &ArmCpuModel) -> Self {
|
||||
*value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Arm64CpuModel> for i32 {
|
||||
fn from(value: Arm64CpuModel) -> Self {
|
||||
value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Arm64CpuModel> for i32 {
|
||||
fn from(value: &Arm64CpuModel) -> Self {
|
||||
*value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Mips32CpuModel> for i32 {
|
||||
fn from(value: Mips32CpuModel) -> Self {
|
||||
value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Mips32CpuModel> for i32 {
|
||||
fn from(value: &Mips32CpuModel) -> Self {
|
||||
*value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Mips64CpuModel> for i32 {
|
||||
fn from(value: Mips64CpuModel) -> Self {
|
||||
value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Mips64CpuModel> for i32 {
|
||||
fn from(value: &Mips64CpuModel) -> Self {
|
||||
*value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Sparc32CpuModel> for i32 {
|
||||
fn from(value: Sparc32CpuModel) -> Self {
|
||||
value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Sparc32CpuModel> for i32 {
|
||||
fn from(value: &Sparc32CpuModel) -> Self {
|
||||
*value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Sparc64CpuModel> for i32 {
|
||||
fn from(value: Sparc64CpuModel) -> Self {
|
||||
value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Sparc64CpuModel> for i32 {
|
||||
fn from(value: &Sparc64CpuModel) -> Self {
|
||||
*value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PpcCpuModel> for i32 {
|
||||
fn from(value: PpcCpuModel) -> Self {
|
||||
value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&PpcCpuModel> for i32 {
|
||||
fn from(value: &PpcCpuModel) -> Self {
|
||||
*value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Ppc64CpuModel> for i32 {
|
||||
fn from(value: Ppc64CpuModel) -> Self {
|
||||
value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Ppc64CpuModel> for i32 {
|
||||
fn from(value: &Ppc64CpuModel) -> Self {
|
||||
*value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Riscv32CpuModel> for i32 {
|
||||
fn from(value: Riscv32CpuModel) -> Self {
|
||||
value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Riscv32CpuModel> for i32 {
|
||||
fn from(value: &Riscv32CpuModel) -> Self {
|
||||
*value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Riscv64CpuModel> for i32 {
|
||||
fn from(value: Riscv64CpuModel) -> Self {
|
||||
value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Riscv64CpuModel> for i32 {
|
||||
fn from(value: &Riscv64CpuModel) -> Self {
|
||||
*value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<S390xCpuModel> for i32 {
|
||||
fn from(value: S390xCpuModel) -> Self {
|
||||
value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&S390xCpuModel> for i32 {
|
||||
fn from(value: &S390xCpuModel) -> Self {
|
||||
*value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TricoreCpuModel> for i32 {
|
||||
fn from(value: TricoreCpuModel) -> Self {
|
||||
value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&TricoreCpuModel> for i32 {
|
||||
fn from(value: &TricoreCpuModel) -> Self {
|
||||
*value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RegisterM68K> for i32 {
|
||||
fn from(value: RegisterM68K) -> Self {
|
||||
value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RegisterX86> for i32 {
|
||||
fn from(value: RegisterX86) -> Self {
|
||||
value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RegisterARM> for i32 {
|
||||
fn from(value: RegisterARM) -> Self {
|
||||
value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RegisterARM64> for i32 {
|
||||
fn from(value: RegisterARM64) -> Self {
|
||||
value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RegisterMIPS> for i32 {
|
||||
fn from(value: RegisterMIPS) -> Self {
|
||||
value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RegisterSPARC> for i32 {
|
||||
fn from(value: RegisterSPARC) -> Self {
|
||||
value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RegisterPPC> for i32 {
|
||||
fn from(value: RegisterPPC) -> Self {
|
||||
value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RegisterRISCV> for i32 {
|
||||
fn from(value: RegisterRISCV) -> Self {
|
||||
value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RegisterS390X> for i32 {
|
||||
fn from(value: RegisterS390X) -> Self {
|
||||
value as Self
|
||||
}
|
||||
}
|
||||
|
||||
impl From<RegisterTRICORE> for i32 {
|
||||
fn from(value: RegisterTRICORE) -> Self {
|
||||
value as Self
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user