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:
mio
2025-04-13 00:17:55 +08:00
parent b999f507b9
commit bd5a8c5146
41 changed files with 5033 additions and 4642 deletions

View File

@@ -25,59 +25,41 @@ jobs:
fail-fast: false fail-fast: false
matrix: matrix:
config: config:
- { - { os: windows-2022, arch: x64, name: "Windows x86_64" }
os: windows-2022, - { os: windows-2022, arch: x86, name: "Windows x86" }
arch: x64, - { os: ubuntu-latest, arch: x64, name: "Ubuntu x86_64" }
name: 'Windows x86_64' - { os: macos-latest, arch: x64, name: "macOS x86_64" }
}
- {
os: windows-2022,
arch: x86,
name: 'Windows x86'
}
- {
os: ubuntu-latest,
arch: x64,
name: 'Ubuntu x86_64'
}
- {
os: macos-latest,
arch: x64,
name: 'macOS x86_64'
}
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: '🛠️ Set up Rust' - name: Set up Rust
uses: dtolnay/rust-toolchain@stable uses: actions-rust-lang/setup-rust-toolchain@v1
- name: '🛠️ Activate Developer Command Prompt' - name: "🛠️ Activate Developer Command Prompt"
if: contains(matrix.config.os, 'win') if: contains(matrix.config.os, 'win')
uses: ilammy/msvc-dev-cmd@v1 uses: ilammy/msvc-dev-cmd@v1
with: with:
arch: ${{ matrix.config.arch }} arch: ${{ matrix.config.arch }}
- name: '🛠️ Win build dependencies' - name: "🛠️ Win build dependencies"
if: contains(matrix.config.os, 'win') if: contains(matrix.config.os, 'win')
shell: bash shell: bash
run: | run: |
choco install ninja choco install ninja
- name: '🛠️ macOS build dependencies' - name: "🛠️ macOS build dependencies"
if: contains(matrix.config.os, 'macOS') if: contains(matrix.config.os, 'macOS')
shell: bash shell: bash
run: | run: |
brew install ninja brew install ninja
- name: '🚧 Cargo test' - name: "🚧 Cargo test"
if: "!startsWith(github.ref, 'refs/tags')" if: "!startsWith(github.ref, 'refs/tags')"
run: | run: |
cargo test cargo test
- name: '📦 Cargo Publish' - name: Publish crates to Crates.io
if: startsWith(github.ref, 'refs/tags') && !startsWith(github.ref, 'refs/tags/v') && contains(matrix.config.os, 'ubuntu') if: startsWith(github.ref, 'refs/tags') && !startsWith(github.ref, 'refs/tags/v') && contains(matrix.config.os, 'ubuntu')
env: uses: katyo/publish-crates@v2
TOKEN: ${{ secrets.cratesio_token }} with:
UNICORN_VERSION: dev registry-token: ${{ secrets.cratesio_token }}
run: |
cargo login $TOKEN && cargo test && cargo publish

1
.gitignore vendored
View File

@@ -91,6 +91,7 @@ cmocka/
zig-cache/ zig-cache/
zig-out/ zig-out/
.cache .cache
bindings/rust/sys/src/bindings.rs
################## ##################
## PyCharm Project ## PyCharm Project

View File

@@ -1,57 +1,31 @@
[package] [workspace]
name = "unicorn-engine" default-members = ["bindings/rust"]
members = ["bindings/rust", "bindings/rust/sys"]
resolver = "2"
[workspace.package]
rust-version = "1.85.0"
version = "2.1.3" version = "2.1.3"
authors = ["Ziqiao Kong", "Lukas Seidel"] authors = ["Ziqiao Kong", "Lukas Seidel", "Amaan Qureshi <amaanq12@gmail.com>"]
keywords = ["unicorn", "cpu", "emulator", "bindings"]
categories = ["api-bindings", "emulators", "no-std", "virtualization"]
documentation = "https://github.com/unicorn-engine/unicorn/wiki" documentation = "https://github.com/unicorn-engine/unicorn/wiki"
edition = "2021" edition = "2024"
license = "GPL-2.0" license = "GPL-2.0"
readme = "README.md" readme = "README.md"
repository = "https://github.com/unicorn-engine/unicorn" repository = "https://github.com/unicorn-engine/unicorn"
description = "Rust bindings for the Unicorn emulator with utility functions" description = "Rust bindings for the Unicorn emulator with utility functions"
build = "bindings/rust/build.rs"
links = "unicorn"
# use `cargo publish --list` to see files to be included
# the resulting list what cargo uses to check for out-of-date files during build
exclude = [
"/docs",
"/bindings/dotnet",
"/bindings/go",
"/bindings/haskell",
"/bindings/java",
"/bindings/pascal",
"/bindings/python",
"/bindings/ruby",
"/bindings/vb6",
"/bindings/zig",
"/samples",
"/tests",
]
[lib] [workspace.lints.clippy]
path = "bindings/rust/src/lib.rs" cast_lossless = "allow"
cast_possible_truncation = "allow"
[dependencies] cast_possible_wrap = "allow"
bitflags = "2.3.3" cast_sign_loss = "allow"
libc = "0.2" missing_errors_doc = "allow"
missing_panics_doc = "allow"
[build-dependencies] similar_names = "allow"
cc = { version = "1.0" } unreadable_literal = "allow"
cmake = { version = "0.1" } use_self = "allow"
pkg-config = { version = "0.3" } pedantic = { level = "warn", priority = -1 }
nursery = { level = "warn", priority = -1 }
[features] cargo = { level = "warn", priority = -1 }
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 = []
# NOTE: unicorn-c only separates on top-level arch name,
# not on the bit-length, so we include both arm and aarch64
arch_aarch64 = ["arch_arm"]
arch_riscv = []
arch_mips = []
arch_sparc = []
arch_m68k = []
arch_ppc = []
arch_s390x = []
arch_tricore = []

53
bindings/rust/Cargo.toml Normal file
View File

@@ -0,0 +1,53 @@
[package]
name = "unicorn-engine"
version.workspace = true
authors.workspace = true
keywords.workspace = true
categories.workspace = true
documentation.workspace = true
edition.workspace = true
license.workspace = true
readme = "README.md"
repository.workspace = true
description.workspace = true
[lints]
workspace = true
[dependencies]
unicorn-engine-sys = { version = "2.1.3", path = "sys", features = [], default-features = false }
[build-dependencies]
bindgen = "0.71.1"
cc = { version = "1.2.17" }
cmake = { version = "0.1.54" }
pkg-config = { version = "0.3.32" }
[dev-dependencies]
page_size = "0.6.0"
[features]
default = ["arch_all"]
dynamic_linkage = ["unicorn-engine-sys/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 = ["unicorn-engine-sys/arch_x86"]
arch_arm = ["unicorn-engine-sys/arch_arm"]
arch_aarch64 = ["arch_arm", "unicorn-engine-sys/arch_aarch64"]
arch_riscv = ["unicorn-engine-sys/arch_riscv"]
arch_mips = ["unicorn-engine-sys/arch_mips"]
arch_sparc = ["unicorn-engine-sys/arch_sparc"]
arch_m68k = ["unicorn-engine-sys/arch_m68k"]
arch_ppc = ["unicorn-engine-sys/arch_ppc"]
arch_s390x = ["unicorn-engine-sys/arch_s390x"]
arch_tricore = ["unicorn-engine-sys/arch_tricore"]

View File

@@ -5,26 +5,25 @@ Rust bindings for the [Unicorn](http://www.unicorn-engine.org/) emulator with ut
Checkout Unicorn2 source code at [dev branch](https://github.com/unicorn-engine/unicorn/tree/dev). Checkout Unicorn2 source code at [dev branch](https://github.com/unicorn-engine/unicorn/tree/dev).
```rust ```rust
use unicorn_engine::{Unicorn, RegisterARM}; use unicorn_engine::{Arch, Mode, Prot, SECOND_SCALE, Unicorn, RegisterARM};
use unicorn_engine::unicorn_const::{Arch, Mode, Permission, SECOND_SCALE};
fn main() { fn main() {
let arm_code32: Vec<u8> = vec![0x17, 0x00, 0x40, 0xe2]; // sub r0, #23 let arm_code32: Vec<u8> = vec![0x17, 0x00, 0x40, 0xe2]; // sub r0, #23
let mut unicorn = Unicorn::new(Arch::ARM, Mode::LITTLE_ENDIAN).expect("failed to initialize Unicorn instance"); let mut emu = Unicorn::new(Arch::ARM, Mode::LITTLE_ENDIAN).expect("failed to initialize Unicorn instance");
let emu = &mut unicorn; emu.mem_map(0x1000, 0x4000, Prot::ALL).expect("failed to map code page");
emu.mem_map(0x1000, 0x4000, Permission::ALL).expect("failed to map code page");
emu.mem_write(0x1000, &arm_code32).expect("failed to write instructions"); emu.mem_write(0x1000, &arm_code32).expect("failed to write instructions");
emu.reg_write(RegisterARM::R0, 123).expect("failed write R0"); emu.reg_write(RegisterARM::R0, 123).expect("failed write R0");
emu.reg_write(RegisterARM::R5, 1337).expect("failed write R5"); emu.reg_write(RegisterARM::R5, 1337).expect("failed write R5");
let _ = emu.emu_start(0x1000, (0x1000 + arm_code32.len()) as u64, 10 * SECOND_SCALE, 1000); emu.emu_start(0x1000, (0x1000 + arm_code32.len()) as u64, 10 * SECOND_SCALE, 1000).expect("failed to start emulation");
assert_eq!(emu.reg_read(RegisterARM::R0), Ok(100)); assert_eq!(emu.reg_read(RegisterARM::R0).unwrap(), 100);
assert_eq!(emu.reg_read(RegisterARM::R5), Ok(1337)); assert_eq!(emu.reg_read(RegisterARM::R5).unwrap(), 1337);
} }
``` ```
Further sample code can be found in [tests](../../tests/rust-tests/main.rs).
Further sample code can be found in [tests](./src/tests).
## Usage ## Usage
@@ -37,7 +36,6 @@ unicorn-engine = "2.1.1"
## Acknowledgements ## Acknowledgements
These bindings are based on Sébastien Duquette's (@ekse) [unicorn-rs](https://github.com/unicorn-rs/unicorn-rs). These bindings were once based on Sébastien Duquette's (@ekse) [unicorn-rs](https://github.com/unicorn-rs/unicorn-rs).
We picked up the project, as it is no longer maintained. We picked up the project, as it is no longer maintained.
Thanks to all contributors. Thanks to all contributors.

View File

@@ -1,182 +0,0 @@
use pkg_config;
use std::env;
use std::path::PathBuf;
use std::process::Command;
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 setup_env_msvc(compiler: &cc::Tool) {
// If PATH already contains what we need, skip this
if msvc_cmake_tools_available() {
return;
}
let target = env::var("TARGET").unwrap();
let devenv = cc::windows_registry::find_tool(target.as_str(), "devenv");
let tool_root: PathBuf = match devenv {
Some(devenv_tool) => devenv_tool.path().parent().unwrap().to_path_buf(),
None => {
// if devenv (i.e. Visual Studio) was not found, assume compiler is
// from standalone Build Tools and look there instead.
// this should be done properly in cc crate, but for now it's not.
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")
}
};
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");
if !cmake_path.is_file() {
panic!("missing cmake");
}
if !ninja_path.is_file() {
panic!("missing ninja");
}
// append cmake and ninja location to PATH
if let Some(path) = env::var_os("PATH") {
let mut paths = env::split_paths(&path).collect::<Vec<_>>();
for tool_path in [cmake_path, ninja_path] {
paths.push(tool_path.parent().unwrap().to_path_buf());
}
let new_path = env::join_paths(paths).unwrap();
env::set_var("PATH", &new_path);
}
}
fn build_with_cmake() {
let uc_dir = PathBuf::from(env::var("CARGO_MANIFEST_DIR").unwrap());
let compiler = cc::Build::new().get_compiler();
let has_ninja = if compiler.is_like_msvc() {
setup_env_msvc(&compiler);
// this is a BIG HACK that should be fixed in unicorn's cmake!!
// but for now, tell link.exe to ignore multiply defined symbol names
println!("cargo:rustc-link-arg=/FORCE:MULTIPLE");
true
} else {
ninja_available()
};
// cc crate (as of 1.0.73) misdetects clang as gnu on apple
if compiler.is_like_gnu() && env::consts::OS != "macos" {
// see comment on /FORCE:MULTIPLE
println!("cargo:rustc-link-arg=-Wl,-allow-multiple-definition");
}
let mut config = cmake::Config::new(&uc_dir);
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();
}
// 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");
}
}
fn main() {
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();
}
};
}

View File

@@ -1,224 +0,0 @@
#![allow(non_camel_case_types)]
// For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
#[repr(C)]
#[derive(PartialEq, Debug, Clone, Copy)]
pub enum RegisterARM {
// ARM registers
INVALID = 0,
APSR = 1,
APSR_NZCV = 2,
CPSR = 3,
FPEXC = 4,
FPINST = 5,
FPSCR = 6,
FPSCR_NZCV = 7,
FPSID = 8,
ITSTATE = 9,
LR = 10,
PC = 11,
SP = 12,
SPSR = 13,
D0 = 14,
D1 = 15,
D2 = 16,
D3 = 17,
D4 = 18,
D5 = 19,
D6 = 20,
D7 = 21,
D8 = 22,
D9 = 23,
D10 = 24,
D11 = 25,
D12 = 26,
D13 = 27,
D14 = 28,
D15 = 29,
D16 = 30,
D17 = 31,
D18 = 32,
D19 = 33,
D20 = 34,
D21 = 35,
D22 = 36,
D23 = 37,
D24 = 38,
D25 = 39,
D26 = 40,
D27 = 41,
D28 = 42,
D29 = 43,
D30 = 44,
D31 = 45,
FPINST2 = 46,
MVFR0 = 47,
MVFR1 = 48,
MVFR2 = 49,
Q0 = 50,
Q1 = 51,
Q2 = 52,
Q3 = 53,
Q4 = 54,
Q5 = 55,
Q6 = 56,
Q7 = 57,
Q8 = 58,
Q9 = 59,
Q10 = 60,
Q11 = 61,
Q12 = 62,
Q13 = 63,
Q14 = 64,
Q15 = 65,
R0 = 66,
R1 = 67,
R2 = 68,
R3 = 69,
R4 = 70,
R5 = 71,
R6 = 72,
R7 = 73,
R8 = 74,
R9 = 75,
R10 = 76,
R11 = 77,
R12 = 78,
S0 = 79,
S1 = 80,
S2 = 81,
S3 = 82,
S4 = 83,
S5 = 84,
S6 = 85,
S7 = 86,
S8 = 87,
S9 = 88,
S10 = 89,
S11 = 90,
S12 = 91,
S13 = 92,
S14 = 93,
S15 = 94,
S16 = 95,
S17 = 96,
S18 = 97,
S19 = 98,
S20 = 99,
S21 = 100,
S22 = 101,
S23 = 102,
S24 = 103,
S25 = 104,
S26 = 105,
S27 = 106,
S28 = 107,
S29 = 108,
S30 = 109,
S31 = 110,
C1_C0_2 = 111,
C13_C0_2 = 112,
C13_C0_3 = 113,
IPSR = 114,
MSP = 115,
PSP = 116,
CONTROL = 117,
IAPSR = 118,
EAPSR = 119,
XPSR = 120,
EPSR = 121,
IEPSR = 122,
PRIMASK = 123,
BASEPRI = 124,
BASEPRI_MAX = 125,
FAULTMASK = 126,
APSR_NZCVQ = 127,
APSR_G = 128,
APSR_NZCVQG = 129,
IAPSR_NZCVQ = 130,
IAPSR_G = 131,
IAPSR_NZCVQG = 132,
EAPSR_NZCVQ = 133,
EAPSR_G = 134,
EAPSR_NZCVQG = 135,
XPSR_NZCVQ = 136,
XPSR_G = 137,
XPSR_NZCVQG = 138,
CP_REG = 139,
ESR = 140,
ENDING = 141,
}
impl RegisterARM {
// alias registers
// (assoc) R13 = 12,
// (assoc) R14 = 10,
// (assoc) R15 = 11,
// (assoc) SB = 75,
// (assoc) SL = 76,
// (assoc) FP = 77,
// (assoc) IP = 78,
pub const R13: RegisterARM = RegisterARM::SP;
pub const R14: RegisterARM = RegisterARM::LR;
pub const R15: RegisterARM = RegisterARM::PC;
pub const SB: RegisterARM = RegisterARM::R9;
pub const SL: RegisterARM = RegisterARM::R10;
pub const FP: RegisterARM = RegisterARM::R11;
pub const IP: RegisterARM = RegisterARM::R12;
}
impl From<RegisterARM> for i32 {
fn from(r: RegisterARM) -> Self {
r as i32
}
}
#[repr(i32)]
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
pub enum ArmCpuModel {
UC_CPU_ARM_926 = 0,
UC_CPU_ARM_946 = 1,
UC_CPU_ARM_1026 = 2,
UC_CPU_ARM_1136_R2 = 3,
UC_CPU_ARM_1136 = 4,
UC_CPU_ARM_1176 = 5,
UC_CPU_ARM_11MPCORE = 6,
UC_CPU_ARM_CORTEX_M0 = 7,
UC_CPU_ARM_CORTEX_M3 = 8,
UC_CPU_ARM_CORTEX_M4 = 9,
UC_CPU_ARM_CORTEX_M7 = 10,
UC_CPU_ARM_CORTEX_M33 = 11,
UC_CPU_ARM_CORTEX_R5 = 12,
UC_CPU_ARM_CORTEX_R5F = 13,
UC_CPU_ARM_CORTEX_A7 = 14,
UC_CPU_ARM_CORTEX_A8 = 15,
UC_CPU_ARM_CORTEX_A9 = 16,
UC_CPU_ARM_CORTEX_A15 = 17,
UC_CPU_ARM_TI925T = 18,
UC_CPU_ARM_SA1100 = 19,
UC_CPU_ARM_SA1110 = 20,
UC_CPU_ARM_PXA250 = 21,
UC_CPU_ARM_PXA255 = 22,
UC_CPU_ARM_PXA260 = 23,
UC_CPU_ARM_PXA261 = 24,
UC_CPU_ARM_PXA262 = 25,
UC_CPU_ARM_PXA270 = 26,
UC_CPU_ARM_PXA270A0 = 27,
UC_CPU_ARM_PXA270A1 = 28,
UC_CPU_ARM_PXA270B0 = 29,
UC_CPU_ARM_PXA270B1 = 30,
UC_CPU_ARM_PXA270C0 = 31,
UC_CPU_ARM_PXA270C5 = 32,
}
impl From<ArmCpuModel> for i32 {
fn from(value: ArmCpuModel) -> Self {
value as i32
}
}
impl From<&ArmCpuModel> for i32 {
fn from(value: &ArmCpuModel) -> Self {
*value as i32
}
}

View File

@@ -1,348 +0,0 @@
#![allow(non_camel_case_types)]
// ARM64 registers
#[repr(C)]
#[derive(PartialEq, Debug, Clone, Copy)]
pub enum RegisterARM64 {
INVALID = 0,
X29 = 1,
X30 = 2,
NZCV = 3,
SP = 4,
WSP = 5,
WZR = 6,
XZR = 7,
B0 = 8,
B1 = 9,
B2 = 10,
B3 = 11,
B4 = 12,
B5 = 13,
B6 = 14,
B7 = 15,
B8 = 16,
B9 = 17,
B10 = 18,
B11 = 19,
B12 = 20,
B13 = 21,
B14 = 22,
B15 = 23,
B16 = 24,
B17 = 25,
B18 = 26,
B19 = 27,
B20 = 28,
B21 = 29,
B22 = 30,
B23 = 31,
B24 = 32,
B25 = 33,
B26 = 34,
B27 = 35,
B28 = 36,
B29 = 37,
B30 = 38,
B31 = 39,
D0 = 40,
D1 = 41,
D2 = 42,
D3 = 43,
D4 = 44,
D5 = 45,
D6 = 46,
D7 = 47,
D8 = 48,
D9 = 49,
D10 = 50,
D11 = 51,
D12 = 52,
D13 = 53,
D14 = 54,
D15 = 55,
D16 = 56,
D17 = 57,
D18 = 58,
D19 = 59,
D20 = 60,
D21 = 61,
D22 = 62,
D23 = 63,
D24 = 64,
D25 = 65,
D26 = 66,
D27 = 67,
D28 = 68,
D29 = 69,
D30 = 70,
D31 = 71,
H0 = 72,
H1 = 73,
H2 = 74,
H3 = 75,
H4 = 76,
H5 = 77,
H6 = 78,
H7 = 79,
H8 = 80,
H9 = 81,
H10 = 82,
H11 = 83,
H12 = 84,
H13 = 85,
H14 = 86,
H15 = 87,
H16 = 88,
H17 = 89,
H18 = 90,
H19 = 91,
H20 = 92,
H21 = 93,
H22 = 94,
H23 = 95,
H24 = 96,
H25 = 97,
H26 = 98,
H27 = 99,
H28 = 100,
H29 = 101,
H30 = 102,
H31 = 103,
Q0 = 104,
Q1 = 105,
Q2 = 106,
Q3 = 107,
Q4 = 108,
Q5 = 109,
Q6 = 110,
Q7 = 111,
Q8 = 112,
Q9 = 113,
Q10 = 114,
Q11 = 115,
Q12 = 116,
Q13 = 117,
Q14 = 118,
Q15 = 119,
Q16 = 120,
Q17 = 121,
Q18 = 122,
Q19 = 123,
Q20 = 124,
Q21 = 125,
Q22 = 126,
Q23 = 127,
Q24 = 128,
Q25 = 129,
Q26 = 130,
Q27 = 131,
Q28 = 132,
Q29 = 133,
Q30 = 134,
Q31 = 135,
S0 = 136,
S1 = 137,
S2 = 138,
S3 = 139,
S4 = 140,
S5 = 141,
S6 = 142,
S7 = 143,
S8 = 144,
S9 = 145,
S10 = 146,
S11 = 147,
S12 = 148,
S13 = 149,
S14 = 150,
S15 = 151,
S16 = 152,
S17 = 153,
S18 = 154,
S19 = 155,
S20 = 156,
S21 = 157,
S22 = 158,
S23 = 159,
S24 = 160,
S25 = 161,
S26 = 162,
S27 = 163,
S28 = 164,
S29 = 165,
S30 = 166,
S31 = 167,
W0 = 168,
W1 = 169,
W2 = 170,
W3 = 171,
W4 = 172,
W5 = 173,
W6 = 174,
W7 = 175,
W8 = 176,
W9 = 177,
W10 = 178,
W11 = 179,
W12 = 180,
W13 = 181,
W14 = 182,
W15 = 183,
W16 = 184,
W17 = 185,
W18 = 186,
W19 = 187,
W20 = 188,
W21 = 189,
W22 = 190,
W23 = 191,
W24 = 192,
W25 = 193,
W26 = 194,
W27 = 195,
W28 = 196,
W29 = 197,
W30 = 198,
X0 = 199,
X1 = 200,
X2 = 201,
X3 = 202,
X4 = 203,
X5 = 204,
X6 = 205,
X7 = 206,
X8 = 207,
X9 = 208,
X10 = 209,
X11 = 210,
X12 = 211,
X13 = 212,
X14 = 213,
X15 = 214,
X16 = 215,
X17 = 216,
X18 = 217,
X19 = 218,
X20 = 219,
X21 = 220,
X22 = 221,
X23 = 222,
X24 = 223,
X25 = 224,
X26 = 225,
X27 = 226,
X28 = 227,
V0 = 228,
V1 = 229,
V2 = 230,
V3 = 231,
V4 = 232,
V5 = 233,
V6 = 234,
V7 = 235,
V8 = 236,
V9 = 237,
V10 = 238,
V11 = 239,
V12 = 240,
V13 = 241,
V14 = 242,
V15 = 243,
V16 = 244,
V17 = 245,
V18 = 246,
V19 = 247,
V20 = 248,
V21 = 249,
V22 = 250,
V23 = 251,
V24 = 252,
V25 = 253,
V26 = 254,
V27 = 255,
V28 = 256,
V29 = 257,
V30 = 258,
V31 = 259,
// pseudo registers
PC = 260,
CPACR_EL1 = 261,
// thread registers, depreciated, use CP_REG instead
TPIDR_EL0 = 262,
TPIDRRO_EL0 = 263,
TPIDR_EL1 = 264,
PSTATE = 265,
// exception link registers, depreciated, use CP_REG instead
ELR_EL0 = 266,
ELR_EL1 = 267,
ELR_EL2 = 268,
ELR_EL3 = 269,
// stack pointers registers, depreciated, use CP_REG instead
SP_EL0 = 270,
SP_EL1 = 271,
SP_EL2 = 272,
SP_EL3 = 273,
// other CP15 registers, depreciated, use CP_REG instead
TTBR0_EL1 = 274,
TTBR1_EL1 = 275,
ESR_EL0 = 276,
ESR_EL1 = 277,
ESR_EL2 = 278,
ESR_EL3 = 279,
FAR_EL0 = 280,
FAR_EL1 = 281,
FAR_EL2 = 282,
FAR_EL3 = 283,
PAR_EL1 = 284,
MAIR_EL1 = 285,
VBAR_EL0 = 286,
VBAR_EL1 = 287,
VBAR_EL2 = 288,
VBAR_EL3 = 289,
CP_REG = 290,
ENDING = 291,
}
impl RegisterARM64 {
// alias registers
// (assoc) IP0 = 215,
// (assoc) IP1 = 216,
// (assoc) FP = 1,
// (assoc) LR = 2,
pub const IP0: RegisterARM64 = RegisterARM64::X16;
pub const IP1: RegisterARM64 = RegisterARM64::X17;
pub const FP: RegisterARM64 = RegisterARM64::X29;
pub const LR: RegisterARM64 = RegisterARM64::X30;
}
impl From<RegisterARM64> for i32 {
fn from(r: RegisterARM64) -> Self {
r as i32
}
}
#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Arm64CpuModel {
UC_CPU_ARM64_A57 = 0,
UC_CPU_ARM64_A53 = 1,
UC_CPU_ARM64_A72 = 2,
UC_CPU_ARM64_MAX = 3,
}
impl From<Arm64CpuModel> for i32 {
fn from(value: Arm64CpuModel) -> Self {
value as i32
}
}
impl From<&Arm64CpuModel> for i32 {
fn from(value: &Arm64CpuModel) -> Self {
(*value) as i32
}
}

View File

@@ -1,297 +0,0 @@
#![allow(non_camel_case_types)]
#![allow(dead_code)]
use crate::{Unicorn, UnicornInner};
use super::unicorn_const::{uc_error, Arch, HookType, MemRegion, MemType, Mode, Query, TlbEntry};
use alloc::rc::Weak;
use core::cell::UnsafeCell;
use core::ffi::c_void;
use libc::{c_char, c_int};
pub type uc_handle = *mut c_void;
// TODO: Use c_size_t as soon as it is stable. The c api exposes uc_hook as size_t
pub type uc_hook = *mut c_void;
pub type uc_context = *mut c_void;
extern "C" {
pub fn uc_version(major: *mut u32, minor: *mut u32) -> u32;
pub fn uc_arch_supported(arch: Arch) -> bool;
pub fn uc_open(arch: Arch, mode: Mode, engine: *mut uc_handle) -> uc_error;
pub fn uc_close(engine: uc_handle) -> uc_error;
pub fn uc_context_free(mem: uc_context) -> uc_error;
pub fn uc_errno(engine: uc_handle) -> uc_error;
pub fn uc_strerror(error_code: uc_error) -> *const c_char;
pub fn uc_reg_write(engine: uc_handle, regid: c_int, value: *const c_void) -> uc_error;
pub fn uc_reg_write_batch(
engine: uc_handle,
regids: *const c_int,
values: *const *const c_void,
count: c_int,
) -> uc_error;
pub fn uc_reg_read(engine: uc_handle, regid: c_int, value: *mut c_void) -> uc_error;
pub fn uc_reg_read_batch(
engine: uc_handle,
regids: *const c_int,
values: *const *mut c_void,
count: c_int,
) -> uc_error;
pub fn uc_mem_write(
engine: uc_handle,
address: u64,
bytes: *const u8,
size: libc::size_t,
) -> uc_error;
pub fn uc_mem_read(
engine: uc_handle,
address: u64,
bytes: *mut u8,
size: libc::size_t,
) -> uc_error;
pub fn uc_mem_map(engine: uc_handle, address: u64, size: libc::size_t, perms: u32) -> uc_error;
pub fn uc_mem_map_ptr(
engine: uc_handle,
address: u64,
size: libc::size_t,
perms: u32,
ptr: *mut c_void,
) -> uc_error;
pub fn uc_mmio_map(
engine: uc_handle,
address: u64,
size: libc::size_t,
read_cb: *mut c_void,
user_data_read: *mut c_void,
write_cb: *mut c_void,
user_data_write: *mut c_void,
) -> uc_error;
pub fn uc_mem_unmap(engine: uc_handle, address: u64, size: libc::size_t) -> uc_error;
pub fn uc_mem_protect(
engine: uc_handle,
address: u64,
size: libc::size_t,
perms: u32,
) -> uc_error;
pub fn uc_mem_regions(
engine: uc_handle,
regions: *const *const MemRegion,
count: *mut u32,
) -> uc_error;
pub fn uc_emu_start(
engine: uc_handle,
begin: u64,
until: u64,
timeout: u64,
count: libc::size_t,
) -> uc_error;
pub fn uc_emu_stop(engine: uc_handle) -> uc_error;
pub fn uc_hook_add(
engine: uc_handle,
hook: *mut uc_hook,
hook_type: HookType,
callback: *mut c_void,
user_data: *mut c_void,
begin: u64,
end: u64,
...
) -> uc_error;
pub fn uc_hook_del(engine: uc_handle, hook: uc_hook) -> uc_error;
pub fn uc_query(engine: uc_handle, query_type: Query, result: *mut libc::size_t) -> uc_error;
pub fn uc_context_alloc(engine: uc_handle, context: *mut uc_context) -> uc_error;
pub fn uc_context_save(engine: uc_handle, context: uc_context) -> uc_error;
pub fn uc_context_restore(engine: uc_handle, context: uc_context) -> uc_error;
pub fn uc_ctl(engine: uc_handle, control: u32, ...) -> uc_error;
}
pub struct UcHook<'a, D: 'a, F: 'a> {
pub callback: F,
pub uc: Weak<UnsafeCell<UnicornInner<'a, D>>>,
}
pub trait IsUcHook<'a> {}
impl<'a, D, F> IsUcHook<'a> for UcHook<'a, D, F> {}
pub unsafe extern "C" fn mmio_read_callback_proxy<D, F>(
uc: uc_handle,
offset: u64,
size: usize,
user_data: *mut UcHook<D, F>,
) -> u64
where
F: FnMut(&mut crate::Unicorn<D>, u64, usize) -> u64,
{
let user_data = &mut *user_data;
let mut user_data_uc = Unicorn {
inner: user_data.uc.upgrade().unwrap(),
};
debug_assert_eq!(uc, user_data_uc.get_handle());
(user_data.callback)(&mut user_data_uc, offset, size)
}
pub unsafe extern "C" fn mmio_write_callback_proxy<D, F>(
uc: uc_handle,
offset: u64,
size: usize,
value: u64,
user_data: *mut UcHook<D, F>,
) where
F: FnMut(&mut crate::Unicorn<D>, u64, usize, u64),
{
let user_data = &mut *user_data;
let mut user_data_uc = Unicorn {
inner: user_data.uc.upgrade().unwrap(),
};
debug_assert_eq!(uc, user_data_uc.get_handle());
(user_data.callback)(&mut user_data_uc, offset, size, value);
}
pub unsafe extern "C" fn code_hook_proxy<D, F>(
uc: uc_handle,
address: u64,
size: u32,
user_data: *mut UcHook<D, F>,
) where
F: FnMut(&mut crate::Unicorn<D>, u64, u32),
{
let user_data = &mut *user_data;
let mut user_data_uc = Unicorn {
inner: user_data.uc.upgrade().unwrap(),
};
debug_assert_eq!(uc, user_data_uc.get_handle());
(user_data.callback)(&mut user_data_uc, address, size);
}
pub unsafe extern "C" fn block_hook_proxy<D, F>(
uc: uc_handle,
address: u64,
size: u32,
user_data: *mut UcHook<D, F>,
) where
F: FnMut(&mut crate::Unicorn<D>, u64, u32),
{
let user_data = &mut *user_data;
let mut user_data_uc = Unicorn {
inner: user_data.uc.upgrade().unwrap(),
};
debug_assert_eq!(uc, user_data_uc.get_handle());
(user_data.callback)(&mut user_data_uc, address, size);
}
pub unsafe extern "C" fn mem_hook_proxy<D, F>(
uc: uc_handle,
mem_type: MemType,
address: u64,
size: u32,
value: i64,
user_data: *mut UcHook<D, F>,
) -> bool
where
F: FnMut(&mut crate::Unicorn<D>, MemType, u64, usize, i64) -> bool,
{
let user_data = &mut *user_data;
let mut user_data_uc = Unicorn {
inner: user_data.uc.upgrade().unwrap(),
};
debug_assert_eq!(uc, user_data_uc.get_handle());
(user_data.callback)(&mut user_data_uc, mem_type, address, size as usize, value)
}
pub unsafe extern "C" fn intr_hook_proxy<D, F>(
uc: uc_handle,
value: u32,
user_data: *mut UcHook<D, F>,
) where
F: FnMut(&mut crate::Unicorn<D>, u32),
{
let user_data = &mut *user_data;
let mut user_data_uc = Unicorn {
inner: user_data.uc.upgrade().unwrap(),
};
debug_assert_eq!(uc, user_data_uc.get_handle());
(user_data.callback)(&mut user_data_uc, value);
}
pub unsafe extern "C" fn insn_in_hook_proxy<D, F>(
uc: uc_handle,
port: u32,
size: usize,
user_data: *mut UcHook<D, F>,
) -> u32
where
F: FnMut(&mut crate::Unicorn<D>, u32, usize) -> u32,
{
let user_data = &mut *user_data;
let mut user_data_uc = Unicorn {
inner: user_data.uc.upgrade().unwrap(),
};
debug_assert_eq!(uc, user_data_uc.get_handle());
(user_data.callback)(&mut user_data_uc, port, size)
}
pub unsafe extern "C" fn insn_invalid_hook_proxy<D, F>(
uc: uc_handle,
user_data: *mut UcHook<D, F>,
) -> bool
where
F: FnMut(&mut crate::Unicorn<D>) -> bool,
{
let user_data = &mut *user_data;
let mut user_data_uc = Unicorn {
inner: user_data.uc.upgrade().unwrap(),
};
debug_assert_eq!(uc, user_data_uc.get_handle());
(user_data.callback)(&mut user_data_uc)
}
pub unsafe extern "C" fn insn_out_hook_proxy<D, F>(
uc: uc_handle,
port: u32,
size: usize,
value: u32,
user_data: *mut UcHook<D, F>,
) where
F: FnMut(&mut crate::Unicorn<D>, u32, usize, u32),
{
let user_data = &mut *user_data;
let mut user_data_uc = Unicorn {
inner: user_data.uc.upgrade().unwrap(),
};
debug_assert_eq!(uc, user_data_uc.get_handle());
(user_data.callback)(&mut user_data_uc, port, size, value);
}
pub unsafe extern "C" fn insn_sys_hook_proxy<D, F>(uc: uc_handle, user_data: *mut UcHook<D, F>)
where
F: FnMut(&mut crate::Unicorn<D>),
{
let user_data = &mut *user_data;
let mut user_data_uc = Unicorn {
inner: user_data.uc.upgrade().unwrap(),
};
debug_assert_eq!(uc, user_data_uc.get_handle());
(user_data.callback)(&mut user_data_uc);
}
pub unsafe extern "C" fn tlb_lookup_hook_proxy<D, F>(
uc: uc_handle,
vaddr: u64,
mem_type: MemType,
result: *mut TlbEntry,
user_data: *mut UcHook<D, F>,
) -> bool
where
F: FnMut(&mut crate::Unicorn<D>, u64, MemType) -> Option<TlbEntry>,
{
let user_data = &mut *user_data;
let mut user_data_uc = Unicorn {
inner: user_data.uc.upgrade().unwrap(),
};
debug_assert_eq!(uc, user_data_uc.get_handle());
let r = (user_data.callback)(&mut user_data_uc, vaddr, mem_type);
if let Some(ref e) = r {
let ref_result: &mut TlbEntry = &mut *result;
*ref_result = *e;
};
r.is_some()
}

278
bindings/rust/src/hook.rs Normal file
View File

@@ -0,0 +1,278 @@
#![allow(non_camel_case_types)]
use alloc::rc::Weak;
use core::{cell::UnsafeCell, ffi::c_void};
pub use unicorn_engine_sys::{self as sys, uc_context, uc_engine, uc_hook};
use crate::{Unicorn, UnicornInner};
pub struct UcHook<'a, D: 'a, F: 'a> {
pub callback: F,
pub uc: Weak<UnsafeCell<UnicornInner<'a, D>>>,
}
pub trait IsUcHook<'a> {}
impl<'a, D, F> IsUcHook<'a> for UcHook<'a, D, F> {}
/// # Safety
///
/// This function is unsafe because it dereferences the `user_data` pointer.
pub extern "C" fn mmio_read_callback_proxy<D, F>(
uc: *mut uc_engine,
offset: u64,
size: u32,
// user_data: *mut UcHook<D, F>,
user_data: *mut c_void,
) -> u64
where
F: FnMut(&mut crate::Unicorn<D>, u64, usize) -> u64,
{
let user_data = unsafe { &mut *user_data.cast::<UcHook<D, F>>() };
let mut user_data_uc = Unicorn {
inner: user_data.uc.upgrade().unwrap(),
};
debug_assert_eq!(uc, user_data_uc.get_handle());
(user_data.callback)(&mut user_data_uc, offset, size as usize)
}
/// # Safety
///
/// This function is unsafe because it dereferences the `user_data` pointer.
pub unsafe extern "C" fn mmio_write_callback_proxy<D, F>(
uc: *mut uc_engine,
offset: u64,
size: u32,
value: u64,
user_data: *mut c_void,
) where
F: FnMut(&mut crate::Unicorn<D>, u64, usize, u64),
{
let user_data = unsafe { &mut *user_data.cast::<UcHook<D, F>>() };
let mut user_data_uc = Unicorn {
inner: user_data.uc.upgrade().unwrap(),
};
debug_assert_eq!(uc, user_data_uc.get_handle());
(user_data.callback)(&mut user_data_uc, offset, size as usize, value);
}
/// # Safety
///
/// This function is unsafe because it dereferences the `user_data` pointer.
pub unsafe extern "C" fn code_hook_proxy<D, F>(
uc: *mut uc_engine,
address: u64,
size: u32,
user_data: *mut UcHook<D, F>,
) where
F: FnMut(&mut crate::Unicorn<D>, u64, u32),
{
let user_data = unsafe { &mut *user_data };
let mut user_data_uc = Unicorn {
inner: user_data.uc.upgrade().unwrap(),
};
debug_assert_eq!(uc, user_data_uc.get_handle());
(user_data.callback)(&mut user_data_uc, address, size);
}
/// # Safety
///
/// This function is unsafe because it dereferences the `user_data` pointer.
pub unsafe extern "C" fn block_hook_proxy<D, F>(
uc: *mut uc_engine,
address: u64,
size: u32,
user_data: *mut UcHook<D, F>,
) where
F: FnMut(&mut crate::Unicorn<D>, u64, u32),
{
let user_data = unsafe { &mut *user_data };
let mut user_data_uc = Unicorn {
inner: user_data.uc.upgrade().unwrap(),
};
debug_assert_eq!(uc, user_data_uc.get_handle());
(user_data.callback)(&mut user_data_uc, address, size);
}
/// # Safety
///
/// This function is unsafe because it dereferences the `user_data` pointer.
pub unsafe extern "C" fn mem_hook_proxy<D, F>(
uc: *mut uc_engine,
mem_type: sys::MemType,
address: u64,
size: u32,
value: i64,
user_data: *mut UcHook<D, F>,
) -> bool
where
F: FnMut(&mut crate::Unicorn<D>, sys::MemType, u64, usize, i64) -> bool,
{
let user_data = unsafe { &mut *user_data };
let mut user_data_uc = Unicorn {
inner: user_data.uc.upgrade().unwrap(),
};
debug_assert_eq!(uc, user_data_uc.get_handle());
(user_data.callback)(&mut user_data_uc, mem_type, address, size as usize, value)
}
/// # Safety
///
/// This function is unsafe because it dereferences the `user_data` pointer.
pub unsafe extern "C" fn intr_hook_proxy<D, F>(
uc: *mut uc_engine,
value: u32,
user_data: *mut UcHook<D, F>,
) where
F: FnMut(&mut crate::Unicorn<D>, u32),
{
let user_data = unsafe { &mut *user_data };
let mut user_data_uc = Unicorn {
inner: user_data.uc.upgrade().unwrap(),
};
debug_assert_eq!(uc, user_data_uc.get_handle());
(user_data.callback)(&mut user_data_uc, value);
}
/// # Safety
///
/// This function is unsafe because it dereferences the `user_data` pointer.
pub unsafe extern "C" fn insn_in_hook_proxy<D, F>(
uc: *mut uc_engine,
port: u32,
size: usize,
user_data: *mut UcHook<D, F>,
) -> u32
where
F: FnMut(&mut crate::Unicorn<D>, u32, usize) -> u32,
{
let user_data = unsafe { &mut *user_data };
let mut user_data_uc = Unicorn {
inner: user_data.uc.upgrade().unwrap(),
};
debug_assert_eq!(uc, user_data_uc.get_handle());
(user_data.callback)(&mut user_data_uc, port, size)
}
/// # Safety
///
/// This function is unsafe because it dereferences the `user_data` pointer.
pub unsafe extern "C" fn insn_invalid_hook_proxy<D, F>(
uc: *mut uc_engine,
user_data: *mut UcHook<D, F>,
) -> bool
where
F: FnMut(&mut crate::Unicorn<D>) -> bool,
{
let user_data = unsafe { &mut *user_data };
let mut user_data_uc = Unicorn {
inner: user_data.uc.upgrade().unwrap(),
};
debug_assert_eq!(uc, user_data_uc.get_handle());
(user_data.callback)(&mut user_data_uc)
}
/// # Safety
///
/// This function is unsafe because it dereferences the `user_data` pointer.
pub unsafe extern "C" fn insn_out_hook_proxy<D, F>(
uc: *mut uc_engine,
port: u32,
size: usize,
value: u32,
user_data: *mut UcHook<D, F>,
) where
F: FnMut(&mut crate::Unicorn<D>, u32, usize, u32),
{
let user_data = unsafe { &mut *user_data };
let mut user_data_uc = Unicorn {
inner: user_data.uc.upgrade().unwrap(),
};
debug_assert_eq!(uc, user_data_uc.get_handle());
(user_data.callback)(&mut user_data_uc, port, size, value);
}
/// # Safety
///
/// This function is unsafe because it dereferences the `user_data` pointer.
pub unsafe extern "C" fn insn_sys_hook_proxy<D, F>(uc: *mut uc_engine, user_data: *mut UcHook<D, F>)
where
F: FnMut(&mut crate::Unicorn<D>),
{
let user_data = unsafe { &mut *user_data };
let mut user_data_uc = Unicorn {
inner: user_data.uc.upgrade().unwrap(),
};
debug_assert_eq!(uc, user_data_uc.get_handle());
(user_data.callback)(&mut user_data_uc);
}
/// # Safety
///
/// This function is unsafe because it dereferences the `user_data` pointer.
#[cfg(feature = "arch_aarch64")]
pub unsafe extern "C" fn insn_sys_hook_proxy_arm64<D, F>(
uc: *mut uc_engine,
reg: sys::RegisterARM64,
cp_reg: *const sys::RegisterARM64_CP,
user_data: *mut UcHook<D, F>,
) -> bool
where
F: FnMut(&mut crate::Unicorn<D>, sys::RegisterARM64, &sys::RegisterARM64_CP) -> bool,
{
let user_data = unsafe { &mut *user_data };
let mut user_data_uc = Unicorn {
inner: user_data.uc.upgrade().unwrap(),
};
debug_assert_eq!(uc, user_data_uc.get_handle());
let cp_reg = unsafe { cp_reg.as_ref() }.unwrap();
(user_data.callback)(&mut user_data_uc, reg, cp_reg)
}
/// # Safety
///
/// This function is unsafe because it dereferences the `user_data` pointer.
pub unsafe extern "C" fn tlb_lookup_hook_proxy<D, F>(
uc: *mut uc_engine,
vaddr: u64,
mem_type: sys::MemType,
result: *mut sys::TlbEntry,
user_data: *mut UcHook<D, F>,
) -> bool
where
F: FnMut(&mut crate::Unicorn<D>, u64, sys::MemType) -> Option<sys::TlbEntry>,
{
let user_data = unsafe { &mut *user_data };
let mut user_data_uc = Unicorn {
inner: user_data.uc.upgrade().unwrap(),
};
debug_assert_eq!(uc, user_data_uc.get_handle());
let r = (user_data.callback)(&mut user_data_uc, vaddr, mem_type);
if let Some(ref e) = r {
let ref_result: &mut sys::TlbEntry = unsafe { &mut *result };
*ref_result = *e;
}
r.is_some()
}
/// # Safety
///
/// This function is unsafe because it dereferences the `user_data` pointer.
pub unsafe extern "C" fn tcg_proxy<D, F>(
uc: *mut uc_engine,
addr: u64,
arg1: u64,
arg2: u64,
size: u32,
user_data: *mut UcHook<D, F>,
) where
F: FnMut(&mut Unicorn<D>, u64, u64, u64, usize),
{
let user_data = unsafe { &mut *user_data };
let mut user_data_uc = Unicorn {
inner: user_data.uc.upgrade().unwrap(),
};
debug_assert_eq!(uc, user_data_uc.get_handle());
(user_data.callback)(&mut user_data_uc, addr, arg1, arg2, size as usize);
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,59 +0,0 @@
#![allow(non_camel_case_types)]
// M68K registers
#[repr(C)]
#[derive(PartialEq, Debug, Clone, Copy)]
pub enum RegisterM68K {
INVALID = 0,
A0,
A1,
A2,
A3,
A4,
A5,
A6,
A7,
D0,
D1,
D2,
D3,
D4,
D5,
D6,
D7,
SR,
PC,
ENDING,
}
impl From<RegisterM68K> for i32 {
fn from(r: RegisterM68K) -> Self {
r as i32
}
}
#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum M68kCpuModel {
UC_CPU_M68K_M5206 = 0,
UC_CPU_M68K_M68000 = 1,
UC_CPU_M68K_M68020 = 2,
UC_CPU_M68K_M68030 = 3,
UC_CPU_M68K_M68040 = 4,
UC_CPU_M68K_M68060 = 5,
UC_CPU_M68K_M5208 = 6,
UC_CPU_M68K_CFV4E = 7,
UC_CPU_M68K_ANY = 8,
}
impl From<M68kCpuModel> for i32 {
fn from(value: M68kCpuModel) -> Self {
value as i32
}
}
impl From<&M68kCpuModel> for i32 {
fn from(value: &M68kCpuModel) -> Self {
(*value) as i32
}
}

View File

@@ -1,286 +0,0 @@
#![allow(non_camel_case_types)]
// For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
// MIPS registers
#[repr(C)]
#[derive(PartialEq, Debug, Clone, Copy)]
pub enum RegisterMIPS {
INVALID = 0,
// General purpose registers
PC = 1,
R0 = 2,
R1 = 3,
R2 = 4,
R3 = 5,
R4 = 6,
R5 = 7,
R6 = 8,
R7 = 9,
R8 = 10,
R9 = 11,
R10 = 12,
R11 = 13,
R12 = 14,
R13 = 15,
R14 = 16,
R15 = 17,
R16 = 18,
R17 = 19,
R18 = 20,
R19 = 21,
R20 = 22,
R21 = 23,
R22 = 24,
R23 = 25,
R24 = 26,
R25 = 27,
R26 = 28,
R27 = 29,
R28 = 30,
R29 = 31,
R30 = 32,
R31 = 33,
// DSP registers
DSPCCOND = 34,
DSPCARRY = 35,
DSPEFI = 36,
DSPOUTFLAG = 37,
DSPOUTFLAG16_19 = 38,
DSPOUTFLAG20 = 39,
DSPOUTFLAG21 = 40,
DSPOUTFLAG22 = 41,
DSPOUTFLAG23 = 42,
DSPPOS = 43,
DSPSCOUNT = 44,
// ACC registers
AC0 = 45,
AC1 = 46,
AC2 = 47,
AC3 = 48,
// COP registers
CC0 = 49,
CC1 = 50,
CC2 = 51,
CC3 = 52,
CC4 = 53,
CC5 = 54,
CC6 = 55,
CC7 = 56,
// FPU registers
F0 = 57,
F1 = 58,
F2 = 59,
F3 = 60,
F4 = 61,
F5 = 62,
F6 = 63,
F7 = 64,
F8 = 65,
F9 = 66,
F10 = 67,
F11 = 68,
F12 = 69,
F13 = 70,
F14 = 71,
F15 = 72,
F16 = 73,
F17 = 74,
F18 = 75,
F19 = 76,
F20 = 77,
F21 = 78,
F22 = 79,
F23 = 80,
F24 = 81,
F25 = 82,
F26 = 83,
F27 = 84,
F28 = 85,
F29 = 86,
F30 = 87,
F31 = 88,
FCC0 = 89,
FCC1 = 90,
FCC2 = 91,
FCC3 = 92,
FCC4 = 93,
FCC5 = 94,
FCC6 = 95,
FCC7 = 96,
// AFPR128
W0 = 97,
W1 = 98,
W2 = 99,
W3 = 100,
W4 = 101,
W5 = 102,
W6 = 103,
W7 = 104,
W8 = 105,
W9 = 106,
W10 = 107,
W11 = 108,
W12 = 109,
W13 = 110,
W14 = 111,
W15 = 112,
W16 = 113,
W17 = 114,
W18 = 115,
W19 = 116,
W20 = 117,
W21 = 118,
W22 = 119,
W23 = 120,
W24 = 121,
W25 = 122,
W26 = 123,
W27 = 124,
W28 = 125,
W29 = 126,
W30 = 127,
W31 = 128,
HI = 129,
LO = 130,
P0 = 131,
P1 = 132,
P2 = 133,
MPL0 = 134,
MPL1 = 135,
MPL2 = 136,
CP0_CONFIG3 = 137,
CP0_USERLOCAL = 138,
CP0_STATUS = 139,
ENDING = 140,
}
impl RegisterMIPS {
// alias registers
// (assoc) ZERO = 2,
// (assoc) AT = 3,
// (assoc) V0 = 4,
// (assoc) V1 = 5,
// (assoc) A0 = 6,
// (assoc) A1 = 7,
// (assoc) A2 = 8,
// (assoc) A3 = 9,
// (assoc) T0 = 10,
// (assoc) T1 = 11,
// (assoc) T2 = 12,
// (assoc) T3 = 13,
// (assoc) T4 = 14,
// (assoc) T5 = 15,
// (assoc) T6 = 16,
// (assoc) T7 = 17,
// (assoc) S0 = 18,
// (assoc) S1 = 19,
// (assoc) S2 = 20,
// (assoc) S3 = 21,
// (assoc) S4 = 22,
// (assoc) S5 = 23,
// (assoc) S6 = 24,
// (assoc) S7 = 25,
// (assoc) T8 = 26,
// (assoc) T9 = 27,
// (assoc) K0 = 28,
// (assoc) K1 = 29,
// (assoc) GP = 30,
// (assoc) SP = 31,
// (assoc) FP = 32,
// (assoc) S8 = 32,
// (assoc) RA = 33,
// (assoc) HI0 = 45,
// (assoc) HI1 = 46,
// (assoc) HI2 = 47,
// (assoc) HI3 = 48,
// (assoc) LO0 = 45,
// (assoc) LO1 = 46,
// (assoc) LO2 = 47,
// (assoc) LO3 = 48,
pub const ZERO: RegisterMIPS = RegisterMIPS::R0;
pub const AT: RegisterMIPS = RegisterMIPS::R1;
pub const V0: RegisterMIPS = RegisterMIPS::R2;
pub const V1: RegisterMIPS = RegisterMIPS::R3;
pub const A0: RegisterMIPS = RegisterMIPS::R4;
pub const A1: RegisterMIPS = RegisterMIPS::R5;
pub const A2: RegisterMIPS = RegisterMIPS::R6;
pub const A3: RegisterMIPS = RegisterMIPS::R7;
pub const T0: RegisterMIPS = RegisterMIPS::R8;
pub const T1: RegisterMIPS = RegisterMIPS::R9;
pub const T2: RegisterMIPS = RegisterMIPS::R10;
pub const T3: RegisterMIPS = RegisterMIPS::R11;
pub const T4: RegisterMIPS = RegisterMIPS::R12;
pub const T5: RegisterMIPS = RegisterMIPS::R13;
pub const T6: RegisterMIPS = RegisterMIPS::R14;
pub const T7: RegisterMIPS = RegisterMIPS::R15;
pub const S0: RegisterMIPS = RegisterMIPS::R16;
pub const S1: RegisterMIPS = RegisterMIPS::R17;
pub const S2: RegisterMIPS = RegisterMIPS::R18;
pub const S3: RegisterMIPS = RegisterMIPS::R19;
pub const S4: RegisterMIPS = RegisterMIPS::R20;
pub const S5: RegisterMIPS = RegisterMIPS::R21;
pub const S6: RegisterMIPS = RegisterMIPS::R22;
pub const S7: RegisterMIPS = RegisterMIPS::R23;
pub const T8: RegisterMIPS = RegisterMIPS::R24;
pub const T9: RegisterMIPS = RegisterMIPS::R25;
pub const K0: RegisterMIPS = RegisterMIPS::R26;
pub const K1: RegisterMIPS = RegisterMIPS::R27;
pub const GP: RegisterMIPS = RegisterMIPS::R28;
pub const SP: RegisterMIPS = RegisterMIPS::R29;
pub const FP: RegisterMIPS = RegisterMIPS::R30;
pub const S8: RegisterMIPS = RegisterMIPS::R30;
pub const RA: RegisterMIPS = RegisterMIPS::R31;
pub const HI0: RegisterMIPS = RegisterMIPS::AC0;
pub const HI1: RegisterMIPS = RegisterMIPS::AC1;
pub const HI2: RegisterMIPS = RegisterMIPS::AC2;
pub const HI3: RegisterMIPS = RegisterMIPS::AC3;
pub const LO0: RegisterMIPS = RegisterMIPS::AC0;
pub const LO1: RegisterMIPS = RegisterMIPS::AC1;
pub const LO2: RegisterMIPS = RegisterMIPS::AC2;
pub const LO3: RegisterMIPS = RegisterMIPS::AC3;
}
impl From<RegisterMIPS> for i32 {
fn from(r: RegisterMIPS) -> Self {
r as i32
}
}
#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Mips32CpuModel {
UC_CPU_MIPS32_4KC = 0,
UC_CPU_MIPS32_4KM = 1,
UC_CPU_MIPS32_4KECR1 = 2,
UC_CPU_MIPS32_4KEMR1 = 3,
UC_CPU_MIPS32_4KEC = 4,
UC_CPU_MIPS32_4KEM = 5,
UC_CPU_MIPS32_24KC = 6,
UC_CPU_MIPS32_24KEC = 7,
UC_CPU_MIPS32_24KF = 8,
UC_CPU_MIPS32_34KF = 9,
UC_CPU_MIPS32_74KF = 10,
UC_CPU_MIPS32_M14K = 11,
UC_CPU_MIPS32_M14KC = 12,
UC_CPU_MIPS32_P5600 = 13,
UC_CPU_MIPS32_MIPS32R6_GENERIC = 14,
UC_CPU_MIPS32_I7200 = 15,
}
impl From<Mips32CpuModel> for i32 {
fn from(value: Mips32CpuModel) -> Self {
value as i32
}
}
impl From<&Mips32CpuModel> for i32 {
fn from(value: &Mips32CpuModel) -> Self {
*value as i32
}
}

View File

@@ -1,437 +0,0 @@
#![allow(non_camel_case_types)]
// For Unicorn Engine. AUTO-GENERATED FILE, DO NOT EDIT
// PowerPC registers
#[repr(C)]
#[derive(PartialEq, Debug, Clone, Copy)]
pub enum RegisterPPC {
INVALID = 0,
PC = 1,
R0 = 2,
R1 = 3,
R2 = 4,
R3 = 5,
R4 = 6,
R5 = 7,
R6 = 8,
R7 = 9,
R8 = 10,
R9 = 11,
R10 = 12,
R11 = 13,
R12 = 14,
R13 = 15,
R14 = 16,
R15 = 17,
R16 = 18,
R17 = 19,
R18 = 20,
R19 = 21,
R20 = 22,
R21 = 23,
R22 = 24,
R23 = 25,
R24 = 26,
R25 = 27,
R26 = 28,
R27 = 29,
R28 = 30,
R29 = 31,
R30 = 32,
R31 = 33,
CR0 = 34,
CR1 = 35,
CR2 = 36,
CR3 = 37,
CR4 = 38,
CR5 = 39,
CR6 = 40,
CR7 = 41,
FPR0 = 42,
FPR1 = 43,
FPR2 = 44,
FPR3 = 45,
FPR4 = 46,
FPR5 = 47,
FPR6 = 48,
FPR7 = 49,
FPR8 = 50,
FPR9 = 51,
FPR10 = 52,
FPR11 = 53,
FPR12 = 54,
FPR13 = 55,
FPR14 = 56,
FPR15 = 57,
FPR16 = 58,
FPR17 = 59,
FPR18 = 60,
FPR19 = 61,
FPR20 = 62,
FPR21 = 63,
FPR22 = 64,
FPR23 = 65,
FPR24 = 66,
FPR25 = 67,
FPR26 = 68,
FPR27 = 69,
FPR28 = 70,
FPR29 = 71,
FPR30 = 72,
FPR31 = 73,
LR = 74,
XER = 75,
CTR = 76,
MSR = 77,
FPSCR = 78,
CR = 79,
ENDING = 80,
}
impl From<RegisterPPC> for i32 {
fn from(r: RegisterPPC) -> Self {
r as i32
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum PpcCpuModel {
UC_CPU_PPC32_401 = 0,
UC_CPU_PPC32_401A1,
UC_CPU_PPC32_401B2,
UC_CPU_PPC32_401C2,
UC_CPU_PPC32_401D2,
UC_CPU_PPC32_401E2,
UC_CPU_PPC32_401F2,
UC_CPU_PPC32_401G2,
UC_CPU_PPC32_IOP480,
UC_CPU_PPC32_COBRA,
UC_CPU_PPC32_403GA,
UC_CPU_PPC32_403GB,
UC_CPU_PPC32_403GC,
UC_CPU_PPC32_403GCX,
UC_CPU_PPC32_405D2,
UC_CPU_PPC32_405D4,
UC_CPU_PPC32_405CRA,
UC_CPU_PPC32_405CRB,
UC_CPU_PPC32_405CRC,
UC_CPU_PPC32_405EP,
UC_CPU_PPC32_405EZ,
UC_CPU_PPC32_405GPA,
UC_CPU_PPC32_405GPB,
UC_CPU_PPC32_405GPC,
UC_CPU_PPC32_405GPD,
UC_CPU_PPC32_405GPR,
UC_CPU_PPC32_405LP,
UC_CPU_PPC32_NPE405H,
UC_CPU_PPC32_NPE405H2,
UC_CPU_PPC32_NPE405L,
UC_CPU_PPC32_NPE4GS3,
UC_CPU_PPC32_STB03,
UC_CPU_PPC32_STB04,
UC_CPU_PPC32_STB25,
UC_CPU_PPC32_X2VP4,
UC_CPU_PPC32_X2VP20,
UC_CPU_PPC32_440_XILINX,
UC_CPU_PPC32_440_XILINX_W_DFPU,
UC_CPU_PPC32_440EPA,
UC_CPU_PPC32_440EPB,
UC_CPU_PPC32_440EPX,
UC_CPU_PPC32_460EXB,
UC_CPU_PPC32_G2,
UC_CPU_PPC32_G2H4,
UC_CPU_PPC32_G2GP,
UC_CPU_PPC32_G2LS,
UC_CPU_PPC32_G2HIP3,
UC_CPU_PPC32_G2HIP4,
UC_CPU_PPC32_MPC603,
UC_CPU_PPC32_G2LE,
UC_CPU_PPC32_G2LEGP,
UC_CPU_PPC32_G2LELS,
UC_CPU_PPC32_G2LEGP1,
UC_CPU_PPC32_G2LEGP3,
UC_CPU_PPC32_MPC5200_V10,
UC_CPU_PPC32_MPC5200_V11,
UC_CPU_PPC32_MPC5200_V12,
UC_CPU_PPC32_MPC5200B_V20,
UC_CPU_PPC32_MPC5200B_V21,
UC_CPU_PPC32_E200Z5,
UC_CPU_PPC32_E200Z6,
UC_CPU_PPC32_E300C1,
UC_CPU_PPC32_E300C2,
UC_CPU_PPC32_E300C3,
UC_CPU_PPC32_E300C4,
UC_CPU_PPC32_MPC8343,
UC_CPU_PPC32_MPC8343A,
UC_CPU_PPC32_MPC8343E,
UC_CPU_PPC32_MPC8343EA,
UC_CPU_PPC32_MPC8347T,
UC_CPU_PPC32_MPC8347P,
UC_CPU_PPC32_MPC8347AT,
UC_CPU_PPC32_MPC8347AP,
UC_CPU_PPC32_MPC8347ET,
UC_CPU_PPC32_MPC8347EP,
UC_CPU_PPC32_MPC8347EAT,
UC_CPU_PPC32_MPC8347EAP,
UC_CPU_PPC32_MPC8349,
UC_CPU_PPC32_MPC8349A,
UC_CPU_PPC32_MPC8349E,
UC_CPU_PPC32_MPC8349EA,
UC_CPU_PPC32_MPC8377,
UC_CPU_PPC32_MPC8377E,
UC_CPU_PPC32_MPC8378,
UC_CPU_PPC32_MPC8378E,
UC_CPU_PPC32_MPC8379,
UC_CPU_PPC32_MPC8379E,
UC_CPU_PPC32_E500_V10,
UC_CPU_PPC32_E500_V20,
UC_CPU_PPC32_E500V2_V10,
UC_CPU_PPC32_E500V2_V20,
UC_CPU_PPC32_E500V2_V21,
UC_CPU_PPC32_E500V2_V22,
UC_CPU_PPC32_E500V2_V30,
UC_CPU_PPC32_E500MC,
UC_CPU_PPC32_MPC8533_V10,
UC_CPU_PPC32_MPC8533_V11,
UC_CPU_PPC32_MPC8533E_V10,
UC_CPU_PPC32_MPC8533E_V11,
UC_CPU_PPC32_MPC8540_V10,
UC_CPU_PPC32_MPC8540_V20,
UC_CPU_PPC32_MPC8540_V21,
UC_CPU_PPC32_MPC8541_V10,
UC_CPU_PPC32_MPC8541_V11,
UC_CPU_PPC32_MPC8541E_V10,
UC_CPU_PPC32_MPC8541E_V11,
UC_CPU_PPC32_MPC8543_V10,
UC_CPU_PPC32_MPC8543_V11,
UC_CPU_PPC32_MPC8543_V20,
UC_CPU_PPC32_MPC8543_V21,
UC_CPU_PPC32_MPC8543E_V10,
UC_CPU_PPC32_MPC8543E_V11,
UC_CPU_PPC32_MPC8543E_V20,
UC_CPU_PPC32_MPC8543E_V21,
UC_CPU_PPC32_MPC8544_V10,
UC_CPU_PPC32_MPC8544_V11,
UC_CPU_PPC32_MPC8544E_V10,
UC_CPU_PPC32_MPC8544E_V11,
UC_CPU_PPC32_MPC8545_V20,
UC_CPU_PPC32_MPC8545_V21,
UC_CPU_PPC32_MPC8545E_V20,
UC_CPU_PPC32_MPC8545E_V21,
UC_CPU_PPC32_MPC8547E_V20,
UC_CPU_PPC32_MPC8547E_V21,
UC_CPU_PPC32_MPC8548_V10,
UC_CPU_PPC32_MPC8548_V11,
UC_CPU_PPC32_MPC8548_V20,
UC_CPU_PPC32_MPC8548_V21,
UC_CPU_PPC32_MPC8548E_V10,
UC_CPU_PPC32_MPC8548E_V11,
UC_CPU_PPC32_MPC8548E_V20,
UC_CPU_PPC32_MPC8548E_V21,
UC_CPU_PPC32_MPC8555_V10,
UC_CPU_PPC32_MPC8555_V11,
UC_CPU_PPC32_MPC8555E_V10,
UC_CPU_PPC32_MPC8555E_V11,
UC_CPU_PPC32_MPC8560_V10,
UC_CPU_PPC32_MPC8560_V20,
UC_CPU_PPC32_MPC8560_V21,
UC_CPU_PPC32_MPC8567,
UC_CPU_PPC32_MPC8567E,
UC_CPU_PPC32_MPC8568,
UC_CPU_PPC32_MPC8568E,
UC_CPU_PPC32_MPC8572,
UC_CPU_PPC32_MPC8572E,
UC_CPU_PPC32_E600,
UC_CPU_PPC32_MPC8610,
UC_CPU_PPC32_MPC8641,
UC_CPU_PPC32_MPC8641D,
UC_CPU_PPC32_601_V0,
UC_CPU_PPC32_601_V1,
UC_CPU_PPC32_601_V2,
UC_CPU_PPC32_602,
UC_CPU_PPC32_603,
UC_CPU_PPC32_603E_V1_1,
UC_CPU_PPC32_603E_V1_2,
UC_CPU_PPC32_603E_V1_3,
UC_CPU_PPC32_603E_V1_4,
UC_CPU_PPC32_603E_V2_2,
UC_CPU_PPC32_603E_V3,
UC_CPU_PPC32_603E_V4,
UC_CPU_PPC32_603E_V4_1,
UC_CPU_PPC32_603E7,
UC_CPU_PPC32_603E7T,
UC_CPU_PPC32_603E7V,
UC_CPU_PPC32_603E7V1,
UC_CPU_PPC32_603E7V2,
UC_CPU_PPC32_603P,
UC_CPU_PPC32_604,
UC_CPU_PPC32_604E_V1_0,
UC_CPU_PPC32_604E_V2_2,
UC_CPU_PPC32_604E_V2_4,
UC_CPU_PPC32_604R,
UC_CPU_PPC32_740_V1_0,
UC_CPU_PPC32_750_V1_0,
UC_CPU_PPC32_740_V2_0,
UC_CPU_PPC32_750_V2_0,
UC_CPU_PPC32_740_V2_1,
UC_CPU_PPC32_750_V2_1,
UC_CPU_PPC32_740_V2_2,
UC_CPU_PPC32_750_V2_2,
UC_CPU_PPC32_740_V3_0,
UC_CPU_PPC32_750_V3_0,
UC_CPU_PPC32_740_V3_1,
UC_CPU_PPC32_750_V3_1,
UC_CPU_PPC32_740E,
UC_CPU_PPC32_750E,
UC_CPU_PPC32_740P,
UC_CPU_PPC32_750P,
UC_CPU_PPC32_750CL_V1_0,
UC_CPU_PPC32_750CL_V2_0,
UC_CPU_PPC32_750CX_V1_0,
UC_CPU_PPC32_750CX_V2_0,
UC_CPU_PPC32_750CX_V2_1,
UC_CPU_PPC32_750CX_V2_2,
UC_CPU_PPC32_750CXE_V2_1,
UC_CPU_PPC32_750CXE_V2_2,
UC_CPU_PPC32_750CXE_V2_3,
UC_CPU_PPC32_750CXE_V2_4,
UC_CPU_PPC32_750CXE_V2_4B,
UC_CPU_PPC32_750CXE_V3_0,
UC_CPU_PPC32_750CXE_V3_1,
UC_CPU_PPC32_750CXE_V3_1B,
UC_CPU_PPC32_750CXR,
UC_CPU_PPC32_750FL,
UC_CPU_PPC32_750FX_V1_0,
UC_CPU_PPC32_750FX_V2_0,
UC_CPU_PPC32_750FX_V2_1,
UC_CPU_PPC32_750FX_V2_2,
UC_CPU_PPC32_750FX_V2_3,
UC_CPU_PPC32_750GL,
UC_CPU_PPC32_750GX_V1_0,
UC_CPU_PPC32_750GX_V1_1,
UC_CPU_PPC32_750GX_V1_2,
UC_CPU_PPC32_750L_V2_0,
UC_CPU_PPC32_750L_V2_1,
UC_CPU_PPC32_750L_V2_2,
UC_CPU_PPC32_750L_V3_0,
UC_CPU_PPC32_750L_V3_2,
UC_CPU_PPC32_745_V1_0,
UC_CPU_PPC32_755_V1_0,
UC_CPU_PPC32_745_V1_1,
UC_CPU_PPC32_755_V1_1,
UC_CPU_PPC32_745_V2_0,
UC_CPU_PPC32_755_V2_0,
UC_CPU_PPC32_745_V2_1,
UC_CPU_PPC32_755_V2_1,
UC_CPU_PPC32_745_V2_2,
UC_CPU_PPC32_755_V2_2,
UC_CPU_PPC32_745_V2_3,
UC_CPU_PPC32_755_V2_3,
UC_CPU_PPC32_745_V2_4,
UC_CPU_PPC32_755_V2_4,
UC_CPU_PPC32_745_V2_5,
UC_CPU_PPC32_755_V2_5,
UC_CPU_PPC32_745_V2_6,
UC_CPU_PPC32_755_V2_6,
UC_CPU_PPC32_745_V2_7,
UC_CPU_PPC32_755_V2_7,
UC_CPU_PPC32_745_V2_8,
UC_CPU_PPC32_755_V2_8,
UC_CPU_PPC32_7400_V1_0,
UC_CPU_PPC32_7400_V1_1,
UC_CPU_PPC32_7400_V2_0,
UC_CPU_PPC32_7400_V2_1,
UC_CPU_PPC32_7400_V2_2,
UC_CPU_PPC32_7400_V2_6,
UC_CPU_PPC32_7400_V2_7,
UC_CPU_PPC32_7400_V2_8,
UC_CPU_PPC32_7400_V2_9,
UC_CPU_PPC32_7410_V1_0,
UC_CPU_PPC32_7410_V1_1,
UC_CPU_PPC32_7410_V1_2,
UC_CPU_PPC32_7410_V1_3,
UC_CPU_PPC32_7410_V1_4,
UC_CPU_PPC32_7448_V1_0,
UC_CPU_PPC32_7448_V1_1,
UC_CPU_PPC32_7448_V2_0,
UC_CPU_PPC32_7448_V2_1,
UC_CPU_PPC32_7450_V1_0,
UC_CPU_PPC32_7450_V1_1,
UC_CPU_PPC32_7450_V1_2,
UC_CPU_PPC32_7450_V2_0,
UC_CPU_PPC32_7450_V2_1,
UC_CPU_PPC32_7441_V2_1,
UC_CPU_PPC32_7441_V2_3,
UC_CPU_PPC32_7451_V2_3,
UC_CPU_PPC32_7441_V2_10,
UC_CPU_PPC32_7451_V2_10,
UC_CPU_PPC32_7445_V1_0,
UC_CPU_PPC32_7455_V1_0,
UC_CPU_PPC32_7445_V2_1,
UC_CPU_PPC32_7455_V2_1,
UC_CPU_PPC32_7445_V3_2,
UC_CPU_PPC32_7455_V3_2,
UC_CPU_PPC32_7445_V3_3,
UC_CPU_PPC32_7455_V3_3,
UC_CPU_PPC32_7445_V3_4,
UC_CPU_PPC32_7455_V3_4,
UC_CPU_PPC32_7447_V1_0,
UC_CPU_PPC32_7457_V1_0,
UC_CPU_PPC32_7447_V1_1,
UC_CPU_PPC32_7457_V1_1,
UC_CPU_PPC32_7457_V1_2,
UC_CPU_PPC32_7447A_V1_0,
UC_CPU_PPC32_7457A_V1_0,
UC_CPU_PPC32_7447A_V1_1,
UC_CPU_PPC32_7457A_V1_1,
UC_CPU_PPC32_7447A_V1_2,
UC_CPU_PPC32_7457A_V1_2,
}
impl From<PpcCpuModel> for i32 {
fn from(value: PpcCpuModel) -> Self {
value as i32
}
}
impl From<&PpcCpuModel> for i32 {
fn from(value: &PpcCpuModel) -> Self {
(*value) as i32
}
}
#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Ppc64CpuModel {
UC_CPU_PPC64_E5500 = 0,
UC_CPU_PPC64_E6500,
UC_CPU_PPC64_970_V2_2,
UC_CPU_PPC64_970FX_V1_0,
UC_CPU_PPC64_970FX_V2_0,
UC_CPU_PPC64_970FX_V2_1,
UC_CPU_PPC64_970FX_V3_0,
UC_CPU_PPC64_970FX_V3_1,
UC_CPU_PPC64_970MP_V1_0,
UC_CPU_PPC64_970MP_V1_1,
UC_CPU_PPC64_POWER5_V2_1,
UC_CPU_PPC64_POWER7_V2_3,
UC_CPU_PPC64_POWER7_V2_1,
UC_CPU_PPC64_POWER8E_V2_1,
UC_CPU_PPC64_POWER8_V2_0,
UC_CPU_PPC64_POWER8NVL_V1_0,
UC_CPU_PPC64_POWER9_V1_0,
UC_CPU_PPC64_POWER9_V2_0,
UC_CPU_PPC64_POWER10_V1_0,
}
impl From<Ppc64CpuModel> for i32 {
fn from(value: Ppc64CpuModel) -> Self {
value as i32
}
}
impl From<&Ppc64CpuModel> for i32 {
fn from(value: &Ppc64CpuModel) -> Self {
(*value) as i32
}
}

View File

@@ -1,388 +0,0 @@
#![allow(non_camel_case_types)]
// RISCV registers
#[repr(C)]
#[derive(PartialEq, Debug, Clone, Copy)]
pub enum RegisterRISCV {
INVALID = 0,
// General purpose registers
X0 = 1,
X1 = 2,
X2 = 3,
X3 = 4,
X4 = 5,
X5 = 6,
X6 = 7,
X7 = 8,
X8 = 9,
X9 = 10,
X10 = 11,
X11 = 12,
X12 = 13,
X13 = 14,
X14 = 15,
X15 = 16,
X16 = 17,
X17 = 18,
X18 = 19,
X19 = 20,
X20 = 21,
X21 = 22,
X22 = 23,
X23 = 24,
X24 = 25,
X25 = 26,
X26 = 27,
X27 = 28,
X28 = 29,
X29 = 30,
X30 = 31,
X31 = 32,
// CSR
USTATUS = 33,
UIE = 34,
UTVEC = 35,
USCRATCH = 36,
UEPC = 37,
UCAUSE = 38,
UTVAL = 39,
UIP = 40,
FFLAGS = 41,
FRM = 42,
FCSR = 43,
CYCLE = 44,
TIME = 45,
INSTRET = 46,
HPMCOUNTER3 = 47,
HPMCOUNTER4 = 48,
HPMCOUNTER5 = 49,
HPMCOUNTER6 = 50,
HPMCOUNTER7 = 51,
HPMCOUNTER8 = 52,
HPMCOUNTER9 = 53,
HPMCOUNTER10 = 54,
HPMCOUNTER11 = 55,
HPMCOUNTER12 = 56,
HPMCOUNTER13 = 57,
HPMCOUNTER14 = 58,
HPMCOUNTER15 = 59,
HPMCOUNTER16 = 60,
HPMCOUNTER17 = 61,
HPMCOUNTER18 = 62,
HPMCOUNTER19 = 63,
HPMCOUNTER20 = 64,
HPMCOUNTER21 = 65,
HPMCOUNTER22 = 66,
HPMCOUNTER23 = 67,
HPMCOUNTER24 = 68,
HPMCOUNTER25 = 69,
HPMCOUNTER26 = 70,
HPMCOUNTER27 = 71,
HPMCOUNTER28 = 72,
HPMCOUNTER29 = 73,
HPMCOUNTER30 = 74,
HPMCOUNTER31 = 75,
CYCLEH = 76,
TIMEH = 77,
INSTRETH = 78,
HPMCOUNTER3H = 79,
HPMCOUNTER4H = 80,
HPMCOUNTER5H = 81,
HPMCOUNTER6H = 82,
HPMCOUNTER7H = 83,
HPMCOUNTER8H = 84,
HPMCOUNTER9H = 85,
HPMCOUNTER10H = 86,
HPMCOUNTER11H = 87,
HPMCOUNTER12H = 88,
HPMCOUNTER13H = 89,
HPMCOUNTER14H = 90,
HPMCOUNTER15H = 91,
HPMCOUNTER16H = 92,
HPMCOUNTER17H = 93,
HPMCOUNTER18H = 94,
HPMCOUNTER19H = 95,
HPMCOUNTER20H = 96,
HPMCOUNTER21H = 97,
HPMCOUNTER22H = 98,
HPMCOUNTER23H = 99,
HPMCOUNTER24H = 100,
HPMCOUNTER25H = 101,
HPMCOUNTER26H = 102,
HPMCOUNTER27H = 103,
HPMCOUNTER28H = 104,
HPMCOUNTER29H = 105,
HPMCOUNTER30H = 106,
HPMCOUNTER31H = 107,
MCYCLE = 108,
MINSTRET = 109,
MCYCLEH = 110,
MINSTRETH = 111,
MVENDORID = 112,
MARCHID = 113,
MIMPID = 114,
MHARTID = 115,
MSTATUS = 116,
MISA = 117,
MEDELEG = 118,
MIDELEG = 119,
MIE = 120,
MTVEC = 121,
MCOUNTEREN = 122,
MSTATUSH = 123,
MUCOUNTEREN = 124,
MSCOUNTEREN = 125,
MHCOUNTEREN = 126,
MSCRATCH = 127,
MEPC = 128,
MCAUSE = 129,
MTVAL = 130,
MIP = 131,
MBADADDR = 132,
SSTATUS = 133,
SEDELEG = 134,
SIDELEG = 135,
SIE = 136,
STVEC = 137,
SCOUNTEREN = 138,
SSCRATCH = 139,
SEPC = 140,
SCAUSE = 141,
STVAL = 142,
SIP = 143,
SBADADDR = 144,
SPTBR = 145,
SATP = 146,
HSTATUS = 147,
HEDELEG = 148,
HIDELEG = 149,
HIE = 150,
HCOUNTEREN = 151,
HTVAL = 152,
HIP = 153,
HTINST = 154,
HGATP = 155,
HTIMEDELTA = 156,
HTIMEDELTAH = 157,
// Floating-point registers
F0 = 158,
F1 = 159,
F2 = 160,
F3 = 161,
F4 = 162,
F5 = 163,
F6 = 164,
F7 = 165,
F8 = 166,
F9 = 167,
F10 = 168,
F11 = 169,
F12 = 170,
F13 = 171,
F14 = 172,
F15 = 173,
F16 = 174,
F17 = 175,
F18 = 176,
F19 = 177,
F20 = 178,
F21 = 179,
F22 = 180,
F23 = 181,
F24 = 182,
F25 = 183,
F26 = 184,
F27 = 185,
F28 = 186,
F29 = 187,
F30 = 188,
F31 = 189,
PC = 190,
PRIV = 191,
ENDING = 192,
}
impl RegisterRISCV {
// Alias registers
// (assoc) ZERO = 1,
// (assoc) RA = 2,
// (assoc) SP = 3,
// (assoc) GP = 4,
// (assoc) TP = 5,
// (assoc) T0 = 6,
// (assoc) T1 = 7,
// (assoc) T2 = 8,
// (assoc) S0 = 9,
// (assoc) FP = 9,
// (assoc) S1 = 10,
// (assoc) A0 = 11,
// (assoc) A1 = 12,
// (assoc) A2 = 13,
// (assoc) A3 = 14,
// (assoc) A4 = 15,
// (assoc) A5 = 16,
// (assoc) A6 = 17,
// (assoc) A7 = 18,
// (assoc) S2 = 19,
// (assoc) S3 = 20,
// (assoc) S4 = 21,
// (assoc) S5 = 22,
// (assoc) S6 = 23,
// (assoc) S7 = 24,
// (assoc) S8 = 25,
// (assoc) S9 = 26,
// (assoc) S10 = 27,
// (assoc) S11 = 28,
// (assoc) T3 = 29,
// (assoc) T4 = 30,
// (assoc) T5 = 31,
// (assoc) T6 = 32,
// (assoc) FT0 = 158,
// (assoc) FT1 = 159,
// (assoc) FT2 = 160,
// (assoc) FT3 = 161,
// (assoc) FT4 = 162,
// (assoc) FT5 = 163,
// (assoc) FT6 = 164,
// (assoc) FT7 = 165,
// (assoc) FS0 = 166,
// (assoc) FS1 = 167,
// (assoc) FA0 = 168,
// (assoc) FA1 = 169,
// (assoc) FA2 = 170,
// (assoc) FA3 = 171,
// (assoc) FA4 = 172,
// (assoc) FA5 = 173,
// (assoc) FA6 = 174,
// (assoc) FA7 = 175,
// (assoc) FS2 = 176,
// (assoc) FS3 = 177,
// (assoc) FS4 = 178,
// (assoc) FS5 = 179,
// (assoc) FS6 = 180,
// (assoc) FS7 = 181,
// (assoc) FS8 = 182,
// (assoc) FS9 = 183,
// (assoc) FS10 = 184,
// (assoc) FS11 = 185,
// (assoc) FT8 = 186,
// (assoc) FT9 = 187,
// (assoc) FT10 = 188,
// (assoc) FT11 = 189,
pub const ZERO: RegisterRISCV = RegisterRISCV::X0;
pub const RA: RegisterRISCV = RegisterRISCV::X1;
pub const SP: RegisterRISCV = RegisterRISCV::X2;
pub const GP: RegisterRISCV = RegisterRISCV::X3;
pub const TP: RegisterRISCV = RegisterRISCV::X4;
pub const T0: RegisterRISCV = RegisterRISCV::X5;
pub const T1: RegisterRISCV = RegisterRISCV::X6;
pub const T2: RegisterRISCV = RegisterRISCV::X7;
pub const S0: RegisterRISCV = RegisterRISCV::X8;
pub const FP: RegisterRISCV = RegisterRISCV::X8;
pub const S1: RegisterRISCV = RegisterRISCV::X9;
pub const A0: RegisterRISCV = RegisterRISCV::X10;
pub const A1: RegisterRISCV = RegisterRISCV::X11;
pub const A2: RegisterRISCV = RegisterRISCV::X12;
pub const A3: RegisterRISCV = RegisterRISCV::X13;
pub const A4: RegisterRISCV = RegisterRISCV::X14;
pub const A5: RegisterRISCV = RegisterRISCV::X15;
pub const A6: RegisterRISCV = RegisterRISCV::X16;
pub const A7: RegisterRISCV = RegisterRISCV::X17;
pub const S2: RegisterRISCV = RegisterRISCV::X18;
pub const S3: RegisterRISCV = RegisterRISCV::X19;
pub const S4: RegisterRISCV = RegisterRISCV::X20;
pub const S5: RegisterRISCV = RegisterRISCV::X21;
pub const S6: RegisterRISCV = RegisterRISCV::X22;
pub const S7: RegisterRISCV = RegisterRISCV::X23;
pub const S8: RegisterRISCV = RegisterRISCV::X24;
pub const S9: RegisterRISCV = RegisterRISCV::X25;
pub const S10: RegisterRISCV = RegisterRISCV::X26;
pub const S11: RegisterRISCV = RegisterRISCV::X27;
pub const T3: RegisterRISCV = RegisterRISCV::X28;
pub const T4: RegisterRISCV = RegisterRISCV::X29;
pub const T5: RegisterRISCV = RegisterRISCV::X30;
pub const T6: RegisterRISCV = RegisterRISCV::X31;
pub const FT0: RegisterRISCV = RegisterRISCV::F0;
pub const FT1: RegisterRISCV = RegisterRISCV::F1;
pub const FT2: RegisterRISCV = RegisterRISCV::F2;
pub const FT3: RegisterRISCV = RegisterRISCV::F3;
pub const FT4: RegisterRISCV = RegisterRISCV::F4;
pub const FT5: RegisterRISCV = RegisterRISCV::F5;
pub const FT6: RegisterRISCV = RegisterRISCV::F6;
pub const FT7: RegisterRISCV = RegisterRISCV::F7;
pub const FS0: RegisterRISCV = RegisterRISCV::F8;
pub const FS1: RegisterRISCV = RegisterRISCV::F9;
pub const FA0: RegisterRISCV = RegisterRISCV::F10;
pub const FA1: RegisterRISCV = RegisterRISCV::F11;
pub const FA2: RegisterRISCV = RegisterRISCV::F12;
pub const FA3: RegisterRISCV = RegisterRISCV::F13;
pub const FA4: RegisterRISCV = RegisterRISCV::F14;
pub const FA5: RegisterRISCV = RegisterRISCV::F15;
pub const FA6: RegisterRISCV = RegisterRISCV::F16;
pub const FA7: RegisterRISCV = RegisterRISCV::F17;
pub const FS2: RegisterRISCV = RegisterRISCV::F18;
pub const FS3: RegisterRISCV = RegisterRISCV::F19;
pub const FS4: RegisterRISCV = RegisterRISCV::F20;
pub const FS5: RegisterRISCV = RegisterRISCV::F21;
pub const FS6: RegisterRISCV = RegisterRISCV::F22;
pub const FS7: RegisterRISCV = RegisterRISCV::F23;
pub const FS8: RegisterRISCV = RegisterRISCV::F24;
pub const FS9: RegisterRISCV = RegisterRISCV::F25;
pub const FS10: RegisterRISCV = RegisterRISCV::F26;
pub const FS11: RegisterRISCV = RegisterRISCV::F27;
pub const FT8: RegisterRISCV = RegisterRISCV::F28;
pub const FT9: RegisterRISCV = RegisterRISCV::F29;
pub const FT10: RegisterRISCV = RegisterRISCV::F30;
pub const FT11: RegisterRISCV = RegisterRISCV::F31;
}
impl From<RegisterRISCV> for i32 {
fn from(r: RegisterRISCV) -> Self {
r as i32
}
}
#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Riscv32CpuModel {
UC_CPU_RISCV32_ANY = 0,
UC_CPU_RISCV32_BASE32,
UC_CPU_RISCV32_SIFIVE_E31,
UC_CPU_RISCV32_SIFIVE_U34,
}
impl From<Riscv32CpuModel> for i32 {
fn from(value: Riscv32CpuModel) -> Self {
value as i32
}
}
impl From<&Riscv32CpuModel> for i32 {
fn from(value: &Riscv32CpuModel) -> Self {
(*value) as i32
}
}
#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Riscv64CpuModel {
UC_CPU_RISCV64_ANY = 0,
UC_CPU_RISCV64_BASE64,
UC_CPU_RISCV64_SIFIVE_E51,
UC_CPU_RISCV64_SIFIVE_U54,
}
impl From<Riscv64CpuModel> for i32 {
fn from(value: Riscv64CpuModel) -> Self {
value as i32
}
}
impl From<&Riscv64CpuModel> for i32 {
fn from(value: &Riscv64CpuModel) -> Self {
(*value) as i32
}
}

View File

@@ -1,141 +0,0 @@
#![allow(non_camel_case_types)]
// S390X registers
#[repr(C)]
#[derive(PartialEq, Debug, Clone, Copy)]
pub enum RegisterS390X {
INVALID = 0,
// General purpose registers
R0 = 1,
R1 = 2,
R2 = 3,
R3 = 4,
R4 = 5,
R5 = 6,
R6 = 7,
R7 = 8,
R8 = 9,
R9 = 10,
R10 = 11,
R11 = 12,
R12 = 13,
R13 = 14,
R14 = 15,
R15 = 16,
// Floating point registers
F0 = 17,
F1 = 18,
F2 = 19,
F3 = 20,
F4 = 21,
F5 = 22,
F6 = 23,
F7 = 24,
F8 = 25,
F9 = 26,
F10 = 27,
F11 = 28,
F12 = 29,
F13 = 30,
F14 = 31,
F15 = 32,
F16 = 33,
F17 = 34,
F18 = 35,
F19 = 36,
F20 = 37,
F21 = 38,
F22 = 39,
F23 = 40,
F24 = 41,
F25 = 42,
F26 = 43,
F27 = 44,
F28 = 45,
F29 = 46,
F30 = 47,
F31 = 48,
// Access registers
A0 = 49,
A1 = 50,
A2 = 51,
A3 = 52,
A4 = 53,
A5 = 54,
A6 = 55,
A7 = 56,
A8 = 57,
A9 = 58,
A10 = 59,
A11 = 60,
A12 = 61,
A13 = 62,
A14 = 63,
A15 = 64,
PC = 65,
PSWM = 66,
ENDING = 67,
}
impl From<RegisterS390X> for i32 {
fn from(r: RegisterS390X) -> Self {
r as i32
}
}
#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum S390xCpuModel {
UC_CPU_S390X_Z900 = 0,
UC_CPU_S390X_Z900_2,
UC_CPU_S390X_Z900_3,
UC_CPU_S390X_Z800,
UC_CPU_S390X_Z990,
UC_CPU_S390X_Z990_2,
UC_CPU_S390X_Z990_3,
UC_CPU_S390X_Z890,
UC_CPU_S390X_Z990_4,
UC_CPU_S390X_Z890_2,
UC_CPU_S390X_Z990_5,
UC_CPU_S390X_Z890_3,
UC_CPU_S390X_Z9EC,
UC_CPU_S390X_Z9EC_2,
UC_CPU_S390X_Z9BC,
UC_CPU_S390X_Z9EC_3,
UC_CPU_S390X_Z9BC_2,
UC_CPU_S390X_Z10EC,
UC_CPU_S390X_Z10EC_2,
UC_CPU_S390X_Z10BC,
UC_CPU_S390X_Z10EC_3,
UC_CPU_S390X_Z10BC_2,
UC_CPU_S390X_Z196,
UC_CPU_S390X_Z196_2,
UC_CPU_S390X_Z114,
UC_CPU_S390X_ZEC12,
UC_CPU_S390X_ZEC12_2,
UC_CPU_S390X_ZBC12,
UC_CPU_S390X_Z13,
UC_CPU_S390X_Z13_2,
UC_CPU_S390X_Z13S,
UC_CPU_S390X_Z14,
UC_CPU_S390X_Z14_2,
UC_CPU_S390X_Z14ZR1,
UC_CPU_S390X_GEN15A,
UC_CPU_S390X_GEN15B,
UC_CPU_S390X_QEMU,
}
impl From<S390xCpuModel> for i32 {
fn from(value: S390xCpuModel) -> Self {
value as i32
}
}
impl From<&S390xCpuModel> for i32 {
fn from(value: &S390xCpuModel) -> Self {
(*value) as i32
}
}

View File

@@ -1,176 +0,0 @@
#![allow(non_camel_case_types)]
// SPARC registers
#[repr(C)]
#[derive(PartialEq, Debug, Clone, Copy)]
#[allow(clippy::upper_case_acronyms)]
pub enum RegisterSPARC {
INVALID = 0,
F0 = 1,
F1 = 2,
F2 = 3,
F3 = 4,
F4 = 5,
F5 = 6,
F6 = 7,
F7 = 8,
F8 = 9,
F9 = 10,
F10 = 11,
F11 = 12,
F12 = 13,
F13 = 14,
F14 = 15,
F15 = 16,
F16 = 17,
F17 = 18,
F18 = 19,
F19 = 20,
F20 = 21,
F21 = 22,
F22 = 23,
F23 = 24,
F24 = 25,
F25 = 26,
F26 = 27,
F27 = 28,
F28 = 29,
F29 = 30,
F30 = 31,
F31 = 32,
F32 = 33,
F34 = 34,
F36 = 35,
F38 = 36,
F40 = 37,
F42 = 38,
F44 = 39,
F46 = 40,
F48 = 41,
F50 = 42,
F52 = 43,
F54 = 44,
F56 = 45,
F58 = 46,
F60 = 47,
F62 = 48,
FCC0 = 49,
FCC1 = 50,
FCC2 = 51,
FCC3 = 52,
G0 = 53,
G1 = 54,
G2 = 55,
G3 = 56,
G4 = 57,
G5 = 58,
G6 = 59,
G7 = 60,
I0 = 61,
I1 = 62,
I2 = 63,
I3 = 64,
I4 = 65,
I5 = 66,
FP = 67,
I7 = 68,
ICC = 69,
L0 = 70,
L1 = 71,
L2 = 72,
L3 = 73,
L4 = 74,
L5 = 75,
L6 = 76,
L7 = 77,
O0 = 78,
O1 = 79,
O2 = 80,
O3 = 81,
O4 = 82,
O5 = 83,
SP = 84,
O7 = 85,
Y = 86,
XCC = 87,
PC = 88,
ENDING = 89,
}
impl RegisterSPARC {
// alias registers
// (assoc) O6 = 84,
// (assoc) I6 = 67,
pub const O6: RegisterSPARC = RegisterSPARC::SP;
pub const I6: RegisterSPARC = RegisterSPARC::FP;
}
impl From<RegisterSPARC> for i32 {
fn from(r: RegisterSPARC) -> Self {
r as i32
}
}
#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Sparc32CpuModel {
UC_CPU_SPARC32_FUJITSU_MB86904 = 0,
UC_CPU_SPARC32_FUJITSU_MB86907,
UC_CPU_SPARC32_TI_MICROSPARC_I,
UC_CPU_SPARC32_TI_MICROSPARC_II,
UC_CPU_SPARC32_TI_MICROSPARC_IIEP,
UC_CPU_SPARC32_TI_SUPERSPARC_40,
UC_CPU_SPARC32_TI_SUPERSPARC_50,
UC_CPU_SPARC32_TI_SUPERSPARC_51,
UC_CPU_SPARC32_TI_SUPERSPARC_60,
UC_CPU_SPARC32_TI_SUPERSPARC_61,
UC_CPU_SPARC32_TI_SUPERSPARC_II,
UC_CPU_SPARC32_LEON2,
UC_CPU_SPARC32_LEON3,
}
impl From<Sparc32CpuModel> for i32 {
fn from(value: Sparc32CpuModel) -> Self {
value as i32
}
}
impl From<&Sparc32CpuModel> for i32 {
fn from(value: &Sparc32CpuModel) -> Self {
(*value) as i32
}
}
#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum Sparc64CpuModel {
UC_CPU_SPARC64_FUJITSU = 0,
UC_CPU_SPARC64_FUJITSU_III,
UC_CPU_SPARC64_FUJITSU_IV,
UC_CPU_SPARC64_FUJITSU_V,
UC_CPU_SPARC64_TI_ULTRASPARC_I,
UC_CPU_SPARC64_TI_ULTRASPARC_II,
UC_CPU_SPARC64_TI_ULTRASPARC_III,
UC_CPU_SPARC64_TI_ULTRASPARC_IIE,
UC_CPU_SPARC64_SUN_ULTRASPARC_III,
UC_CPU_SPARC64_SUN_ULTRASPARC_III_CU,
UC_CPU_SPARC64_SUN_ULTRASPARC_IIII,
UC_CPU_SPARC64_SUN_ULTRASPARC_IV,
UC_CPU_SPARC64_SUN_ULTRASPARC_IV_PLUS,
UC_CPU_SPARC64_SUN_ULTRASPARC_IIII_PLUS,
UC_CPU_SPARC64_SUN_ULTRASPARC_T1,
UC_CPU_SPARC64_SUN_ULTRASPARC_T2,
UC_CPU_SPARC64_NEC_ULTRASPARC_I,
}
impl From<Sparc64CpuModel> for i32 {
fn from(value: Sparc64CpuModel) -> Self {
value as i32
}
}
impl From<&Sparc64CpuModel> for i32 {
fn from(value: &Sparc64CpuModel) -> Self {
(*value) as i32
}
}

View File

@@ -0,0 +1,957 @@
use super::*;
use crate::{ArmCpuModel, RegisterARM, RegisterARM_CP, TcgOpCode, TcgOpFlag, uc_error};
#[test]
fn test_arm_nop() {
let code = b"\x00\xf0\x20\xe3"; // nop
let r0 = 0x1234;
let r1 = 0x5678;
let mut uc = uc_common_setup(Arch::ARM, Mode::ARM, None, code, ());
uc.reg_write(RegisterARM::R0, r0).unwrap();
uc.reg_write(RegisterARM::R1, r1).unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let r0 = uc.reg_read(RegisterARM::R0).unwrap();
let r1 = uc.reg_read(RegisterARM::R1).unwrap();
assert_eq!(r0, 0x1234);
assert_eq!(r1, 0x5678);
}
#[test]
fn test_arm_thumb_sub() {
let code = b"\x83\xb0"; // sub sp, #0xc
let sp = 0x1234;
let mut uc = uc_common_setup(
Arch::ARM,
Mode::THUMB,
Some(ArmCpuModel::CORTEX_A15 as i32),
code,
(),
);
uc.reg_write(RegisterARM::SP, sp).unwrap();
uc.emu_start(CODE_START | 1, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let sp = uc.reg_read(RegisterARM::SP).unwrap();
assert_eq!(sp, 0x1228);
}
#[test]
fn test_armeb_sub() {
let code = &[
0xe3, 0xa0, 0x00, 0x37, // mov r0, #0x37
0xe0, 0x42, 0x10, 0x03, // sub r1, r2, r3
];
let r0 = 0x1234;
let r2 = 0x6789;
let r3 = 0x3333;
let mut uc = uc_common_setup(
Arch::ARM,
Mode::ARM | Mode::BIG_ENDIAN,
Some(ArmCpuModel::Model_1176 as i32),
code,
(),
);
uc.reg_write(RegisterARM::R0, r0).unwrap();
uc.reg_write(RegisterARM::R2, r2).unwrap();
uc.reg_write(RegisterARM::R3, r3).unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let r0 = uc.reg_read(RegisterARM::R0).unwrap();
let r1 = uc.reg_read(RegisterARM::R1).unwrap();
let r2 = uc.reg_read(RegisterARM::R2).unwrap();
let r3 = uc.reg_read(RegisterARM::R3).unwrap();
assert_eq!(r0, 0x37);
assert_eq!(r2, 0x6789);
assert_eq!(r3, 0x3333);
assert_eq!(r1, 0x3456);
}
#[test]
fn test_armeb_be8_sub() {
let code = &[
0x37, 0x00, 0xa0, 0xe3, // mov r0, #0x37
0x03, 0x10, 0x42, 0xe0, // sub r1, r2, r3
];
let r0 = 0x1234;
let r2 = 0x6789;
let r3 = 0x3333;
let mut uc = uc_common_setup(
Arch::ARM,
Mode::ARM | Mode::ARMBE8,
Some(ArmCpuModel::CORTEX_A15 as i32),
code,
(),
);
uc.reg_write(RegisterARM::R0, r0).unwrap();
uc.reg_write(RegisterARM::R2, r2).unwrap();
uc.reg_write(RegisterARM::R3, r3).unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let r0 = uc.reg_read(RegisterARM::R0).unwrap();
let r1 = uc.reg_read(RegisterARM::R1).unwrap();
let r2 = uc.reg_read(RegisterARM::R2).unwrap();
let r3 = uc.reg_read(RegisterARM::R3).unwrap();
assert_eq!(r0, 0x37);
assert_eq!(r2, 0x6789);
assert_eq!(r3, 0x3333);
assert_eq!(r1, 0x3456);
}
#[test]
fn test_arm_thumbeb_sub() {
let code = b"\xb0\x83"; // sub sp, #0xc
let sp = 0x1234;
let mut uc = uc_common_setup(
Arch::ARM,
Mode::THUMB | Mode::BIG_ENDIAN,
Some(ArmCpuModel::Model_1176 as i32),
code,
(),
);
uc.reg_write(RegisterARM::SP, sp).unwrap();
uc.emu_start(CODE_START | 1, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let sp = uc.reg_read(RegisterARM::SP).unwrap();
assert_eq!(sp, 0x1228);
}
#[test]
fn test_arm_thumb_ite() {
let code = &[
0x9a, 0x42, // cmp r2, r3
0x15, 0xbf, // itete ne
0x00, 0x9a, // ldrne r2, [sp]
0x01, 0x9a, // ldreq r2, [sp,#4]
0x78, 0x23, // movne r3, #0x78
0x15, 0x23, // moveq r3, #0x15
];
let sp = 0x8000;
let mut r2 = 0u32;
let mut r3 = 1u32;
let mut pc = CODE_START as u32;
let mut count = 0;
let mut uc = uc_common_setup(
Arch::ARM,
Mode::THUMB,
Some(ArmCpuModel::CORTEX_A15 as i32),
code,
count,
);
uc.reg_write(RegisterARM::SP, sp).unwrap();
uc.reg_write(RegisterARM::R2, r2 as u64).unwrap();
uc.reg_write(RegisterARM::R3, r3 as u64).unwrap();
uc.mem_map(sp, 0x1000, Prot::ALL).unwrap();
r2 = 0x68;
uc.mem_write(sp, &r2.to_le_bytes()).unwrap();
r2 = 0x4d;
uc.mem_write(sp + 4, &r2.to_le_bytes()).unwrap();
uc.add_code_hook(CODE_START, CODE_START + code.len() as u64, |uc, _, _| {
*uc.get_data_mut() += 1;
})
.unwrap();
// Execute four instructions
uc.emu_start(CODE_START | 1, CODE_START + code.len() as u64, 0, 0)
.unwrap();
r2 = uc.reg_read(RegisterARM::R2).unwrap() as u32;
r3 = uc.reg_read(RegisterARM::R3).unwrap() as u32;
count = *uc.get_data();
assert_eq!(r2, 0x68);
assert_eq!(r3, 0x78);
assert_eq!(count, 4);
r2 = 0;
*uc.get_data_mut() = 0;
uc.reg_write(RegisterARM::R2, r2 as u64).unwrap();
uc.reg_write(RegisterARM::R3, r3 as u64).unwrap();
for _ in 0..6 {
// Execute one instruction at a time.
uc.emu_start(pc as u64 | 1, CODE_START + code.len() as u64, 0, 1)
.unwrap();
pc = uc.reg_read(RegisterARM::PC).unwrap() as u32;
}
r2 = uc.reg_read(RegisterARM::R2).unwrap() as u32;
r3 = uc.reg_read(RegisterARM::R3).unwrap() as u32;
count = *uc.get_data();
assert_eq!(r2, 0x68);
assert_eq!(r3, 0x78);
assert_eq!(count, 4);
}
#[test]
fn test_arm_m_thumb_mrs() {
let code = &[
0xef, 0xf3, 0x14, 0x80, // mrs r0, control
0xef, 0xf3, 0x00, 0x81, // mrs r1, apsr
];
let control = 0b10;
let apsr = (0b10101 << 27) as u32;
let mut uc = uc_common_setup(
Arch::ARM,
Mode::THUMB | Mode::MCLASS,
Some(ArmCpuModel::CORTEX_A15 as i32),
code,
(),
);
uc.reg_write(RegisterARM::CONTROL, control as u64).unwrap();
uc.reg_write(RegisterARM::APSR_NZCVQ, apsr as u64).unwrap();
uc.emu_start(CODE_START | 1, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let r0 = uc.reg_read(RegisterARM::R0).unwrap() as u32;
let r1 = uc.reg_read(RegisterARM::R1).unwrap() as u32;
assert_eq!(r0, 0b10);
assert_eq!(r1, 0b10101 << 27);
}
#[test]
fn test_arm_m_control() {
let mut uc = Unicorn::new(Arch::ARM, Mode::THUMB | Mode::MCLASS).unwrap();
let mut control = 0;
uc.reg_write(RegisterARM::CONTROL, control as u64).unwrap();
let msp = 0x1000;
uc.reg_write(RegisterARM::R13, msp as u64).unwrap();
control = 0b10;
uc.reg_write(RegisterARM::CONTROL, control as u64).unwrap();
let psp = uc.reg_read(RegisterARM::R13).unwrap() as u32;
assert_ne!(psp, msp);
let psp = 0x2000;
uc.reg_write(RegisterARM::R13, psp as u64).unwrap();
control = 0;
uc.reg_write(RegisterARM::CONTROL, control as u64).unwrap();
let msp = uc.reg_read(RegisterARM::R13).unwrap() as u32;
assert_ne!(psp, msp);
assert_eq!(msp, 0x1000);
}
// NOTE:
// QEMU raise a special exception [`EXCP_EXCEPTION_EXIT`] to handle the
// [`EXC_RETURN`]. We can't help user handle [`EXC_RETURN`] since unicorn is designed
// not to handle any CPU exception.
#[test]
fn test_arm_m_exc_return() {
let code = b"\x6f\xf0\x02\x00\x00\x47"; // mov r0, #0xFFFFFFFD; bx r0;
let ipsr = 16;
let mut sp = 0x8000;
let mut uc = uc_common_setup(
Arch::ARM,
Mode::THUMB | Mode::MCLASS,
Some(ArmCpuModel::CORTEX_A15 as i32),
code,
(),
);
uc.mem_map(sp - 0x1000, 0x1000, Prot::ALL).unwrap();
uc.add_intr_hook(|uc, intno| {
let pc = uc.reg_read(RegisterARM::PC).unwrap() as u32;
assert_eq!(intno, 8); // EXCP_EXCEPTION_EXIT: Return from v7M exception.
assert_eq!(pc | 1, 0xFFFFFFFD);
uc.emu_stop().unwrap();
})
.unwrap();
sp -= 0x1c;
uc.reg_write(RegisterARM::SP, sp).unwrap();
uc.reg_write(RegisterARM::IPSR, ipsr as u64).unwrap();
uc.emu_start(CODE_START | 1, CODE_START + code.len() as u64, 0, 2)
.unwrap();
}
/// For details, see <https://github.com/unicorn-engine/unicorn/issues/1494>.
#[test]
fn test_arm_und32_to_svc32() {
let code = &[
0x00, 0x00, 0xe0, 0xe3, // MVN r0, #0
0x0e, 0xf0, 0xb0, 0xe1, // MOVS pc, lr
0x00, 0x00, 0xe0, 0xe3, // MVN r0, #0
0x00, 0x00, 0xe0, 0xe3, // MVN r0, #0
];
let cpsr = 0x40000093; // SVC32
let sp = 0x12345678;
let spsr = 0x40000093; // Save previous CPSR
let lr = CODE_START + 8;
let mut uc = uc_common_setup(
Arch::ARM,
Mode::ARM,
Some(ArmCpuModel::CORTEX_A9 as i32),
code,
(),
);
uc.reg_write(RegisterARM::CPSR, cpsr as u64).unwrap();
uc.reg_write(RegisterARM::SP, sp as u64).unwrap();
let cpsr = 0x4000009b; // UND32
uc.reg_write(RegisterARM::CPSR, cpsr as u64).unwrap();
uc.reg_write(RegisterARM::SPSR, spsr as u64).unwrap();
uc.reg_write(RegisterARM::SP, 0xDEAD0000).unwrap();
uc.reg_write(RegisterARM::LR, lr).unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 3)
.unwrap();
let sp = uc.reg_read(RegisterARM::SP).unwrap() as u32;
assert_eq!(sp, 0x12345678);
}
#[test]
fn test_arm_usr32_to_svc32() {
let mut uc = Unicorn::new(Arch::ARM, Mode::ARM).unwrap();
uc.ctl_set_cpu_model(ArmCpuModel::CORTEX_A9 as i32).unwrap();
// https://www.keil.com/pack/doc/CMSIS/Core_A/html/group__CMSIS__CPSR__M.html
let mut cpsr = 0x40000093; // SVC32
let mut sp = 0x12345678;
let mut lr = 0x00102220;
uc.reg_write(RegisterARM::CPSR, cpsr as u64).unwrap();
uc.reg_write(RegisterARM::SP, sp as u64).unwrap();
uc.reg_write(RegisterARM::LR, lr as u64).unwrap();
cpsr = 0x4000009b; // UND32
uc.reg_write(RegisterARM::CPSR, cpsr as u64).unwrap();
let spsr = 0x40000093; // Save previous CPSR
uc.reg_write(RegisterARM::SPSR, spsr as u64).unwrap();
sp = 0xDEAD0000;
uc.reg_write(RegisterARM::SP, sp as u64).unwrap();
lr = 0x00509998;
uc.reg_write(RegisterARM::LR, lr as u64).unwrap();
cpsr = uc.reg_read(RegisterARM::CPSR).unwrap() as u32;
assert_eq!(cpsr & ((1 << 4) - 1), 0xb); // We are in UND32
cpsr = 0x40000090; // USR32
uc.reg_write(RegisterARM::CPSR, cpsr as u64).unwrap();
sp = 0x0010000;
uc.reg_write(RegisterARM::R13, sp as u64).unwrap();
lr = 0x0001234;
uc.reg_write(RegisterARM::LR, lr as u64).unwrap();
cpsr = uc.reg_read(RegisterARM::CPSR).unwrap() as u32;
assert_eq!(cpsr & ((1 << 4) - 1), 0); // We are in USR32
cpsr = 0x40000093; // SVC32
uc.reg_write(RegisterARM::CPSR, cpsr as u64).unwrap();
cpsr = uc.reg_read(RegisterARM::CPSR).unwrap() as u32;
sp = uc.reg_read(RegisterARM::SP).unwrap() as u32;
assert_eq!(cpsr & ((1 << 4) - 1), 3); // We are in SVC32
assert_eq!(sp, 0x12345678);
}
#[test]
fn test_arm_v8() {
let code = b"\xd0\xe8\xff\x17"; // LDAEXD.W R1, [R0]
let r0 = 0x8000;
let r1 = 0xdeadbeefu32;
let mut uc = uc_common_setup(
Arch::ARM,
Mode::THUMB,
Some(ArmCpuModel::CORTEX_M33 as i32),
code,
(),
);
uc.mem_map(r0, 0x1000, Prot::ALL).unwrap();
uc.mem_write(r0, &r1.to_le_bytes()).unwrap();
uc.reg_write(RegisterARM::R0, r0).unwrap();
uc.emu_start(CODE_START | 1, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let r1 = uc.reg_read(RegisterARM::R1).unwrap() as u32;
assert_eq!(r1, 0xdeadbeef);
}
#[test]
fn test_arm_thumb_smlabb() {
let code = b"\x13\xfb\x01\x23";
let r1 = 7;
let r2 = 9;
let r3 = 5;
let mut uc = uc_common_setup(
Arch::ARM,
Mode::THUMB,
Some(ArmCpuModel::CORTEX_M7 as i32),
code,
(),
);
uc.reg_write(RegisterARM::R1, r1 as u64).unwrap();
uc.reg_write(RegisterARM::R2, r2 as u64).unwrap();
uc.reg_write(RegisterARM::R3, r3 as u64).unwrap();
uc.emu_start(CODE_START | 1, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let r3 = uc.reg_read(RegisterARM::R3).unwrap() as u32;
assert_eq!(r3, 5 * 7 + 9);
}
#[test]
fn test_arm_not_allow_privilege_escalation() {
#[rustfmt::skip]
let code = &[
0x1f, 0x60, 0xc6, 0xe3, // BIC r6, r6, #&1F
0x13, 0x60, 0x86, 0xe3, // ORR r6, r6, #&13
0x06, 0xf0, 0x21, 0xe1, // MSR cpsr_c, r6 ; switch to SVC32 (should be ineffective from USR32)
0x00, 0x00, 0xa0, 0xe1, // MOV r0,r0
0x11, 0x00, 0x00, 0xef, // SWI OS_Exit
];
let cpsr = 0x40000013; // SVC32
let sp = 0x12345678;
let lr = 0x00102220;
let mut uc = uc_common_setup(
Arch::ARM,
Mode::ARM,
Some(ArmCpuModel::CORTEX_A15 as i32),
code,
(),
);
uc.reg_write(RegisterARM::CPSR, cpsr as u64).unwrap();
uc.reg_write(RegisterARM::SP, sp as u64).unwrap();
uc.reg_write(RegisterARM::LR, lr as u64).unwrap();
let cpsr = 0x40000010; // USR32
uc.reg_write(RegisterARM::CPSR, cpsr as u64).unwrap();
let sp = 0x0010000;
uc.reg_write(RegisterARM::SP, sp as u64).unwrap();
let lr = 0x0001234;
uc.reg_write(RegisterARM::LR, lr as u64).unwrap();
let err = uc
.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap_err();
assert_eq!(err, uc_error::EXCEPTION);
let sp = uc.reg_read(RegisterARM::SP).unwrap() as u32;
let lr = uc.reg_read(RegisterARM::LR).unwrap() as u32;
let cpsr = uc.reg_read(RegisterARM::CPSR).unwrap() as u32;
assert_eq!(cpsr & ((1 << 4) - 1), 0); // Stay in USR32
assert_eq!(lr, 0x1234);
assert_eq!(sp, 0x10000);
}
#[test]
fn test_arm_mrc() {
let code = b"\x1d\xee\x70\x1f"; // mrc p15, #0, r1, c13, c0, #3
let mut uc = uc_common_setup(Arch::ARM, Mode::THUMB, None, code, ());
uc.emu_start(CODE_START | 1, CODE_START + code.len() as u64, 0, 0)
.unwrap();
}
#[test]
fn test_arm_hflags_rebuilt() {
let code = &[
0x00, 0x60, 0x0f, 0xe1, // MRS r6, apsr
0x1f, 0x60, 0xc6, 0xe3, // BIC r6, r6, #&1F
0x10, 0x60, 0x86, 0xe3, // ORR r6, r6, #&10
0x06, 0xf0, 0x21, 0xe1, // MSR cpsr_c, r6
0x16, 0x00, 0x02, 0xef, // SWI OS_EnterOS
0x06, 0xf0, 0x21, 0xe1, // MSR cpsr_c, r6
];
let cpsr = 0x40000013; // SVC32
let sp = 0x12345678;
let lr = 0x00102220;
let mut uc = uc_common_setup(
Arch::ARM,
Mode::ARM,
Some(ArmCpuModel::CORTEX_A9 as i32),
code,
(),
);
uc.reg_write(RegisterARM::CPSR, cpsr as u64).unwrap();
uc.reg_write(RegisterARM::SP, sp as u64).unwrap();
uc.reg_write(RegisterARM::LR, lr as u64).unwrap();
let cpsr = 0x40000010; // USR32
uc.reg_write(RegisterARM::CPSR, cpsr as u64).unwrap();
let sp = 0x0010000;
uc.reg_write(RegisterARM::R13, sp as u64).unwrap();
let lr = 0x0001234;
uc.reg_write(RegisterARM::R14, lr as u64).unwrap();
let err = uc
.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap_err();
assert_eq!(err, uc_error::EXCEPTION);
let cpsr = 0x60000013;
uc.reg_write(RegisterARM::CPSR, cpsr as u64).unwrap();
let cpsr = 0x60000010;
uc.reg_write(RegisterARM::CPSR, cpsr as u64).unwrap();
let cpsr = 0x60000013;
uc.reg_write(RegisterARM::CPSR, cpsr as u64).unwrap();
let pc = uc.reg_read(RegisterARM::PC).unwrap();
uc.emu_start(pc, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let cpsr = uc.reg_read(RegisterARM::CPSR).unwrap() as u32;
let sp = uc.reg_read(RegisterARM::R13).unwrap() as u32;
let lr = uc.reg_read(RegisterARM::R14).unwrap() as u32;
assert_eq!(cpsr, 0x60000010);
assert_eq!(sp, 0x00010000);
assert_eq!(lr, 0x00001234);
}
#[test]
fn test_arm_mem_access_abort() {
let code = &[
0x00, 0x00, 0x90, 0xe5, // LDR r0, [r0]
0x00, 0xa0, 0xf0, 0xf7, // Undefined instruction
];
let r0 = 0x990000;
let mut uc = uc_common_setup(
Arch::ARM,
Mode::ARM,
Some(ArmCpuModel::CORTEX_A9 as i32),
code,
0,
);
uc.reg_write(RegisterARM::R0, r0 as u64).unwrap();
uc.add_mem_hook(HookType::MEM_UNMAPPED, 1, 0, |uc, _, _, _, _| {
*uc.get_data_mut() = uc.reg_read(RegisterARM::PC).unwrap();
false
})
.unwrap();
uc.add_insn_invalid_hook(|uc| {
*uc.get_data_mut() = uc.reg_read(RegisterARM::PC).unwrap();
false
})
.unwrap();
let err = uc.emu_start(CODE_START, CODE_START + 4, 0, 0).unwrap_err();
assert_eq!(err, uc_error::READ_UNMAPPED);
let pc = uc.reg_read(RegisterARM::PC).unwrap();
assert_eq!(pc, *uc.get_data());
let err = uc
.emu_start(CODE_START + 4, CODE_START + 8, 0, 0)
.unwrap_err();
assert_eq!(err, uc_error::INSN_INVALID);
let pc = uc.reg_read(RegisterARM::PC).unwrap();
assert_eq!(pc, *uc.get_data());
let err = uc.emu_start(0x900000, 0x900000 + 8, 0, 0).unwrap_err();
assert_eq!(err, uc_error::FETCH_UNMAPPED);
let pc = uc.reg_read(RegisterARM::PC).unwrap();
assert_eq!(pc, *uc.get_data());
}
#[test]
fn test_arm_read_sctlr() {
let uc = Unicorn::new(Arch::ARM, Mode::ARM).unwrap();
let mut reg = RegisterARM_CP {
cp: 15,
is64: 0,
sec: 0,
crn: 1,
crm: 0,
opc1: 0,
opc2: 0,
val: 0,
};
uc.reg_read_arm_coproc(&mut reg).unwrap();
assert_eq!((reg.val >> 31) & 1, 0);
}
#[test]
fn test_arm_be_cpsr_sctlr() {
let mut uc = Unicorn::new(Arch::ARM, Mode::ARM | Mode::BIG_ENDIAN).unwrap();
uc.ctl_set_cpu_model(ArmCpuModel::Model_1176 as i32)
.unwrap();
let mut reg = RegisterARM_CP {
cp: 15,
is64: 0,
sec: 0,
crn: 1,
crm: 0,
opc1: 0,
opc2: 0,
val: 0,
};
uc.reg_read_arm_coproc(&mut reg).unwrap();
let cpsr = uc.reg_read(RegisterARM::CPSR).unwrap();
assert_ne!(reg.val & (1 << 7), 0);
assert_ne!(cpsr & (1 << 9), 0);
let mut uc = Unicorn::new(Arch::ARM, Mode::ARM | Mode::ARMBE8).unwrap();
uc.ctl_set_cpu_model(ArmCpuModel::CORTEX_A15 as i32)
.unwrap();
let mut reg = RegisterARM_CP {
cp: 15,
is64: 0,
sec: 0,
crn: 1,
crm: 0,
opc1: 0,
opc2: 0,
val: 0,
};
uc.reg_read_arm_coproc(&mut reg).unwrap();
let cpsr = uc.reg_read(RegisterARM::CPSR).unwrap();
// SCTLR.B == 0
assert_eq!(reg.val & (1 << 7), 0);
assert_ne!(cpsr & (1 << 9), 0);
}
#[test]
fn test_arm_switch_endian() {
let code = b"\x00\x00\x91\xe5"; // ldr r0, [r1]
let mut uc = uc_common_setup(
Arch::ARM,
Mode::ARM,
Some(ArmCpuModel::CORTEX_A15 as i32),
code,
(),
);
let r1 = CODE_START;
uc.reg_write(RegisterARM::R1, r1).unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let r0 = uc.reg_read(RegisterARM::R0).unwrap();
assert_eq!(r0, 0xe5910000);
let mut cpsr = uc.reg_read(RegisterARM::CPSR).unwrap();
cpsr |= 1 << 9;
uc.reg_write(RegisterARM::CPSR, cpsr).unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let r0 = uc.reg_read(RegisterARM::R0).unwrap();
assert_eq!(r0, 0x000091e5);
}
#[test]
fn test_armeb_ldrb() {
let code = b"\xe5\xd2\x10\x00"; // ldrb r1, [r2]
let data_address = 0x800000;
let mut uc = uc_common_setup(
Arch::ARM,
Mode::ARM | Mode::BIG_ENDIAN,
Some(ArmCpuModel::Model_1176 as i32),
code,
(),
);
uc.mem_map(data_address, 1024 * 1024, Prot::ALL).unwrap();
uc.mem_write(data_address, b"\x66\x67\x68\x69").unwrap();
let mut r2 = data_address;
uc.reg_write(RegisterARM::R2, r2).unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let r1 = uc.reg_read(RegisterARM::R1).unwrap();
assert_eq!(r1, 0x66);
r2 += 1;
uc.reg_write(RegisterARM::R2, r2).unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let r1 = uc.reg_read(RegisterARM::R1).unwrap();
assert_eq!(r1, 0x67);
r2 += 1;
uc.reg_write(RegisterARM::R2, r2).unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let r1 = uc.reg_read(RegisterARM::R1).unwrap();
assert_eq!(r1, 0x68);
r2 += 1;
uc.reg_write(RegisterARM::R2, r2).unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let r1 = uc.reg_read(RegisterARM::R1).unwrap();
assert_eq!(r1, 0x69);
}
#[test]
fn test_arm_context_save() {
let code = b"\x83\xb0"; // sub sp, #0xc
let uc = uc_common_setup(
Arch::ARM,
Mode::THUMB,
Some(ArmCpuModel::CORTEX_R5 as i32),
code,
(),
);
let mut context = uc.context_alloc().unwrap();
uc.context_save(&mut context).unwrap();
let pc = context.reg_read(RegisterARM::PC).unwrap();
context.reg_write(RegisterARM::PC, pc).unwrap();
uc.context_restore(&context).unwrap();
let uc2 = uc_common_setup(
Arch::ARM,
Mode::THUMB,
Some(ArmCpuModel::CORTEX_A7 as i32), // NOTE: different CPU model
code,
(),
);
uc2.context_restore(&context).unwrap();
let pc2 = uc2.reg_read(RegisterARM::PC).unwrap();
assert_eq!(pc, pc2);
}
#[test]
fn test_arm_thumb2() {
#[rustfmt::skip]
let code = &[
0x24, 0x20, // MOVS R0, #0x24
0x00, 0xF0, 0x04, 0x00, // AND.W R0, R0, #4
];
let mut uc = uc_common_setup(
Arch::ARM,
Mode::THUMB | Mode::LITTLE_ENDIAN,
Some(ArmCpuModel::CORTEX_R5 as i32),
code,
(),
);
uc.emu_start(CODE_START | 1, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let r0 = uc.reg_read(RegisterARM::R0).unwrap();
assert_eq!(r0, 0x4);
}
#[test]
fn test_armeb_be32_thumb2() {
#[rustfmt::skip]
let code = &[
0x20, 0x24, // MOVS R0, #0x24
0xF0, 0x00, 0x00, 0x04, // AND.W R0, R0, #4
];
let mut uc = uc_common_setup(
Arch::ARM,
Mode::THUMB | Mode::BIG_ENDIAN,
Some(ArmCpuModel::CORTEX_R5 as i32),
code,
(),
);
uc.emu_start(CODE_START | 1, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let r0 = uc.reg_read(RegisterARM::R0).unwrap();
assert_eq!(r0, 0x4);
}
#[test]
fn test_arm_mem_hook_read_write() {
#[rustfmt::skip]
let code = &[
0x00, 0x10, 0x9d, 0xe5, // ldr r1, [sp]
0x04, 0x10, 0x8d, 0xe5, // str r1, [sp, #4]
0x04, 0x20, 0x9d, 0xe5, // ldr r2, [sp, #4]
0x00, 0x20, 0x8d, 0xe5, // str r2, [sp]
];
let sp = 0x9000;
let mut uc = uc_common_setup(
Arch::ARM,
Mode::ARM,
Some(ArmCpuModel::CORTEX_A15 as i32),
code,
[0u64; 2],
);
uc.reg_write(RegisterARM::SP, sp).unwrap();
uc.mem_map(0x8000, 1024 * 16, Prot::ALL).unwrap();
uc.add_mem_hook(HookType::MEM_READ, 1, 0, |uc, _, _, _, _| {
(*uc.get_data_mut())[0] += 1;
false
})
.unwrap();
uc.add_mem_hook(HookType::MEM_WRITE, 1, 0, |uc, _, _, _, _| {
(*uc.get_data_mut())[1] += 1;
false
})
.unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let [read, write] = *uc.get_data();
assert_eq!(read, 2);
assert_eq!(write, 2);
}
#[derive(Default)]
struct CmpInfo {
v0: u64,
v1: u64,
size: u64,
pc: u64,
}
fn uc_hook_sub_cmp(uc: &mut Unicorn<'_, CmpInfo>, address: u64, arg1: u64, arg2: u64, size: usize) {
let data = uc.get_data_mut();
data.pc = address;
data.size = size as u64;
data.v0 = arg1;
data.v1 = arg2;
}
#[test]
fn test_arm_tcg_opcode_cmp() {
let code = &[
0x04, 0x00, 0x9f, 0xe5, // ldr r0, [pc, #4]
0x04, 0x10, 0x9f, 0xe5, // ldr r1, [pc, #4]
0x01, 0x00, 0x50, 0xe1, // cmp r0, r1
0x05, 0x00, 0x00, 0x00, // (5)
0x03, 0x00, 0x00, 0x00, // (3)
];
let mut uc = uc_common_setup(
Arch::ARM,
Mode::ARM,
Some(ArmCpuModel::CORTEX_A15 as i32),
code,
CmpInfo::default(),
);
uc.add_tcg_hook(TcgOpCode::SUB, TcgOpFlag::CMP, 1, 0, uc_hook_sub_cmp)
.unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 3)
.unwrap();
let cmp_info = uc.get_data();
assert_eq!(cmp_info.v0, 5);
assert_eq!(cmp_info.v1, 3);
assert_eq!(cmp_info.pc, 0x1008);
assert_eq!(cmp_info.size, 32);
}
#[test]
fn test_arm_thumb_tcg_opcode_cmn() {
#[rustfmt::skip]
let code = &[
0x01, 0x48, // ldr r0, [pc #4]
0x02, 0x49, // ldr r1, [pc #8]
0x00, 0xbf, // nop
0xc8, 0x42, // cmn r0, r1
0x05, 0x00, 0x00, 0x00, // (5)
0x03, 0x00, 0x00, 0x00, // (3)
];
let mut uc = uc_common_setup(
Arch::ARM,
Mode::THUMB,
Some(ArmCpuModel::CORTEX_A15 as i32),
code,
CmpInfo::default(),
);
uc.add_tcg_hook(TcgOpCode::SUB, TcgOpFlag::CMP, 1, 0, uc_hook_sub_cmp)
.unwrap();
uc.emu_start(CODE_START | 1, CODE_START + code.len() as u64, 0, 4)
.unwrap();
let cmp_info = uc.get_data();
assert_eq!(cmp_info.v0, 5);
assert_eq!(cmp_info.v1, 3);
assert_eq!(cmp_info.pc, 0x1006);
assert_eq!(cmp_info.size, 32);
}
#[test]
fn test_arm_cp15_c1_c0_2() {
let val = 0x12345678;
// Initialize emulator in ARM mode
let mut uc = Unicorn::new(Arch::ARM, Mode::ARM).unwrap();
uc.ctl_set_cpu_model(ArmCpuModel::CORTEX_A15 as i32)
.unwrap();
// Write to CP15 C1_C0_2
uc.reg_write(RegisterARM::C1_C0_2, val).unwrap();
// Read from CP15 C1_C0_2
let read_val = uc.reg_read(RegisterARM::C1_C0_2).unwrap();
assert_eq!(val, read_val);
}

View File

@@ -0,0 +1,542 @@
use unicorn_engine_sys::{Arm64CpuModel, Arm64Insn, RegisterARM64};
use super::*;
#[test]
fn test_arm64_until() {
let code = &[
0x30, 0x00, 0x80, 0xd2, // mov x16, #1
0x11, 0x04, 0x80, 0xd2, // mov x17, #0x20
0x9c, 0x23, 0x00, 0x91, // add x28, x28, 8
];
let mut uc = uc_common_setup(
Arch::ARM64,
Mode::ARM,
Some(Arm64CpuModel::A72 as i32),
code,
(),
);
let x16 = 0x12341234;
let x17 = 0x78907890;
let x28 = 0x12341234;
uc.reg_write(RegisterARM64::X16, x16).unwrap();
uc.reg_write(RegisterARM64::X17, x17).unwrap();
uc.reg_write(RegisterARM64::X28, x28).unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 3)
.unwrap();
let x16 = uc.reg_read(RegisterARM64::X16).unwrap();
let x17 = uc.reg_read(RegisterARM64::X17).unwrap();
let x28 = uc.reg_read(RegisterARM64::X28).unwrap();
let pc = uc.reg_read(RegisterARM64::PC).unwrap();
assert_eq!(x16, 0x1);
assert_eq!(x17, 0x20);
assert_eq!(x28, 0x1234123c);
assert_eq!(pc, CODE_START + code.len() as u64);
}
#[test]
fn test_arm64_code_patching() {
let code = &[0x00, 0x04, 0x00, 0x11]; // add w0, w0, 0x1
let mut uc = uc_common_setup(
Arch::ARM64,
Mode::ARM,
Some(Arm64CpuModel::A72 as i32),
code,
(),
);
let x0 = 0x0;
uc.reg_write(RegisterARM64::X0, x0).unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let x0 = uc.reg_read(RegisterARM64::X0).unwrap();
assert_eq!(x0, 0x1);
let patch_code = &[0x00, 0xfc, 0x1f, 0x11]; // add w0, w0, 0x7FF
uc.mem_write(CODE_START, patch_code).unwrap();
let x0 = 0x0;
uc.reg_write(RegisterARM64::X0, x0).unwrap();
uc.emu_start(CODE_START, CODE_START + patch_code.len() as u64, 0, 0)
.unwrap();
let x0 = uc.reg_read(RegisterARM64::X0).unwrap();
assert_ne!(x0, 0x1);
assert_eq!(x0, 0x7ff);
}
/// Need to flush the cache before running the emulation after patching
#[test]
fn test_arm64_code_patching_count() {
let code = &[0x00, 0x04, 0x00, 0x11]; // add w0, w0, 0x1
let mut uc = uc_common_setup(
Arch::ARM64,
Mode::ARM,
Some(Arm64CpuModel::A72 as i32),
code,
(),
);
let x0 = 0x0;
uc.reg_write(RegisterARM64::X0, x0).unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 1)
.unwrap();
let x0 = uc.reg_read(RegisterARM64::X0).unwrap();
assert_eq!(x0, 0x1);
let patch_code = &[0x00, 0xfc, 0x1f, 0x11]; // add w0, w0, 0x7FF
uc.mem_write(CODE_START, patch_code).unwrap();
uc.ctl_remove_cache(CODE_START, CODE_START + patch_code.len() as u64)
.unwrap();
let x0 = 0x0;
uc.reg_write(RegisterARM64::X0, x0).unwrap();
uc.emu_start(CODE_START, CODE_START + patch_code.len() as u64, 0, 1)
.unwrap();
let x0 = uc.reg_read(RegisterARM64::X0).unwrap();
assert_ne!(x0, 0x1);
assert_eq!(x0, 0x7ff);
}
#[test]
fn test_arm64_v8_pac() {
let code = &[0x28, 0xfd, 0xea, 0xc8]; // casal x10, x8, [x9]
let mut uc = uc_common_setup(
Arch::ARM64,
Mode::ARM,
Some(Arm64CpuModel::MAX as i32),
code,
(),
);
uc.mem_map(0x40000, 0x1000, Prot::ALL).unwrap();
uc.mem_write(0x40000, &[0; 8]).unwrap();
let x9 = 0x40000;
uc.reg_write(RegisterARM64::X9, x9).unwrap();
let x8 = 0xdeadbeafdeadbeaf;
uc.reg_write(RegisterARM64::X8, x8).unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let mem = u64::from_le_bytes(uc.mem_read_as_vec(0x40000, 8).unwrap().try_into().unwrap());
assert_eq!(mem, x8);
}
#[test]
fn test_arm64_read_sctlr() {
let uc = Unicorn::new(Arch::ARM64, Mode::ARM | Mode::LITTLE_ENDIAN).unwrap();
let reg = uc.reg_read_arm64_coproc().unwrap();
assert_eq!(reg.val >> 58, 0);
}
#[test]
fn test_arm64_mrs_hook() {
let code = &[0x62, 0xd0, 0x3b, 0xd5]; // mrs x2, tpidrro_el0
let mut uc = uc_common_setup(
Arch::ARM64,
Mode::ARM | Mode::LITTLE_ENDIAN,
Some(Arm64CpuModel::A72 as i32),
code,
(),
);
uc.add_insn_sys_hook_arm64(Arm64Insn::UC_ARM64_INS_MRS, 1, 0, |uc, reg, _| {
let x2 = 0x114514;
uc.reg_write(reg, x2).unwrap();
// Skip
true
})
.unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let x2 = uc.reg_read(RegisterARM64::X2).unwrap();
assert_eq!(x2, 0x114514);
}
#[test]
fn test_arm64_correct_address_in_small_jump_hook() {
let code = &[
0x00, 0xe0, 0x8f, 0xd2, // mov x0, #0x7F00
0x00, 0x00, 0x1f, 0xd6, // br x0
];
let mut uc = uc_common_setup(
Arch::ARM64,
Mode::ARM,
Some(Arm64CpuModel::A72 as i32),
code,
(),
);
uc.add_mem_hook(HookType::MEM_UNMAPPED, 1, 0, |uc, _, address, _, _| {
let x0 = uc.reg_read(RegisterARM64::X0).unwrap();
let pc = uc.reg_read(RegisterARM64::PC).unwrap();
assert_eq!(x0, 0x7F00);
assert_eq!(pc, 0x7F00);
assert_eq!(address, 0x7F00);
false
})
.unwrap();
let err = uc
.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap_err();
assert_eq!(err, uc_error::FETCH_UNMAPPED);
let x0 = uc.reg_read(RegisterARM64::X0).unwrap();
let pc = uc.reg_read(RegisterARM64::PC).unwrap();
assert_eq!(x0, 0x7F00);
assert_eq!(pc, 0x7F00);
}
#[test]
fn test_arm64_correct_address_in_long_jump_hook() {
let code = &[0xe0, 0xdb, 0x78, 0xb2, 0x00, 0x00, 0x1f, 0xd6]; // mov x0, 0x7FFFFFFFFFFFFF00; br x0
let mut uc = uc_common_setup(
Arch::ARM64,
Mode::ARM,
Some(Arm64CpuModel::A72 as i32),
code,
(),
);
uc.add_mem_hook(HookType::MEM_UNMAPPED, 1, 0, |uc, _, address, _, _| {
let x0 = uc.reg_read(RegisterARM64::X0).unwrap();
let pc = uc.reg_read(RegisterARM64::PC).unwrap();
assert_eq!(x0, 0x7FFFFFFFFFFFFF00);
assert_eq!(pc, 0x7FFFFFFFFFFFFF00);
assert_eq!(address, 0x7FFFFFFFFFFFFF00);
false
})
.unwrap();
let err = uc
.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap_err();
assert_eq!(err, uc_error::FETCH_UNMAPPED);
let x0 = uc.reg_read(RegisterARM64::X0).unwrap();
let pc = uc.reg_read(RegisterARM64::PC).unwrap();
assert_eq!(x0, 0x7FFFFFFFFFFFFF00);
assert_eq!(pc, 0x7FFFFFFFFFFFFF00);
}
#[test]
fn test_arm64_block_sync_pc() {
let code = &[
0x00, 0x48, 0x13, 0x91, // add x0, x0, #1234
0x01, 0x00, 0x00, 0x94, // bl t
0xc1, 0xc5, 0x82, 0xd2, // t: mov x1, #5678
];
let mut uc = uc_common_setup(
Arch::ARM64,
Mode::ARM,
Some(Arm64CpuModel::A72 as i32),
code,
true,
);
uc.add_block_hook(CODE_START + 8, CODE_START + 12, |uc, addr, _| {
let pc = uc.reg_read(RegisterARM64::PC).unwrap();
assert_eq!(pc, addr);
let val = CODE_START;
let first = *uc.get_data_mut();
if first {
uc.reg_write(RegisterARM64::PC, val).unwrap();
*uc.get_data_mut() = false;
}
})
.unwrap();
let x0 = 0;
uc.reg_write(RegisterARM64::X0, x0).unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let x0 = uc.reg_read(RegisterARM64::X0).unwrap();
assert_eq!(x0, 1234 * 2);
}
#[test]
fn test_arm64_block_invalid_mem_read_write_sync() {
let code = &[
0x20, 0x00, 0x80, 0xd2, // mov x0, #1
0x41, 0x00, 0x80, 0xd2, // mov x1, #2
0x20, 0x00, 0x40, 0xf9, // ldr x0, [x1]
];
let mut uc = uc_common_setup(
Arch::ARM64,
Mode::ARM,
Some(Arm64CpuModel::A72 as i32),
code,
true,
);
uc.add_mem_hook(
HookType::MEM_READ,
CODE_START + 8,
CODE_START + 12,
|_, _, _, _, _| false,
)
.unwrap();
let err = uc
.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap_err();
assert_eq!(err, uc_error::READ_UNMAPPED);
let x0 = uc.reg_read(RegisterARM64::X0).unwrap();
let x1 = uc.reg_read(RegisterARM64::X1).unwrap();
assert_eq!(x0, 1);
assert_eq!(x1, 2);
}
#[test]
fn test_arm64_mmu() {
#[rustfmt::skip]
let code = &[
// Read data from physical address
0x00, 0x81, 0x00, 0x58, // ldr x0, =0x40000000
0x01, 0x00, 0x40, 0xf9, // ldr x1, [x0]
// Initialize translation table control registers
0x00, 0x81, 0x00, 0x58, // ldr x0, =0x180803F20
0x40, 0x20, 0x18, 0xd5, // msr TCR_EL1, x0
0x00, 0x81, 0x00, 0x58, // ldr x0, =0xFFFFFFFF
0x00, 0xa2, 0x18, 0xd5, // msr MAIR_EL1, x0
// Set translation table
0x40, 0x7f, 0x00, 0x10, // adr x0, ttb0_base
0x00, 0x20, 0x18, 0xd5, // msr TTBR0_EL1, x0
// Enable caches and the MMU
0x00, 0x10, 0x38, 0xd5, // mrs x0, SCTLR_EL1
0x00, 0x00, 0x7e, 0xb2, // orr x0, x0, #(0x1 << 2) // The C bit (data cache)
0x00, 0x00, 0x74, 0xb2, // orr x0, x0, #(0x1 << 12) // The I bit (instruction cache)
0x00, 0x00, 0x40, 0xb2, // orr x0, x0, #0x1
0x00, 0x10, 0x18, 0xd5, // msr SCTLR_EL1, x0
0x9f, 0x3f, 0x03, 0xd5, // dsb SY
0xdf, 0x3f, 0x03, 0xd5, // isb
// Read the same memory area through virtual address
0xe0, 0x7f, 0x00, 0x58, // ldr x0, =0x80000000
0x02, 0x00, 0x40, 0xf9, // ldr x2, [x0]
// Stop
0x00, 0x00, 0x00, 0x14, // b .
0x1f, 0x20, 0x03, 0xd5, // nop
0x1f, 0x20, 0x03, 0xd5, // nop
0x1f, 0x20, 0x03, 0xd5, // nop
0x1f, 0x20, 0x03, 0xd5, // nop
0x1f, 0x20, 0x03, 0xd5, // nop
];
let mut data = vec![0x44u8; 0x1000];
let mut tlbe = [0x41, 0x07, 0, 0, 0, 0, 0, 0];
let mut uc = Unicorn::new(Arch::ARM64, Mode::ARM).unwrap();
uc.ctl_set_tlb_type(unicorn_engine_sys::TlbType::CPU)
.unwrap();
uc.mem_map(0, 0x2000, Prot::ALL).unwrap();
uc.mem_write(0, code).unwrap();
uc.mem_write(0x1000, &tlbe).unwrap();
tlbe[3] = 0x40;
uc.mem_write(0x1008, &tlbe).unwrap();
uc.mem_write(0x1010, &tlbe).unwrap();
uc.mem_write(0x1018, &tlbe).unwrap();
tlbe[0] = 0;
tlbe[1] = 0;
uc.mem_write(0x1020, &tlbe).unwrap();
tlbe[0] = 0x20;
tlbe[1] = 0x3f;
tlbe[2] = 0x80;
tlbe[3] = 0x80;
tlbe[4] = 0x1;
uc.mem_write(0x1028, &tlbe).unwrap();
tlbe[0] = 0xff;
tlbe[1] = 0xff;
tlbe[2] = 0xff;
tlbe[3] = 0xff;
tlbe[4] = 0x00;
uc.mem_write(0x1030, &tlbe).unwrap();
tlbe[0] = 0;
tlbe[1] = 0;
tlbe[2] = 0;
tlbe[3] = 0x80;
uc.mem_write(0x1038, &tlbe).unwrap();
unsafe {
uc.mem_map_ptr(
0x40000000,
0x1000,
Prot::READ,
data.as_mut_ptr().cast::<core::ffi::c_void>(),
)
.unwrap();
}
uc.emu_start(0, 0x44, 0, 0).unwrap();
let x0 = uc.reg_read(RegisterARM64::X0).unwrap();
let x1 = uc.reg_read(RegisterARM64::X1).unwrap();
let x2 = uc.reg_read(RegisterARM64::X2).unwrap();
assert_eq!(x0, 0x80000000);
assert_eq!(x1, 0x4444444444444444);
assert_eq!(x2, 0x4444444444444444);
}
#[test]
fn test_arm64_pc_wrap() {
let code1 = &[0x20, 0x00, 0x02, 0x8b]; // add x1, x2, x3
let code2 = &[0x20, 0x00, 0x03, 0x8b]; // add x1, x3, x3
let mut uc = Unicorn::new(Arch::ARM64, Mode::ARM).unwrap();
uc.mem_map(0xFFFFFFFFFFFFF000, 4096, Prot::READ | Prot::EXEC)
.unwrap();
uc.mem_write(0xFFFFFFFFFFFFFFFC, code1).unwrap();
let x1 = 1;
let x2 = 2;
uc.reg_write(RegisterARM64::X1, x1).unwrap();
uc.reg_write(RegisterARM64::X2, x2).unwrap();
uc.emu_start(
0xFFFFFFFFFFFFFFFC,
0xFFFFFFFFFFFFFFFCu64.wrapping_add(4),
0,
1,
)
.unwrap();
uc.mem_unmap(0xFFFFFFFFFFFFF000, 4096).unwrap();
let x0 = uc.reg_read(RegisterARM64::X0).unwrap();
assert_eq!(x0, 1 + 2);
uc.mem_map(0xFFFFFFFFFFFFF000, 4096, Prot::READ | Prot::EXEC)
.unwrap();
uc.mem_write(0xFFFFFFFFFFFFFFFC, code2).unwrap();
let x1 = 5;
let x2 = 0;
let x3 = 5;
uc.reg_write(RegisterARM64::X1, x1).unwrap();
uc.reg_write(RegisterARM64::X2, x2).unwrap();
uc.reg_write(RegisterARM64::X3, x3).unwrap();
uc.emu_start(
0xFFFFFFFFFFFFFFFC,
0xFFFFFFFFFFFFFFFCu64.wrapping_add(4),
0,
1,
)
.unwrap();
uc.mem_unmap(0xFFFFFFFFFFFFF000, 4096).unwrap();
let x0 = uc.reg_read(RegisterARM64::X0).unwrap();
assert_eq!(x0, 5 + 5);
}
#[test]
fn test_arm64_mem_prot_regress() {
let code = &[0x08, 0x40, 0x5e, 0x78]; // ldurh w8, [x0, #-0x1c]
let mut uc = Unicorn::new(Arch::ARM64, Mode::ARM).unwrap();
uc.mem_map(0, 0x4000, Prot::READ | Prot::EXEC).unwrap();
uc.mem_map(0x4000, 0xC000, Prot::READ | Prot::WRITE)
.unwrap();
uc.mem_write(0, code).unwrap();
uc.add_mem_hook(
HookType::MEM_READ | HookType::MEM_WRITE,
1,
0,
|_, _, _, _, _| false,
)
.unwrap();
uc.add_mem_hook(HookType::MEM_PROT, 1, 0, |_, _, _, _, _| false)
.unwrap();
uc.add_mem_hook(HookType::MEM_UNMAPPED, 1, 0, |_, _, _, _, _| false)
.unwrap();
let value = 0x801b;
uc.reg_write(RegisterARM64::X0, value).unwrap();
uc.emu_start(0, code.len() as u64, 0, 0).unwrap();
}
#[test]
fn test_arm64_mem_hook_read_write() {
let code = &[
0xe1, 0x0b, 0x40, 0xa9, // ldp x1, x2, [sp]
0xe1, 0x0b, 0x00, 0xa9, // stp x1, x2, [sp]
0xe1, 0x0b, 0x40, 0xa9, // ldp x1, x2, [sp]
0xe1, 0x0b, 0x00, 0xa9, // stp x1, x2, [sp]
];
let mut uc = uc_common_setup(
Arch::ARM64,
Mode::ARM,
Some(Arm64CpuModel::A72 as i32),
code,
[0, 0],
);
let sp = 0x16db6a040;
uc.reg_write(RegisterARM64::SP, sp).unwrap();
uc.mem_map(0x16db68000, 1024 * 16, Prot::ALL).unwrap();
uc.add_mem_hook(HookType::MEM_READ, 1, 0, |uc, _, _, _, _| {
(*uc.get_data_mut())[0] += 1;
false
})
.unwrap();
uc.add_mem_hook(HookType::MEM_WRITE, 1, 0, |uc, _, _, _, _| {
(*uc.get_data_mut())[1] += 1;
false
})
.unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let [read, write] = *uc.get_data();
assert_eq!(read, 4);
assert_eq!(write, 4);
}

View File

@@ -0,0 +1,281 @@
use std::time::{Duration, Instant};
use unicorn_engine_sys::{RegisterX86, X86Insn};
use super::*;
use crate::Unicorn;
#[test]
fn test_uc_ctl_mode() {
let uc = Unicorn::new(Arch::X86, Mode::MODE_32).unwrap();
let mode = uc.ctl_get_mode().unwrap();
assert_eq!(mode, Mode::MODE_32);
}
#[test]
fn test_uc_ctl_arch() {
let uc = Unicorn::new(Arch::X86, Mode::MODE_32).unwrap();
let arch = uc.ctl_get_arch().unwrap();
assert_eq!(arch, Arch::X86);
}
#[test]
fn test_uc_ctl_page_size() {
let uc = Unicorn::new(Arch::X86, Mode::MODE_32).unwrap();
let page_size = uc.ctl_get_page_size().unwrap();
assert_eq!(page_size, 4096);
}
#[test]
fn test_uc_ctl_timeout() {
let uc = Unicorn::new(Arch::X86, Mode::MODE_32).unwrap();
let timeout = uc.ctl_get_timeout().unwrap();
assert_eq!(timeout, 0);
}
#[test]
fn test_uc_ctl_exits() {
// cmp eax, 0;
// jg lb;
// inc eax;
// nop; <---- exit1
// lb:
// inc ebx;
// nop; <---- exit2
let code = b"\x83\xf8\x00\x7f\x02\x40\x90\x43\x90";
let eax = 0;
let ebx = 0;
let exits = [CODE_START + 6, CODE_START + 8];
let mut uc = Unicorn::new(Arch::X86, Mode::MODE_32).unwrap();
uc.mem_map(CODE_START, CODE_LEN, Prot::ALL).unwrap();
uc.mem_write(CODE_START, code).unwrap();
uc.ctl_exits_enable().unwrap();
uc.ctl_set_exits(&exits).unwrap();
uc.reg_write(RegisterX86::EAX, eax).unwrap();
uc.reg_write(RegisterX86::EBX, ebx).unwrap();
// Run twice.
uc.emu_start(CODE_START, 0, 0, 0).unwrap();
uc.emu_start(CODE_START, 0, 0, 0).unwrap();
let eax = uc.reg_read(RegisterX86::EAX).unwrap();
let ebx = uc.reg_read(RegisterX86::EBX).unwrap();
assert_eq!(eax, 1);
assert_eq!(ebx, 1);
}
#[test]
fn test_uc_ctl_tb_cache() {
fn time_emulation(uc: &mut Unicorn<'_, ()>, start: u64, end: u64) -> Duration {
let now = Instant::now();
uc.emu_start(start, end, 0, 0).unwrap();
now.elapsed()
}
const TB_COUNT: usize = 8;
const TCG_MAX_INSNS: usize = 512;
const CODE_LEN: usize = TB_COUNT * TCG_MAX_INSNS;
let code = [0x90; CODE_LEN]; // nop
let mut uc = Unicorn::new(Arch::X86, Mode::MODE_32).unwrap();
uc.mem_map(CODE_START, CODE_LEN, Prot::ALL).unwrap();
uc.mem_write(CODE_START, &code).unwrap();
let standard = time_emulation(&mut uc, CODE_START, CODE_START + code.len() as u64);
for i in 0..TB_COUNT {
uc.ctl_request_cache(CODE_START + (i * TCG_MAX_INSNS) as u64, None)
.unwrap();
}
let cached = time_emulation(&mut uc, CODE_START, CODE_START + code.len() as u64);
for i in 0..TB_COUNT {
let start = CODE_START + (i * TCG_MAX_INSNS) as u64;
uc.ctl_remove_cache(start, start + 1).unwrap();
}
let evicted = time_emulation(&mut uc, CODE_START, CODE_START + code.len() as u64);
assert!(cached < standard);
assert!(evicted > cached);
}
#[cfg(feature = "arch_arm")]
#[test]
fn test_uc_ctl_change_page_size() {
let mut uc = Unicorn::new(Arch::ARM, Mode::ARM).unwrap();
let mut uc2 = Unicorn::new(Arch::ARM, Mode::ARM).unwrap();
uc2.ctl_set_page_size(4096).unwrap();
let page_size = uc2.ctl_get_page_size().unwrap();
assert_eq!(page_size, 4096);
// Mapping at 0x400 (1024) should succeed for the first Unicorn instance,
// but fail for the second instance since the page size is different.
// (Note: ARM's default page size is 1024)
assert!(uc.mem_map(1 << 10, 1 << 10, Prot::ALL).is_ok());
assert!(uc2.mem_map(1 << 10, 1 << 10, Prot::ALL).is_err());
}
#[cfg(feature = "arch_arm")]
#[test]
fn test_uc_ctl_arm_cpu() {
use unicorn_engine_sys::ArmCpuModel;
let mut uc = Unicorn::new(Arch::ARM, Mode::THUMB).unwrap();
uc.ctl_set_cpu_model(ArmCpuModel::CORTEX_M7 as i32).unwrap();
}
#[cfg(feature = "arch_arm")]
#[test]
fn test_uc_ctl_change_page_size_arm64() {
let mut uc = Unicorn::new(Arch::ARM64, Mode::ARM).unwrap();
let mut uc2 = Unicorn::new(Arch::ARM64, Mode::ARM).unwrap();
uc2.ctl_set_page_size(16384).unwrap();
let page_size = uc2.ctl_get_page_size().unwrap();
assert_eq!(page_size, 16384);
// Mapping at 0x400 (1024) should succeed for the first Unicorn instance,
// but fail for the second instance since the page size is different.
// (Note: ARM64's default page size is 1024)
assert!(uc.mem_map(1 << 10, 1 << 10, Prot::ALL).is_ok());
assert!(uc2.mem_map(1 << 10, 1 << 10, Prot::ALL).is_err());
}
#[test]
fn test_uc_hook_cached_uaf() {
let code = b"\x41\x4a\xeb\x00\x90";
let mut uc = Unicorn::new_with_data(Arch::X86, Mode::MODE_32, 0u64).unwrap();
uc.mem_map(CODE_START, CODE_LEN, Prot::ALL).unwrap();
uc.mem_write(CODE_START, code).unwrap();
let hook = uc
.add_code_hook(CODE_START, CODE_START + code.len() as u64, |uc, _, _| {
*uc.get_data_mut() += 1;
})
.unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
// Move the hook to the deleted hooks list.
uc.remove_hook(hook).unwrap();
// This will clear deleted hooks and SHOULD clear cache.
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
// Now hooks are deleted and thus this _should not_ call
// test_uc_hook_cached_cb anymore. If the hook is allocated like from
// malloc, and the code region is free-ed, this call _shall not_ call the
// hook anymore to avoid UAF.
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
// Only 4 calls
assert_eq!(*uc.get_data(), 4);
}
#[test]
fn test_uc_emu_stop_set_ip() {
#[rustfmt::skip]
let code = &[
0x48, 0x31, 0xc0, // 0x0 xor rax, rax : rax = 0
0x90, // 0x3 nop :
0x48, 0xff, 0xc0, // 0x4 inc rax : rax++
0x90, // 0x7 nop : <-- going to stop here
0x48, 0xff, 0xc0, // 0x8 inc rax : rax++
0x90, // 0xb nop :
0x0f, 0x0b, // 0xc ud2 : <-- will raise UC_ERR_INSN_INVALID,
// : but should not never be reached
0x90, // 0xe nop :
0x90, // 0xf nop :
];
let mut uc = Unicorn::new(Arch::X86, Mode::MODE_64).unwrap();
uc.mem_map(CODE_START, CODE_LEN, Prot::ALL).unwrap();
uc.mem_write(CODE_START, code).unwrap();
uc.add_code_hook(
CODE_START,
CODE_START + code.len() as u64,
|uc, address, _| {
let rip = CODE_START + 0xb;
if address == CODE_START + 0x7 {
uc.emu_stop().unwrap();
uc.reg_write(RegisterX86::RIP, rip).unwrap();
}
},
)
.unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let rip = uc.reg_read(RegisterX86::RIP).unwrap();
assert_eq!(rip, CODE_START + 0xb);
}
#[test]
fn test_tlb_clear() {
#[rustfmt::skip]
let code = &[
0xa3, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, // movabs dword ptr [0x200000], eax
0x0f, 0x05, // syscall
0xa3, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, // movabs dword ptr [0x200000], eax
];
let mut uc = Unicorn::new_with_data(Arch::X86, Mode::MODE_64, 0usize).unwrap();
uc.mem_map(CODE_START, CODE_LEN, Prot::ALL).unwrap();
uc.mem_write(CODE_START, code).unwrap();
uc.mem_map(0x200000, 0x1000, Prot::ALL).unwrap();
uc.ctl_set_tlb_type(TlbType::VIRTUAL).unwrap();
uc.add_tlb_hook(1, 0, |uc, addr, _| {
*uc.get_data_mut() += 1;
Some(TlbEntry {
paddr: addr,
perms: Prot::ALL,
})
})
.unwrap();
uc.add_insn_sys_hook(X86Insn::SYSCALL, 1, 0, |uc| {
uc.ctl_flush_tlb().unwrap();
})
.unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let tlbcount = *uc.get_data();
assert_eq!(tlbcount, 4);
}
#[test]
fn test_noexec() {
#[rustfmt::skip]
let code = &[
0x8a, 0x05, 0x00, 0x00, 0x00, 0x00, // mov al, byte ptr[rip]
0x90, // nop
];
let mut uc = Unicorn::new(Arch::X86, Mode::MODE_64).unwrap();
uc.mem_map(CODE_START, CODE_LEN, Prot::ALL).unwrap();
uc.mem_write(CODE_START, code).unwrap();
uc.ctl_set_tlb_type(TlbType::VIRTUAL).unwrap();
uc.mem_protect(CODE_START, CODE_START as usize + 0x1000, Prot::EXEC)
.unwrap();
let err = uc
.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap_err();
assert_eq!(err, uc_error::READ_PROT);
}

View File

@@ -0,0 +1,22 @@
use unicorn_engine_sys::RegisterM68K;
use super::*;
#[test]
fn test_move_to_sr() {
let code = [
0x46, 0xfc, 0x27, 0x00, // move #$2700, sr
];
let mut uc = uc_common_setup(Arch::M68K, Mode::BIG_ENDIAN, None, &code, ());
let mut sr = uc.reg_read(RegisterM68K::SR).unwrap();
sr |= 0x2000;
uc.reg_write(RegisterM68K::SR, sr).unwrap(); // Set supervisor mode
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let sr = uc.reg_read(RegisterM68K::SR).unwrap();
assert_eq!(sr, 0x2700);
}

View File

@@ -0,0 +1,529 @@
use unicorn_engine_sys::{ContextMode, RegisterX86};
use super::*;
#[test]
fn test_map_correct() {
let mut uc = Unicorn::new(Arch::X86, Mode::MODE_64).unwrap();
uc.mem_map(0x40000, 0x1000 * 16, Prot::ALL).unwrap(); // [0x40000, 0x50000]
uc.mem_map(0x60000, 0x1000 * 16, Prot::ALL).unwrap(); // [0x60000, 0x70000]
uc.mem_map(0x20000, 0x1000 * 16, Prot::ALL).unwrap(); // [0x20000, 0x30000]
assert_eq!(
uc.mem_map(0x10000, 0x2000 * 16, Prot::ALL),
Err(uc_error::MAP)
);
assert_eq!(
uc.mem_map(0x25000, 0x1000 * 16, Prot::ALL),
Err(uc_error::MAP)
);
assert_eq!(
uc.mem_map(0x35000, 0x1000 * 16, Prot::ALL),
Err(uc_error::MAP)
);
assert_eq!(
uc.mem_map(0x45000, 0x1000 * 16, Prot::ALL),
Err(uc_error::MAP)
);
assert_eq!(
uc.mem_map(0x55000, 0x2000 * 16, Prot::ALL),
Err(uc_error::MAP)
);
uc.mem_map(0x35000, 0x5000, Prot::ALL).unwrap();
uc.mem_map(0x50000, 0x5000, Prot::ALL).unwrap();
}
#[test]
fn test_map_wrapping() {
let mut uc = Unicorn::new(Arch::X86, Mode::MODE_64).unwrap();
assert_eq!(
uc.mem_map((!0 - 0x4000) & !0xfff, 0x8000, Prot::ALL),
Err(uc_error::ARG)
);
}
#[test]
fn test_mem_protect() {
let code = [
0x01, 0x70, 0x04, // add [eax + 4], esi
];
let mut uc = Unicorn::new(Arch::X86, Mode::MODE_32).unwrap();
let eax = 0x2000;
let esi = 0xdeadbeef;
uc.reg_write(RegisterX86::EAX, eax).unwrap();
uc.reg_write(RegisterX86::ESI, esi).unwrap();
uc.mem_map(0x1000, 0x1000, Prot::READ | Prot::EXEC).unwrap();
uc.mem_map(0x2000, 0x1000, Prot::READ).unwrap();
uc.mem_protect(0x2000, 0x1000, Prot::READ | Prot::WRITE)
.unwrap();
uc.mem_write(0x1000, &code).unwrap();
uc.emu_start(0x1000, 0x1000 + code.len() as u64, 0, 1)
.unwrap();
let mem = u32::from_le_bytes(
uc.mem_read_as_vec(0x2000 + 4, 4)
.unwrap()
.try_into()
.unwrap(),
);
assert_eq!(mem, 0xdeadbeef);
}
#[test]
fn test_splitting_mem_unmap() {
let mut uc = Unicorn::new(Arch::X86, Mode::MODE_32).unwrap();
uc.mem_map(0x20000, 0x1000, Prot::NONE).unwrap();
uc.mem_map(0x21000, 0x2000, Prot::NONE).unwrap();
uc.mem_unmap(0x21000, 0x1000).unwrap();
}
#[test]
fn test_splitting_mmio_unmap() {
let mut uc = Unicorn::new(Arch::X86, Mode::MODE_32).unwrap();
let code = &[
0x8b, 0x0d, 0x04, 0x30, 0x00, 0x00, // mov ecx, [0x3004] <-- normal read
0x8b, 0x1d, 0x04, 0x40, 0x00, 0x00, // mov ebx, [0x4004] <-- mmio read
];
let bytes = 0xdeadbeefu32;
uc.mem_map(0x1000, 0x1000, Prot::ALL).unwrap();
uc.mem_write(0x1000, code).unwrap();
uc.mmio_map_ro(0x3000, 0x2000, |_, offset, size| {
assert_eq!(offset, 4);
assert_eq!(size, 4);
0x19260817
})
.unwrap();
// Map a ram area instead
uc.mem_unmap(0x3000, 0x1000).unwrap();
uc.mem_map(0x3000, 0x1000, Prot::ALL).unwrap();
uc.mem_write(0x3004, &bytes.to_le_bytes()).unwrap();
uc.emu_start(0x1000, 0x1000 + code.len() as u64, 0, 0)
.unwrap();
let ecx = uc.reg_read(RegisterX86::ECX).unwrap();
let ebx = uc.reg_read(RegisterX86::EBX).unwrap();
assert_eq!(ecx, 0xdeadbeef);
assert_eq!(ebx, 0x19260817);
}
#[test]
fn test_mem_protect_map_ptr() {
let mut uc = Unicorn::new(Arch::X86, Mode::MODE_64).unwrap();
let val = 0x114514u64;
let mut data1 = vec![0u8; 0x4000];
let mut data2 = vec![0u8; 0x2000];
unsafe {
uc.mem_map_ptr(0x4000, 0x4000, Prot::ALL, data1.as_mut_ptr().cast())
.unwrap();
}
uc.mem_unmap(0x6000, 0x2000).unwrap();
unsafe {
uc.mem_map_ptr(0x6000, 0x2000, Prot::ALL, data2.as_mut_ptr().cast())
.unwrap();
}
uc.mem_write(0x6004, &val.to_le_bytes()).unwrap();
uc.mem_protect(0x6000, 0x1000, Prot::READ).unwrap();
let mut mem = vec![0u8; 8];
uc.mem_read(0x6004, &mut mem).unwrap();
assert_eq!(val.to_le_bytes(), mem.as_slice());
}
#[test]
fn test_map_at_the_end() {
let mut uc = Unicorn::new(Arch::X86, Mode::MODE_64).unwrap();
let mem = vec![0xffu8; 0x1000];
uc.mem_map(0xfffffffffffff000, 0x1000, Prot::ALL).unwrap();
uc.mem_write(0xfffffffffffff000, &mem).unwrap();
assert_eq!(
uc.mem_write(0xffffffffffffff00, &mem),
Err(uc_error::WRITE_UNMAPPED)
);
assert_eq!(uc.mem_write(0, &mem), Err(uc_error::WRITE_UNMAPPED));
}
#[test]
fn test_map_big_memory() {
let mut uc = Unicorn::new(Arch::X86, Mode::MODE_64).unwrap();
let requested_size = !(page_size::get() - 1);
assert_eq!(
uc.mem_map(0x0, requested_size, Prot::ALL),
Err(uc_error::NOMEM)
);
}
#[test]
fn test_mem_protect_remove_exec() {
#[rustfmt::skip]
let code = [
0x90, // nop
0xeb, 0x00, // jmp 3
0x90, // nop
];
let mut uc = Unicorn::new_with_data(Arch::X86, Mode::MODE_64, 0).unwrap();
uc.mem_map(0x1000, 0x1000, Prot::ALL).unwrap();
uc.mem_map(0x2000, 0x1000, Prot::ALL).unwrap();
uc.mem_write(0x1000, &code).unwrap();
uc.add_block_hook(1, 0, |uc, _, _| {
*uc.get_data_mut() += 1;
uc.mem_protect(0x2000, 0x1000, Prot::READ).unwrap();
})
.unwrap();
uc.emu_start(0x1000, 0x1000 + code.len() as u64, 0, 0)
.unwrap();
assert_eq!(*uc.get_data_mut(), 2);
}
#[test]
fn test_mem_protect_mmio() {
#[rustfmt::skip]
let code = [
0xa1, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // movabs eax, dword ptr [0x2020]
0xa3, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // movabs dword ptr [0x2020], eax
];
let mut uc = Unicorn::new_with_data(Arch::X86, Mode::MODE_64, 0).unwrap();
uc.mem_map(0x8000, 0x1000, Prot::ALL).unwrap();
uc.mem_write(0x8000, &code).unwrap();
uc.mmio_map(
0x1000,
0x3000,
Some(|uc: &mut Unicorn<'_, i32>, addr, _| {
assert_eq!(addr, 0x20);
*uc.get_data_mut() += 1;
0x114514
}),
Some(|_: &mut Unicorn<'_, i32>, _addr, _size, _val| {
panic!("Write callback should not be called");
}),
)
.unwrap();
uc.mem_protect(0x2000, 0x1000, Prot::READ).unwrap();
assert_eq!(
uc.emu_start(0x8000, 0x8000 + code.len() as u64, 0, 0),
Err(uc_error::WRITE_PROT)
);
let eax = uc.reg_read(RegisterX86::RAX).unwrap();
assert_eq!(*uc.get_data_mut(), 1);
assert_eq!(eax, 0x114514u64);
}
#[test]
fn test_snapshot() {
#[rustfmt::skip]
let code = [
0xa1, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // movabs eax, dword ptr [0x2020]
0xff, 0xc0, // inc eax
0xa3, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // movabs dword ptr [0x2020], eax
];
let mut uc = Unicorn::new(Arch::X86, Mode::MODE_64).unwrap();
let mut c0 = uc.context_alloc().unwrap();
let mut c1 = uc.context_alloc().unwrap();
uc.ctl_set_context_mode(ContextMode::MEMORY).unwrap();
uc.mem_map(0x1000, 0x1000, Prot::ALL).unwrap();
uc.mem_write(0x1000, &code).unwrap();
uc.mem_map(0x2000, 0x1000, Prot::ALL).unwrap();
uc.context_save(&mut c0).unwrap();
uc.emu_start(0x1000, 0x1000 + code.len() as u64, 0, 0)
.unwrap();
let mem = u32::from_le_bytes(uc.mem_read_as_vec(0x2020, 4).unwrap().try_into().unwrap());
assert_eq!(mem, 1);
uc.context_save(&mut c1).unwrap();
uc.emu_start(0x1000, 0x1000 + code.len() as u64, 0, 0)
.unwrap();
let mem = u32::from_le_bytes(uc.mem_read_as_vec(0x2020, 4).unwrap().try_into().unwrap());
assert_eq!(mem, 2);
uc.context_restore(&c1).unwrap();
let mem = u32::from_le_bytes(uc.mem_read_as_vec(0x2020, 4).unwrap().try_into().unwrap());
assert_eq!(mem, 1);
uc.context_restore(&c0).unwrap();
let mem = u32::from_le_bytes(uc.mem_read_as_vec(0x2020, 4).unwrap().try_into().unwrap());
assert_eq!(mem, 0);
let code_data = uc.mem_read_as_vec(0x1000, 1).unwrap();
assert_eq!(code_data[0], 0xa1);
}
// static bool test_snapshot_with_vtlb_callback(uc_engine *uc, uint64_t addr,
// uc_mem_type type,
// uc_tlb_entry *result,
// void *user_data)
// {
// result->paddr = addr - 0x400000000;
// result->perms = UC_PROT_ALL;
// return true;
// }
//
// static void test_snapshot_with_vtlb(void)
// {
// uc_engine *uc;
// uc_context *c0, *c1;
// uint32_t mem;
// uc_hook hook;
//
// // mov eax, [0x2020]; inc eax; mov [0x2020], eax
// char code[] = "\xA1\x20\x20\x00\x00\x04\x00\x00\x00\xFF\xC0\xA3\x20\x20\x00"
// "\x00\x04\x00\x00\x00";
//
// OK(uc_open(UC_ARCH_X86, UC_MODE_64, &uc));
//
// // Allocate contexts
// OK(uc_context_alloc(uc, &c0));
// OK(uc_context_alloc(uc, &c1));
// OK(uc_ctl_context_mode(uc, UC_CTL_CONTEXT_MEMORY));
//
// OK(uc_ctl_tlb_mode(uc, UC_TLB_VIRTUAL));
// OK(uc_hook_add(uc, &hook, UC_HOOK_TLB_FILL,
// test_snapshot_with_vtlb_callback, NULL, 1, 0));
//
// // Map physical memory
// OK(uc_mem_map(uc, 0x1000, 0x1000, UC_PROT_EXEC | UC_PROT_READ));
// OK(uc_mem_write(uc, 0x1000, code, sizeof(code) - 1));
// OK(uc_mem_map(uc, 0x2000, 0x1000, UC_PROT_ALL));
//
// // Initial context save
// OK(uc_context_save(uc, c0));
//
// OK(uc_emu_start(uc, 0x400000000 + 0x1000,
// 0x400000000 + 0x1000 + sizeof(code) - 1, 0, 0));
// OK(uc_mem_read(uc, 0x2020, &mem, sizeof(mem)));
// TEST_CHECK(mem == 1);
// OK(uc_context_save(uc, c1));
// OK(uc_emu_start(uc, 0x400000000 + 0x1000,
// 0x400000000 + 0x1000 + sizeof(code) - 1, 0, 0));
// OK(uc_mem_read(uc, 0x2020, &mem, sizeof(mem)));
// TEST_CHECK(mem == 2);
// OK(uc_context_restore(uc, c1));
// // TODO check mem
// OK(uc_mem_read(uc, 0x2020, &mem, sizeof(mem)));
// TEST_CHECK(mem == 1);
// OK(uc_context_restore(uc, c0));
// OK(uc_mem_read(uc, 0x2020, &mem, sizeof(mem)));
// TEST_CHECK(mem == 0);
// // TODO check mem
//
// OK(uc_context_free(c0));
// OK(uc_context_free(c1));
// OK(uc_close(uc));
// }
#[test]
fn test_snapshot_with_vtlb() {
#[rustfmt::skip]
let code = [
0xa1, 0x20, 0x20, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, // movabs eax, dword ptr [0x2020]
0xff, 0xc0, // inc eax
0xa3, 0x20, 0x20, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, // movabs dword ptr [0x2020], eax
];
let mut uc = Unicorn::new(Arch::X86, Mode::MODE_64).unwrap();
// Allocate contexts
let mut c0 = uc.context_alloc().unwrap();
let mut c1 = uc.context_alloc().unwrap();
uc.ctl_set_context_mode(ContextMode::MEMORY).unwrap();
uc.ctl_set_tlb_type(TlbType::VIRTUAL).unwrap();
uc.add_tlb_hook(1, 0, |_, addr, _| {
Some(TlbEntry {
paddr: addr - 0x400000000,
perms: Prot::ALL,
})
})
.unwrap();
// Map physical memory
uc.mem_map(0x1000, 0x1000, Prot::EXEC | Prot::READ).unwrap();
uc.mem_write(0x1000, &code).unwrap();
uc.mem_map(0x2000, 0x1000, Prot::ALL).unwrap();
// Initial context save
uc.context_save(&mut c0).unwrap();
uc.emu_start(
0x400000000 + CODE_START,
0x400000000 + CODE_START + code.len() as u64,
0,
0,
)
.unwrap();
let mem = u32::from_le_bytes(uc.mem_read_as_vec(0x2020, 4).unwrap().try_into().unwrap());
assert_eq!(mem, 1);
uc.context_save(&mut c1).unwrap();
uc.emu_start(
0x400000000 + CODE_START,
0x400000000 + CODE_START + code.len() as u64,
0,
0,
)
.unwrap();
let mem = u32::from_le_bytes(uc.mem_read_as_vec(0x2020, 4).unwrap().try_into().unwrap());
assert_eq!(mem, 2);
uc.context_restore(&c1).unwrap();
let mem = u32::from_le_bytes(uc.mem_read_as_vec(0x2020, 4).unwrap().try_into().unwrap());
assert_eq!(mem, 1);
uc.context_restore(&c0).unwrap();
let mem = u32::from_le_bytes(uc.mem_read_as_vec(0x2020, 4).unwrap().try_into().unwrap());
assert_eq!(mem, 0);
}
#[test]
fn test_context_snapshot() {
let mut uc = Unicorn::new(Arch::X86, Mode::MODE_64).unwrap();
let baseaddr = 0xfffff1000;
let offset = 0x10;
let mut tmp = 1u64;
uc.ctl_set_context_mode(ContextMode::MEMORY | ContextMode::CPU)
.unwrap();
uc.mem_map(baseaddr, 0x1000, Prot::ALL).unwrap();
let mut ctx = uc.context_alloc().unwrap();
uc.context_save(&mut ctx).unwrap();
uc.mem_write(baseaddr + offset, &tmp.to_le_bytes()).unwrap();
tmp = u64::from_le_bytes(
uc.mem_read_as_vec(baseaddr + offset, 8)
.unwrap()
.try_into()
.unwrap(),
);
assert_eq!(tmp, 1);
uc.context_restore(&ctx).unwrap();
tmp = u64::from_le_bytes(
uc.mem_read_as_vec(baseaddr + offset, 8)
.unwrap()
.try_into()
.unwrap(),
);
assert_eq!(tmp, 0);
tmp = 2;
uc.mem_write(baseaddr + offset, &tmp.to_le_bytes()).unwrap();
tmp = u64::from_le_bytes(
uc.mem_read_as_vec(baseaddr + offset, 8)
.unwrap()
.try_into()
.unwrap(),
);
assert_eq!(tmp, 2);
uc.context_restore(&ctx).unwrap();
tmp = u64::from_le_bytes(
uc.mem_read_as_vec(baseaddr + offset, 8)
.unwrap()
.try_into()
.unwrap(),
);
assert_eq!(tmp, 0);
}
// static void test_snapshot_unmap(void)
// {
// uc_engine *uc;
// uc_context *ctx;
// uint64_t tmp;
//
// OK(uc_open(UC_ARCH_X86, UC_MODE_64, &uc));
// OK(uc_ctl_context_mode(uc, UC_CTL_CONTEXT_MEMORY | UC_CTL_CONTEXT_CPU));
// OK(uc_mem_map(uc, 0x1000, 0x2000, UC_PROT_ALL));
//
// tmp = 1;
// OK(uc_mem_write(uc, 0x1000, &tmp, sizeof(tmp)));
// tmp = 2;
// OK(uc_mem_write(uc, 0x2000, &tmp, sizeof(tmp)));
//
// OK(uc_context_alloc(uc, &ctx));
// OK(uc_context_save(uc, ctx));
//
// uc_assert_err(UC_ERR_ARG, uc_mem_unmap(uc, 0x1000, 0x1000));
// OK(uc_mem_unmap(uc, 0x1000, 0x2000));
// uc_assert_err(UC_ERR_READ_UNMAPPED,
// uc_mem_read(uc, 0x1000, &tmp, sizeof(tmp)));
// uc_assert_err(UC_ERR_READ_UNMAPPED,
// uc_mem_read(uc, 0x2000, &tmp, sizeof(tmp)));
//
// OK(uc_context_restore(uc, ctx));
// OK(uc_mem_read(uc, 0x1000, &tmp, sizeof(tmp)));
// TEST_CHECK(tmp == 1);
// OK(uc_mem_read(uc, 0x2000, &tmp, sizeof(tmp)));
// TEST_CHECK(tmp == 2);
//
// OK(uc_context_free(ctx));
// OK(uc_close(uc));
// }
#[test]
fn test_snapshot_unmap() {
let mut uc = Unicorn::new(Arch::X86, Mode::MODE_64).unwrap();
let offset = 0x10;
uc.ctl_set_context_mode(ContextMode::MEMORY | ContextMode::CPU)
.unwrap();
uc.mem_map(0x1000, 0x2000, Prot::ALL).unwrap();
let mut tmp = 1u64;
uc.mem_write(0x1000 + offset, &tmp.to_le_bytes()).unwrap();
tmp = 2;
uc.mem_write(0x2000 + offset, &tmp.to_le_bytes()).unwrap();
let mut ctx = uc.context_alloc().unwrap();
uc.context_save(&mut ctx).unwrap();
assert_eq!(uc.mem_unmap(0x1000, 0x1000).unwrap_err(), uc_error::ARG);
uc.mem_unmap(0x1000, 0x2000).unwrap();
assert_eq!(
uc.mem_read_as_vec(0x1000 + offset, 8).unwrap_err(),
uc_error::READ_UNMAPPED,
);
assert_eq!(
uc.mem_read_as_vec(0x2000 + offset, 8).unwrap_err(),
uc_error::READ_UNMAPPED,
);
uc.context_restore(&ctx).unwrap();
tmp = u64::from_le_bytes(
uc.mem_read_as_vec(0x1000 + offset, 8)
.unwrap()
.try_into()
.unwrap(),
);
assert_eq!(tmp, 1);
tmp = u64::from_le_bytes(
uc.mem_read_as_vec(0x2000 + offset, 8)
.unwrap()
.try_into()
.unwrap(),
);
assert_eq!(tmp, 2);
}

View File

@@ -0,0 +1,227 @@
use unicorn_engine_sys::RegisterMIPS;
use super::*;
const CODE_START: u64 = 0x10000000;
const CODE_LEN: usize = 0x4000;
fn uc_common_setup<T>(
arch: Arch,
mode: Mode,
cpu_model: Option<i32>,
code: &[u8],
data: T,
) -> Unicorn<'_, T> {
let mut uc = Unicorn::new_with_data(arch, mode, data).unwrap();
if let Some(cpu_model) = cpu_model {
uc.ctl_set_cpu_model(cpu_model).unwrap();
}
uc.mem_map(CODE_START, CODE_LEN, Prot::ALL).unwrap();
uc.mem_write(CODE_START, code).unwrap();
uc
}
#[test]
fn test_mips_el_ori() {
let code = [
0x56, 0x34, 0x21, 0x34, // ori $at, $at, 0x3456;
];
let r1 = 0x6789;
let mut uc = uc_common_setup(
Arch::MIPS,
Mode::MIPS32 | Mode::LITTLE_ENDIAN,
None,
&code,
(),
);
uc.reg_write(RegisterMIPS::R1, r1).unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let r1 = uc.reg_read(RegisterMIPS::R1).unwrap();
assert_eq!(r1, 0x77df);
}
#[test]
fn test_mips_eb_ori() {
let code = [
0x34, 0x21, 0x34, 0x56, // ori $at, $at, 0x3456;
];
let r1 = 0x6789;
let mut uc = uc_common_setup(Arch::MIPS, Mode::MIPS32 | Mode::BIG_ENDIAN, None, &code, ());
uc.reg_write(RegisterMIPS::R1, r1).unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let r1 = uc.reg_read(RegisterMIPS::R1).unwrap();
assert_eq!(r1, 0x77df);
}
#[test]
fn test_mips_stop_at_branch() {
let code = [
0x02, 0x00, 0x00, 0x08, // j 0x8
0x21, 0x10, 0x62, 0x00, // addu $v0, $v1, $v0
];
let v1 = 5;
let mut uc = uc_common_setup(
Arch::MIPS,
Mode::MIPS32 | Mode::LITTLE_ENDIAN,
None,
&code,
(),
);
uc.reg_write(RegisterMIPS::V1, v1).unwrap();
// Execute one instruction with branch delay slot.
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 1)
.unwrap();
let pc = uc.reg_read(RegisterMIPS::PC).unwrap();
let v1 = uc.reg_read(RegisterMIPS::V0).unwrap();
// Even if we just execute one instruction, the instruction in the
// delay slot would also be executed.
assert_eq!(pc, CODE_START + 0x8);
assert_eq!(v1, 0x5);
}
#[test]
fn test_mips_stop_at_delay_slot() {
let code = [
0x02, 0x00, 0x00, 0x08, // j 0x8
0x00, 0x00, 0x00, 0x00, // nop
0x00, 0x00, 0x00, 0x00, // nop
];
let mut uc = uc_common_setup(
Arch::MIPS,
Mode::MIPS32 | Mode::LITTLE_ENDIAN,
None,
&code,
(),
);
// Stop at the delay slot by design.
uc.emu_start(CODE_START, CODE_START + 4, 0, 0).unwrap();
let pc = uc.reg_read(RegisterMIPS::PC).unwrap();
// The branch instruction isn't committed and the PC is not updated.
// The user is responsible for restarting emulation at the branch instruction.
assert_eq!(pc, CODE_START);
}
#[test]
fn test_mips_stop_at_delay_slot_2() {
let code = [
0x24, 0x06, 0x00, 0x03, // addiu $a2, $zero, 3
0x10, 0xa6, 0x00, 0x79, // beq $a1, $a2, 0x1e8
0x30, 0x42, 0x00, 0xfc, // andi $v0, $v0, 0xfc
0x10, 0x40, 0x00, 0x32, // beqz $v0, 0x47c8c90
0x24, 0xab, 0xff, 0xda, // addiu $t3, $a1, -0x26
0x2d, 0x62, 0x00, 0x02, // sltiu $v0, $t3, 2
0x10, 0x40, 0x00, 0x32, // beqz $v0, 0x47c8c9c
0x00, 0x00, 0x00, 0x00, // nop
];
let v0 = 0xff;
let a1 = 0x3;
let mut uc = uc_common_setup(Arch::MIPS, Mode::MIPS32 | Mode::BIG_ENDIAN, None, &code, ());
uc.reg_write(RegisterMIPS::V0, v0).unwrap();
uc.reg_write(RegisterMIPS::A1, a1).unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64 + 16, 0, 2)
.unwrap();
let pc = uc.reg_read(RegisterMIPS::PC).unwrap();
let v0 = uc.reg_read(RegisterMIPS::V0).unwrap();
assert_eq!(pc, CODE_START + 4 + 0x1e8);
assert_eq!(v0, 0xfc);
}
#[test]
fn test_mips_lwx_exception_issue_1314() {
let code = [
0x0a, 0xc8, 0x79, 0x7e, // lwx $t9, $t9($s3)
];
let mut uc = uc_common_setup(
Arch::MIPS,
Mode::MIPS32 | Mode::LITTLE_ENDIAN,
None,
&code,
(),
);
uc.mem_map(0x10000, 0x4000, Prot::ALL).unwrap();
// Enable DSP
// https://s3-eu-west-1.amazonaws.com/downloads-mips/documents/MD00090-2B-MIPS32PRA-AFP-06.02.pdf
let mut reg = uc.reg_read(RegisterMIPS::CP0_STATUS).unwrap();
reg |= 1 << 24;
uc.reg_write(RegisterMIPS::CP0_STATUS, reg).unwrap();
reg = 0;
uc.reg_write(RegisterMIPS::R1, reg).unwrap();
uc.reg_write(RegisterMIPS::T9, reg).unwrap();
reg = 0xdeadbeef;
uc.mem_write(0x10000, &(reg as u32).to_le_bytes()).unwrap();
reg = 0x10000;
uc.reg_write(RegisterMIPS::S3, reg).unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
reg = uc.reg_read(RegisterMIPS::T9).unwrap();
assert_eq!(reg, 0xdeadbeef);
}
#[test]
fn test_mips_mips16() {
let code = [
0xC4, 0x6B, 0x49, 0xE3, // sc $t1, 0x6bc4($k0)
];
let v0 = 0x6789;
let mips16_lowbit = 1;
let mut uc = uc_common_setup(
Arch::MIPS,
Mode::MIPS32 | Mode::LITTLE_ENDIAN,
None,
&code,
(),
);
uc.reg_write(RegisterMIPS::V0, v0).unwrap();
uc.emu_start(
CODE_START | mips16_lowbit,
CODE_START + code.len() as u64,
0,
0,
)
.unwrap();
let v0 = uc.reg_read(RegisterMIPS::V0).unwrap();
assert_eq!(v0, 0x684D);
}
#[test]
fn test_mips_mips_fpr() {
#[rustfmt::skip]
let code = [
0xf6, 0x42, 0x09, 0x3c, 0x79, 0xe9, 0x29, 0x35, // li $t1, 0x42f6e979
0x00, 0x08, 0x89, 0x44, // mtc1 $t1, $f1
];
let mut uc = uc_common_setup(Arch::MIPS, Mode::MIPS32, None, &code, ());
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let f1 = uc.reg_read(RegisterMIPS::F1).unwrap();
assert_eq!(f1, 0x42f6e979);
}

View File

@@ -0,0 +1,39 @@
extern crate std;
#[cfg(feature = "arch_arm")]
mod arm;
#[cfg(feature = "arch_aarch64")]
mod arm64;
mod ctl;
#[cfg(feature = "arch_m68k")]
mod m68k;
mod mem;
#[cfg(feature = "arch_mips")]
mod mips;
#[cfg(feature = "arch_ppc")]
mod ppc;
#[cfg(feature = "arch_riscv")]
mod riscv;
#[cfg(feature = "arch_s390x")]
mod s390x;
use crate::{Arch, HookType, Mode, Prot, TlbEntry, TlbType, Unicorn, uc_error};
pub const CODE_START: u64 = 0x1000;
pub const CODE_LEN: usize = 0x4000;
fn uc_common_setup<T>(
arch: Arch,
mode: Mode,
cpu_model: Option<i32>,
code: &[u8],
data: T,
) -> Unicorn<'_, T> {
let mut uc = Unicorn::new_with_data(arch, mode, data).unwrap();
if let Some(cpu_model) = cpu_model {
uc.ctl_set_cpu_model(cpu_model).unwrap();
}
uc.mem_map(CODE_START, CODE_LEN, Prot::ALL).unwrap();
uc.mem_write(CODE_START, code).unwrap();
uc
}

View File

@@ -0,0 +1,92 @@
use unicorn_engine_sys::RegisterPPC;
use super::*;
#[test]
fn test_ppc32_add() {
let code = [
0x7f, 0x46, 0x1a, 0x14, // add 26, 6, 3
];
let r3 = 42;
let r6 = 1337;
let mut uc = uc_common_setup(Arch::PPC, Mode::PPC32 | Mode::BIG_ENDIAN, None, &code, ());
uc.reg_write(RegisterPPC::R3, r3).unwrap();
uc.reg_write(RegisterPPC::R6, r6).unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let reg = uc.reg_read(RegisterPPC::R26).unwrap();
assert_eq!(reg, 1379);
}
// https://www.ibm.com/docs/en/aix/7.2?topic=set-fadd-fa-floating-add-instruction
#[test]
// #[ignore = "Crashes on Windows & some Linux distros"]
fn test_ppc32_fadd() {
let code = [
0xfc, 0xc4, 0x28, 0x2a, // fadd 6, 4, 5
];
let mut msr = 0;
let fpr4 = 0xC053400000000000;
let fpr5 = 0x400C000000000000;
let mut uc = uc_common_setup(Arch::PPC, Mode::PPC32 | Mode::BIG_ENDIAN, None, &code, ());
msr |= 1 << 13; // Big endian
uc.reg_write(RegisterPPC::MSR, msr).unwrap(); // enable FP
uc.reg_write(RegisterPPC::FPR4, fpr4).unwrap();
uc.reg_write(RegisterPPC::FPR5, fpr5).unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let fpr6 = uc.reg_read(RegisterPPC::FPR6).unwrap();
assert_eq!(fpr6, 0xC052600000000000);
}
#[test]
fn test_ppc32_sc() {
let code = [
0x44, 0x00, 0x00, 0x02, // sc
];
let mut uc = uc_common_setup(Arch::PPC, Mode::PPC32 | Mode::BIG_ENDIAN, None, &code, ());
uc.add_intr_hook(|uc, _| uc.emu_stop().unwrap()).unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let pc = uc.reg_read(RegisterPPC::PC).unwrap();
assert_eq!(pc, CODE_START + 4);
}
#[test]
fn test_ppc32_cr() {
let mut uc = uc_common_setup(Arch::PPC, Mode::PPC32 | Mode::BIG_ENDIAN, None, &[], ());
let mut cr = 0x12345678;
uc.reg_write(RegisterPPC::CR, cr).unwrap();
cr = uc.reg_read(RegisterPPC::CR).unwrap();
assert_eq!(cr, 0x12345678);
}
#[test]
fn test_ppc32_spr_time() {
let code = [
0x7c, 0x76, 0x02, 0xa6, // mfspr r3, DEC
0x7c, 0x6d, 0x42, 0xa6, // mfspr r3, TBUr
];
let mut uc = uc_common_setup(Arch::PPC, Mode::PPC32 | Mode::BIG_ENDIAN, None, &code, ());
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
}

View File

@@ -0,0 +1,676 @@
use unicorn_engine_sys::RegisterRISCV;
use super::*;
#[test]
fn test_riscv32_nop() {
let code = [
0x13, 0x00, 0x00, 0x00, // nop
];
let t0 = 0x1234;
let t1 = 0x5678;
let mut uc = uc_common_setup(Arch::RISCV, Mode::RISCV32, None, &code, ());
uc.reg_write(RegisterRISCV::T0, t0).unwrap();
uc.reg_write(RegisterRISCV::T1, t1).unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let t0 = uc.reg_read(RegisterRISCV::T0).unwrap();
let t1 = uc.reg_read(RegisterRISCV::T1).unwrap();
assert_eq!(t0, 0x1234);
assert_eq!(t1, 0x5678);
}
#[test]
fn test_riscv64_nop() {
let code = [
0x13, 0x00, 0x00, 0x00, // nop
];
let t0 = 0x1234;
let t1 = 0x5678;
let mut uc = uc_common_setup(Arch::RISCV, Mode::RISCV64, None, &code, ());
uc.reg_write(RegisterRISCV::T0, t0).unwrap();
uc.reg_write(RegisterRISCV::T1, t1).unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let t0 = uc.reg_read(RegisterRISCV::T0).unwrap();
let t1 = uc.reg_read(RegisterRISCV::T1).unwrap();
assert_eq!(t0, 0x1234);
assert_eq!(t1, 0x5678);
}
#[test]
fn test_riscv32_until_pc_update() {
let code = [
0x93, 0x02, 0x10, 0x00, // addi t0, zero, 1
0x13, 0x03, 0x00, 0x02, // addi t1, zero, 0x20
0x13, 0x01, 0x81, 0x00, // addi sp, sp, 8
];
let mut uc = uc_common_setup(Arch::RISCV, Mode::RISCV32, None, &code, ());
let mut t0 = 0x1234;
let mut t1 = 0x7890;
let mut sp = 0x1234;
uc.reg_write(RegisterRISCV::T0, t0).unwrap();
uc.reg_write(RegisterRISCV::T1, t1).unwrap();
uc.reg_write(RegisterRISCV::SP, sp).unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
t0 = uc.reg_read(RegisterRISCV::T0).unwrap();
t1 = uc.reg_read(RegisterRISCV::T1).unwrap();
sp = uc.reg_read(RegisterRISCV::SP).unwrap();
let pc = uc.reg_read(RegisterRISCV::PC).unwrap();
assert_eq!(t0, 0x1);
assert_eq!(t1, 0x20);
assert_eq!(sp, 0x123c);
assert_eq!(pc, CODE_START + code.len() as u64);
}
#[test]
fn test_riscv64_until_pc_update() {
let code = [
0x93, 0x02, 0x10, 0x00, // addi t0, zero, 1
0x13, 0x03, 0x00, 0x02, // addi t1, zero, 0x20
0x13, 0x01, 0x81, 0x00, // addi sp, sp, 8
];
let mut uc = uc_common_setup(Arch::RISCV, Mode::RISCV64, None, &code, ());
let mut t0 = 0x1234;
let mut t1 = 0x7890;
let mut sp = 0x1234;
uc.reg_write(RegisterRISCV::T0, t0).unwrap();
uc.reg_write(RegisterRISCV::T1, t1).unwrap();
uc.reg_write(RegisterRISCV::SP, sp).unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
t0 = uc.reg_read(RegisterRISCV::T0).unwrap();
t1 = uc.reg_read(RegisterRISCV::T1).unwrap();
sp = uc.reg_read(RegisterRISCV::SP).unwrap();
let pc = uc.reg_read(RegisterRISCV::PC).unwrap();
assert_eq!(t0, 0x1);
assert_eq!(t1, 0x20);
assert_eq!(sp, 0x123c);
assert_eq!(pc, CODE_START + code.len() as u64);
}
#[test]
fn test_riscv32_3steps_pc_update() {
let code = [
0x93, 0x02, 0x10, 0x00, // addi t0, zero, 1
0x13, 0x03, 0x00, 0x02, // addi t1, zero, 0x20
0x13, 0x01, 0x81, 0x00, // addi sp, sp, 8
];
let mut uc = uc_common_setup(Arch::RISCV, Mode::RISCV32, None, &code, ());
let mut t0 = 0x1234;
let mut t1 = 0x7890;
let mut sp = 0x1234;
uc.reg_write(RegisterRISCV::T0, t0).unwrap();
uc.reg_write(RegisterRISCV::T1, t1).unwrap();
uc.reg_write(RegisterRISCV::SP, sp).unwrap();
uc.emu_start(CODE_START, u64::MAX, 0, 3).unwrap();
t0 = uc.reg_read(RegisterRISCV::T0).unwrap();
t1 = uc.reg_read(RegisterRISCV::T1).unwrap();
sp = uc.reg_read(RegisterRISCV::SP).unwrap();
let pc = uc.reg_read(RegisterRISCV::PC).unwrap();
assert_eq!(t0, 0x1);
assert_eq!(t1, 0x20);
assert_eq!(sp, 0x123c);
assert_eq!(pc, CODE_START + code.len() as u64);
}
#[test]
fn test_riscv64_3steps_pc_update() {
let code = [
0x93, 0x02, 0x10, 0x00, // addi t0, zero, 1
0x13, 0x03, 0x00, 0x02, // addi t1, zero, 0x20
0x13, 0x01, 0x81, 0x00, // addi sp, sp, 8
];
let mut uc = uc_common_setup(Arch::RISCV, Mode::RISCV64, None, &code, ());
let mut t0 = 0x1234;
let mut t1 = 0x7890;
let mut sp = 0x1234;
uc.reg_write(RegisterRISCV::T0, t0).unwrap();
uc.reg_write(RegisterRISCV::T1, t1).unwrap();
uc.reg_write(RegisterRISCV::SP, sp).unwrap();
uc.emu_start(CODE_START, u64::MAX, 0, 3).unwrap();
t0 = uc.reg_read(RegisterRISCV::T0).unwrap();
t1 = uc.reg_read(RegisterRISCV::T1).unwrap();
sp = uc.reg_read(RegisterRISCV::SP).unwrap();
let pc = uc.reg_read(RegisterRISCV::PC).unwrap();
assert_eq!(t0, 0x1);
assert_eq!(t1, 0x20);
assert_eq!(sp, 0x123c);
assert_eq!(pc, CODE_START + code.len() as u64);
}
#[test]
fn test_riscv32_fp_move() {
let code = [
0xd3, 0x81, 0x10, 0x22, // fmv.d f3, f1
];
let mut f1 = 0x123456781a2b3c4d;
let mut f3 = 0x56780246aaaabbbb;
let mut uc = uc_common_setup(Arch::RISCV, Mode::RISCV32, None, &code, ());
uc.reg_write(RegisterRISCV::F1, f1).unwrap();
uc.reg_write(RegisterRISCV::F3, f3).unwrap();
uc.emu_start(CODE_START, u64::MAX, 0, 1).unwrap();
f1 = uc.reg_read(RegisterRISCV::F1).unwrap();
f3 = uc.reg_read(RegisterRISCV::F3).unwrap();
assert_eq!(f1, 0x123456781a2b3c4d);
assert_eq!(f3, 0x123456781a2b3c4d);
}
#[test]
fn test_riscv64_fp_move() {
let code = [
0xd3, 0x81, 0x10, 0x22, // fmv.d f3, f1
];
let mut f1 = 0x123456781a2b3c4d;
let mut f3 = 0x56780246aaaabbbb;
let mut uc = uc_common_setup(Arch::RISCV, Mode::RISCV64, None, &code, ());
uc.reg_write(RegisterRISCV::F1, f1).unwrap();
uc.reg_write(RegisterRISCV::F3, f3).unwrap();
uc.emu_start(CODE_START, u64::MAX, 0, 1).unwrap();
f1 = uc.reg_read(RegisterRISCV::F1).unwrap();
f3 = uc.reg_read(RegisterRISCV::F3).unwrap();
assert_eq!(f1, 0x123456781a2b3c4d);
assert_eq!(f3, 0x123456781a2b3c4d);
}
#[test]
fn test_riscv64_fp_move_from_int() {
let code = [
0xf3, 0x90, 0x01, 0x30, // csrrw x2, mstatus, x3;
0x53, 0x00, 0x0b, 0xf2, // fmvd.d.x ft0, s6
];
let mut ft0 = 0x12341234;
let mut s6 = 0x56785678;
let x3 = 0x6000;
let mut uc = uc_common_setup(Arch::RISCV, Mode::RISCV64, None, &code, ());
uc.reg_write(RegisterRISCV::FT0, ft0).unwrap();
uc.reg_write(RegisterRISCV::S6, s6).unwrap();
// mstatus.fs
uc.reg_write(RegisterRISCV::X3, x3).unwrap();
uc.emu_start(CODE_START, u64::MAX, 0, 2).unwrap();
ft0 = uc.reg_read(RegisterRISCV::FT0).unwrap();
s6 = uc.reg_read(RegisterRISCV::S6).unwrap();
assert_eq!(ft0, 0x56785678);
assert_eq!(s6, 0x56785678);
}
#[test]
fn test_riscv64_fp_move_from_int_reg_write() {
let code = [
0x53, 0x00, 0x0b, 0xf2, // fmvd.d.x ft0, s6
];
let mut ft0 = 0x12341234;
let mut s6 = 0x56785678;
let mstatus = 0x6000;
let mut uc = uc_common_setup(Arch::RISCV, Mode::RISCV64, None, &code, ());
uc.reg_write(RegisterRISCV::FT0, ft0).unwrap();
uc.reg_write(RegisterRISCV::S6, s6).unwrap();
// mstatus.fs
uc.reg_write(RegisterRISCV::MSTATUS, mstatus).unwrap();
uc.emu_start(CODE_START, u64::MAX, 0, 1).unwrap();
ft0 = uc.reg_read(RegisterRISCV::FT0).unwrap();
s6 = uc.reg_read(RegisterRISCV::S6).unwrap();
assert_eq!(ft0, 0x56785678);
assert_eq!(s6, 0x56785678);
}
#[test]
fn test_riscv64_fp_move_to_int() {
let code = [
0xf3, 0x90, 0x01, 0x30, // csrrw x2, mstatus, x3;
0x53, 0x0b, 0x00, 0xe2, // fmv.x.d s6, ft0
];
let mut ft0 = 0x12341234;
let mut s6 = 0x56785678;
let x3 = 0x6000;
let mut uc = uc_common_setup(Arch::RISCV, Mode::RISCV64, None, &code, ());
uc.reg_write(RegisterRISCV::FT0, ft0).unwrap();
uc.reg_write(RegisterRISCV::S6, s6).unwrap();
// mstatus.fs
uc.reg_write(RegisterRISCV::X3, x3).unwrap();
uc.emu_start(CODE_START, u64::MAX, 0, 2).unwrap();
ft0 = uc.reg_read(RegisterRISCV::FT0).unwrap();
s6 = uc.reg_read(RegisterRISCV::S6).unwrap();
assert_eq!(ft0, 0x12341234);
assert_eq!(s6, 0x12341234);
}
#[test]
fn test_riscv64_code_patching() {
let code = [
0x93, 0x82, 0x12, 0x00, // addi t0, t0, 0x1
];
let mut uc = uc_common_setup(Arch::RISCV, Mode::RISCV64, None, &code, ());
// Zero out t0 and t1
let mut t0 = 0x0;
uc.reg_write(RegisterRISCV::T0, t0).unwrap();
// emulate the instruction
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
// check value
t0 = uc.reg_read(RegisterRISCV::T0).unwrap();
assert_eq!(t0, 0x1);
// patch instruction
let patch_code = [
0x93, 0x82, 0xf2, 0x7f, // addi t0, t0, 0x7FF
];
uc.mem_write(CODE_START, &patch_code).unwrap();
// zero out t0
t0 = 0x0;
uc.reg_write(RegisterRISCV::T0, t0).unwrap();
uc.emu_start(CODE_START, CODE_START + patch_code.len() as u64, 0, 0)
.unwrap();
// check value
t0 = uc.reg_read(RegisterRISCV::T0).unwrap();
assert_eq!(t0, 0x7ff);
}
#[test]
fn test_riscv64_code_patching_count() {
let code = [
0x93, 0x82, 0x12, 0x00, // addi t0, t0, 0x1
];
let mut uc = uc_common_setup(Arch::RISCV, Mode::RISCV64, None, &code, ());
// Zero out t0 and t1
let mut t0 = 0x0;
uc.reg_write(RegisterRISCV::T0, t0).unwrap();
// emulate the instruction
uc.emu_start(CODE_START, u64::MAX, 0, 1).unwrap();
// check value
t0 = uc.reg_read(RegisterRISCV::T0).unwrap();
assert_eq!(t0, 0x1);
// patch instruction
let patch_code = [
0x93, 0x82, 0xf2, 0x7f, // addi t0, t0, 0x7FF
];
uc.mem_write(CODE_START, &patch_code).unwrap();
uc.ctl_remove_cache(CODE_START, CODE_START + patch_code.len() as u64)
.unwrap();
// zero out t0
t0 = 0x0;
uc.reg_write(RegisterRISCV::T0, t0).unwrap();
uc.emu_start(CODE_START, u64::MAX, 0, 1).unwrap();
// check value
t0 = uc.reg_read(RegisterRISCV::T0).unwrap();
assert_eq!(t0, 0x7ff);
}
#[test]
fn test_riscv64_ecall() {
let code = [
0x73, 0x00, 0x00, 0x00, // ecall
];
let mut uc = uc_common_setup(Arch::RISCV, Mode::RISCV64, None, &code, ());
uc.add_intr_hook(|uc, _| uc.emu_stop().unwrap()).unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let pc = uc.reg_read(RegisterRISCV::PC).unwrap();
assert_eq!(pc, CODE_START + 4);
}
#[test]
fn test_riscv32_mmio_map() {
#[rustfmt::skip]
let code = [
0x37, 0x17, 0x02, 0x40, // lui a4, 0x40021
0x1c, 0x4f, // c.lw a5, 0x18(a4)
];
let mut uc = uc_common_setup(Arch::RISCV, Mode::RISCV32, None, &code, ());
uc.mmio_map_ro(0x40000000, 0x40000, |uc, offset, _size| {
let a4 = uc.reg_read(RegisterRISCV::A4).unwrap();
assert_eq!(a4, 0x40021 << 12);
assert_eq!(offset, 0x21018);
0
})
.unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
}
#[test]
fn test_riscv32_map() {
#[rustfmt::skip]
let code = [
0x37, 0x17, 0x02, 0x40, // lui a4, 0x40021
0x1c, 0x4f, // c.lw a5, 0x18(a4)
];
let mut uc = uc_common_setup(Arch::RISCV, Mode::RISCV32, None, &code, ());
let val = 0xdeadbeefu64;
uc.mem_map(0x40000000, 0x40000, Prot::ALL).unwrap();
uc.mem_write(0x40000000 + 0x21018, &val.to_le_bytes())
.unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let a5 = uc.reg_read(RegisterRISCV::A5).unwrap();
assert_eq!(a5, val);
}
#[test]
fn test_riscv64_mmio_map() {
#[rustfmt::skip]
let code = [
0x37, 0x17, 0x02, 0x40, // lui a4, 0x40021
0x1c, 0x4f, // c.lw a5, 0x18(a4)
];
let mut uc = uc_common_setup(Arch::RISCV, Mode::RISCV64, None, &code, ());
uc.mmio_map_ro(0x40000000, 0x40000, |uc, offset, _size| {
let a4 = uc.reg_read(RegisterRISCV::A4).unwrap();
assert_eq!(a4, 0x40021 << 12);
assert_eq!(offset, 0x21018);
0
})
.unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
}
#[test]
fn test_riscv_correct_address_in_small_jump_hook() {
#[rustfmt::skip]
let code = [
0xb7, 0x82, 0x00, 0x00, // lui t0, 8
0x9b, 0x82, 0x02, 0xf0, // addiw t0, t0, -256;
0x67, 0x80, 0x02, 0x00, // jr x5
];
let mut uc = uc_common_setup(Arch::RISCV, Mode::RISCV64, None, &code, ());
uc.add_mem_hook(HookType::MEM_UNMAPPED, 1, 0, |uc, _, address, _, _| {
// Check registers
let x5 = uc.reg_read(RegisterRISCV::X5).unwrap();
let pc = uc.reg_read(RegisterRISCV::PC).unwrap();
assert_eq!(x5, 0x7F00);
assert_eq!(pc, 0x7F00);
// Check address
assert_eq!(address, 0x7F00);
false
})
.unwrap();
let err = uc
.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap_err();
assert_eq!(err, uc_error::FETCH_UNMAPPED);
let x5 = uc.reg_read(RegisterRISCV::X5).unwrap();
let pc = uc.reg_read(RegisterRISCV::PC).unwrap();
assert_eq!(x5, 0x7F00);
assert_eq!(pc, 0x7F00);
}
#[test]
fn test_riscv_correct_address_in_long_jump_hook() {
#[rustfmt::skip]
let code = [
0x93, 0x02, 0xf0, 0xff, // addi t0, zero, -1
0x93, 0x92, 0xf2, 0x03, // slli t0, t0, 63
0x93, 0x82, 0x02, 0xf0, // addi t0, t0, -256
0x67, 0x80, 0x02, 0x00, // jr x5
];
let mut uc = uc_common_setup(Arch::RISCV, Mode::RISCV64, None, &code, ());
uc.add_mem_hook(HookType::MEM_UNMAPPED, 1, 0, |uc, _, address, _, _| {
// Check registers
let x5 = uc.reg_read(RegisterRISCV::X5).unwrap();
let pc = uc.reg_read(RegisterRISCV::PC).unwrap();
assert_eq!(x5, 0x7FFFFFFFFFFFFF00);
assert_eq!(pc, 0x7FFFFFFFFFFFFF00);
// Check address
assert_eq!(address, 0x7FFFFFFFFFFFFF00);
false
})
.unwrap();
let err = uc
.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap_err();
assert_eq!(err, uc_error::FETCH_UNMAPPED);
let x5 = uc.reg_read(RegisterRISCV::X5).unwrap();
let pc = uc.reg_read(RegisterRISCV::PC).unwrap();
assert_eq!(x5, 0x7FFFFFFFFFFFFF00);
assert_eq!(pc, 0x7FFFFFFFFFFFFF00);
}
#[test]
fn test_riscv_mmu() {
fn test_riscv_mmu_prepare_tlb(uc: &mut Unicorn<'_, ()>, data_address: u64, code_address: u64) {
let sptbr = 0x2000;
uc.mem_map(sptbr, 0x3000, Prot::ALL).unwrap(); // tlb base
let tlbe = ((sptbr + 0x1000) >> 2) | 1;
uc.mem_write(sptbr, &tlbe.to_le_bytes()).unwrap();
let tlbe = ((sptbr + 0x2000) >> 2) | 1;
uc.mem_write(sptbr + 0x1000, &tlbe.to_le_bytes()).unwrap();
let tlbe = (code_address >> 2) | (7 << 1) | 1;
uc.mem_write(sptbr + 0x2000 + 0x15 * 8, &tlbe.to_le_bytes())
.unwrap();
let tlbe = (data_address >> 2) | (7 << 1) | 1;
uc.mem_write(sptbr + 0x2000 + 0x16 * 8, &tlbe.to_le_bytes())
.unwrap();
}
let code_address = 0x5000;
let data_address = 0x6000;
let data_value = 0x41414141u32;
let code_m = [
0x1b, 0x0e, 0xf0, 0xff, // li t3, (8 << 60) | 2
0x13, 0x1e, 0xfe, 0x03, // csrw sptbr, t3
0x13, 0x0e, 0x2e, 0x00, // li t0, (1 << 11) | (1 << 5)
0x73, 0x10, 0x0e, 0x18, // csrw mstatus, t0
0xb7, 0x12, 0x00, 0x00, // la t1, 0x15000
0x9b, 0x82, 0x02, 0x82, // csrw mepc, t1
0x73, 0x90, 0x02, 0x30, // mret
0x37, 0x53, 0x01, 0x00, // lui t0, 8
0x73, 0x10, 0x13, 0x34, // csrw mepc, t1
0x73, 0x00, 0x20, 0x30, // mret
];
#[rustfmt::skip]
let code_s = [
0xb7, 0x42, 0x41, 0x41, 0x9b, 0x82, 0x12, 0x14, // li t0, 0x41414141
0x37, 0x63, 0x01, 0x00, // li t1, 0x16000
0x23, 0x20, 0x53, 0x00, // sw t0, 0(t1)
0x13, 0x00, 0x00, 0x00, // nop
];
let mut uc = Unicorn::new(Arch::RISCV, Mode::RISCV64).unwrap();
uc.ctl_set_tlb_type(TlbType::CPU).unwrap();
uc.add_code_hook(1, 0, |uc, address, _| {
if address == 0x15010 {
uc.emu_stop().unwrap();
}
})
.unwrap();
uc.mem_map(0x1000, 0x1000, Prot::ALL).unwrap();
uc.mem_map(code_address, 0x1000, Prot::ALL).unwrap();
uc.mem_map(data_address, 0x1000, Prot::ALL).unwrap();
uc.mem_write(code_address, &code_s).unwrap();
uc.mem_write(0x1000, &code_m).unwrap();
test_riscv_mmu_prepare_tlb(&mut uc, data_address, code_address);
uc.emu_start(0x1000, 0x1000 + code_m.len() as u64, 0, 0)
.unwrap();
let data_result = u32::from_le_bytes(
uc.mem_read_as_vec(data_address, 4)
.unwrap()
.try_into()
.unwrap(),
);
assert_eq!(data_value, data_result);
}
#[test]
fn test_riscv_priv() {
let m_entry_address = 0x1000;
let main_address = 0x3000;
#[rustfmt::skip]
let code_m_entry = [
0x93, 0x02, 0x00, 0x00, // li t0, 0
0x73, 0x90, 0x02, 0x30, // csrw mstatus, t0
0x37, 0x33, 0x00, 0x00, // li t1, 0x3000
0x73, 0x10, 0x13, 0x34, // csrw mepc, t1
0x73, 0x00, 0x20, 0x30, // mret
];
#[rustfmt::skip]
let code_main = [
0x73, 0x90, 0x02, 0x14, // csrw sscratch, t1
0x13, 0x00, 0x00, 0x00, // nop
];
let main_end_address = main_address + code_main.len() as u64;
let mut uc = Unicorn::new(Arch::RISCV, Mode::RISCV64).unwrap();
uc.ctl_set_tlb_type(TlbType::CPU).unwrap();
uc.mem_map(m_entry_address, 0x1000, Prot::ALL).unwrap();
uc.mem_map(main_address, 0x1000, Prot::ALL).unwrap();
uc.mem_write(m_entry_address, &code_m_entry).unwrap();
uc.mem_write(main_address, &code_main).unwrap();
// Before anything executes we should be in M-Mode
let mut priv_value = uc.reg_read(RegisterRISCV::PRIV).unwrap();
assert_eq!(priv_value, 3);
// We'll put a sentinel value in sscratch so we can determine whether we've
// successfully written to it below.
let mut reg_value = 0xffff;
uc.reg_write(RegisterRISCV::SSCRATCH, reg_value).unwrap();
// Run until we reach the "csrw" at the start of code_main, at which
// point we should be in U-Mode due to the mret instruction.
uc.emu_start(m_entry_address, main_address, 0, 10).unwrap();
let mut pc = uc.reg_read(RegisterRISCV::PC).unwrap();
assert_eq!(pc, main_address);
priv_value = uc.reg_read(RegisterRISCV::PRIV).unwrap();
assert_eq!(priv_value, 0); // Now in U-Mode
// U-Mode can't write to sscratch, so execution at this point should
// cause an invalid instruction exception.
let err = uc
.emu_start(main_address, main_end_address, 0, 0)
.unwrap_err();
assert_eq!(err, uc_error::EXCEPTION);
pc = uc.reg_read(RegisterRISCV::PC).unwrap();
assert_eq!(pc, main_address + 4);
// ...but if we force S-Mode then we should be able to set it successfully.
priv_value = 1;
uc.reg_write(RegisterRISCV::PRIV, priv_value).unwrap();
uc.emu_start(main_address, main_end_address, 0, 0).unwrap();
reg_value = uc.reg_read(RegisterRISCV::SSCRATCH).unwrap();
assert_eq!(reg_value, 0);
pc = uc.reg_read(RegisterRISCV::PC).unwrap();
assert_eq!(pc, main_end_address);
}

View File

@@ -0,0 +1,25 @@
use unicorn_engine_sys::RegisterS390X;
use super::*;
#[test]
fn test_s390x_lr() {
let code = [
0x18, 0x23, // lr %r2, %r3
];
let r3 = 0x114514;
let mut uc = uc_common_setup(Arch::S390X, Mode::BIG_ENDIAN, None, &code, ());
uc.reg_write(RegisterS390X::R3, r3).unwrap();
uc.emu_start(CODE_START, CODE_START + code.len() as u64, 0, 0)
.unwrap();
let r2 = uc.reg_read(RegisterS390X::R2).unwrap();
let pc = uc.reg_read(RegisterS390X::PC).unwrap();
assert_eq!(r2, 0x114514);
assert_eq!(pc, CODE_START + code.len() as u64);
}

View File

@@ -1,158 +0,0 @@
#![allow(non_camel_case_types)]
// TRICORE registers
#[repr(C)]
#[derive(PartialEq, Debug, Clone, Copy)]
pub enum RegisterTRICORE {
INVALID = 0,
A0 = 1,
A1 = 2,
A2 = 3,
A3 = 4,
A4 = 5,
A5 = 6,
A6 = 7,
A7 = 8,
A8 = 9,
A9 = 10,
A10 = 11,
A11 = 12,
A12 = 13,
A13 = 14,
A14 = 15,
A15 = 16,
D0 = 17,
D1 = 18,
D2 = 19,
D3 = 20,
D4 = 21,
D5 = 22,
D6 = 23,
D7 = 24,
D8 = 25,
D9 = 26,
D10 = 27,
D11 = 28,
D12 = 29,
D13 = 30,
D14 = 31,
D15 = 32,
PCXI = 33,
PSW = 34,
PSW_USB_C = 35,
PSW_USB_V = 36,
PSW_USB_SV = 37,
PSW_USB_AV = 38,
PSW_USB_SAV = 39,
PC = 40,
SYSCON = 41,
CPU_ID = 42,
BIV = 43,
BTV = 44,
ISP = 45,
ICR = 46,
FCX = 47,
LCX = 48,
COMPAT = 49,
DPR0_U = 50,
DPR1_U = 51,
DPR2_U = 52,
DPR3_U = 53,
DPR0_L = 54,
DPR1_L = 55,
DPR2_L = 56,
DPR3_L = 57,
CPR0_U = 58,
CPR1_U = 59,
CPR2_U = 60,
CPR3_U = 61,
CPR0_L = 62,
CPR1_L = 63,
CPR2_L = 64,
CPR3_L = 65,
DPM0 = 66,
DPM1 = 67,
DPM2 = 68,
DPM3 = 69,
CPM0 = 70,
CPM1 = 71,
CPM2 = 72,
CPM3 = 73,
MMU_CON = 74,
MMU_ASI = 75,
MMU_TVA = 76,
MMU_TPA = 77,
MMU_TPX = 78,
MMU_TFA = 79,
BMACON = 80,
SMACON = 81,
DIEAR = 82,
DIETR = 83,
CCDIER = 84,
MIECON = 85,
PIEAR = 86,
PIETR = 87,
CCPIER = 88,
DBGSR = 89,
EXEVT = 90,
CREVT = 91,
SWEVT = 92,
TR0EVT = 93,
TR1EVT = 94,
DMS = 95,
DCX = 96,
DBGTCR = 97,
CCTRL = 98,
CCNT = 99,
ICNT = 100,
M1CNT = 101,
M2CNT = 102,
M3CNT = 103,
ENDING = 104,
}
impl RegisterTRICORE {
// alias registers
// (assoc) GA0 = 1,
// (assoc) GA1 = 2,
// (assoc) GA8 = 9,
// (assoc) GA9 = 10,
// (assoc) SP = 11,
// (assoc) LR = 12,
// (assoc) IA = 16,
// (assoc) ID = 32,
pub const GA0: RegisterTRICORE = RegisterTRICORE::A0;
pub const GA1: RegisterTRICORE = RegisterTRICORE::A1;
pub const GA8: RegisterTRICORE = RegisterTRICORE::A8;
pub const GA9: RegisterTRICORE = RegisterTRICORE::A9;
pub const SP: RegisterTRICORE = RegisterTRICORE::A10;
pub const LR: RegisterTRICORE = RegisterTRICORE::A11;
pub const IA: RegisterTRICORE = RegisterTRICORE::A15;
pub const ID: RegisterTRICORE = RegisterTRICORE::D15;
}
impl From<RegisterTRICORE> for i32 {
fn from(r: RegisterTRICORE) -> Self {
r as i32
}
}
#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum TricoreCpuModel {
UC_CPU_TRICORE_TC1796,
UC_CPU_TRICORE_TC1797,
UC_CPU_TRICORE_TC27X,
}
impl From<TricoreCpuModel> for i32 {
fn from(value: TricoreCpuModel) -> Self {
value as i32
}
}
impl From<&TricoreCpuModel> for i32 {
fn from(value: &TricoreCpuModel) -> Self {
(*value) as i32
}
}

View File

@@ -1,5 +1,4 @@
#![allow(non_camel_case_types)] pub use unicorn_engine_sys::*;
use bitflags::bitflags;
pub const API_MAJOR: u64 = 2; pub const API_MAJOR: u64 = 2;
pub const API_MINOR: u64 = 1; pub const API_MINOR: u64 = 1;
@@ -8,309 +7,22 @@ pub const VERSION_MINOR: u64 = 1;
pub const VERSION_PATCH: u64 = 3; pub const VERSION_PATCH: u64 = 3;
pub const VERSION_EXTRA: u64 = 255; pub const VERSION_EXTRA: u64 = 255;
pub const SECOND_SCALE: u64 = 1_000_000; pub const SECOND_SCALE: u64 = 1_000_000;
pub const MILISECOND_SCALE: u64 = 1_000; pub const MILLISECOND_SCALE: u64 = 1_000;
#[repr(C)]
#[derive(PartialEq, Debug, Clone, Copy)]
#[allow(clippy::upper_case_acronyms)]
pub enum uc_error {
OK = 0,
NOMEM = 1,
ARCH = 2,
HANDLE = 3,
MODE = 4,
VERSION = 5,
READ_UNMAPPED = 6,
WRITE_UNMAPPED = 7,
FETCH_UNMAPPED = 8,
HOOK = 9,
INSN_INVALID = 10,
MAP = 11,
WRITE_PROT = 12,
READ_PROT = 13,
FETCH_PROT = 14,
ARG = 15,
READ_UNALIGNED = 16,
WRITE_UNALIGNED = 17,
FETCH_UNALIGNED = 18,
HOOK_EXIST = 19,
RESOURCE = 20,
EXCEPTION = 21,
}
impl uc_error {
/// Calls op if the result is Ok, otherwise returns the Err value of self.
/// This function can be used for control flow based on Result values.
pub fn and_then<U, F: FnOnce() -> Result<U, uc_error>>(self, op: F) -> Result<U, uc_error> {
if let Self::OK = self {
op()
} else {
Err(self)
}
}
/// Returns res if the result is Ok, otherwise returns the Err value of self.
/// Arguments passed to and are eagerly evaluated; if you are passing the result
/// of a function call, it is recommended to use and_then, which is lazily evaluated.
pub fn and<U>(self, res: Result<U, uc_error>) -> Result<U, uc_error> {
if let Self::OK = self {
res
} else {
Err(self)
}
}
}
impl From<uc_error> for Result<(), uc_error> {
fn from(value: uc_error) -> Self {
if let uc_error::OK = value {
Ok(())
} else {
Err(value)
}
}
}
#[repr(C)]
#[derive(PartialEq, Debug, Clone, Copy)]
pub enum MemType {
READ = 16,
WRITE = 17,
FETCH = 18,
READ_UNMAPPED = 19,
WRITE_UNMAPPED = 20,
FETCH_UNMAPPED = 21,
WRITE_PROT = 22,
READ_PROT = 23,
FETCH_PROT = 24,
READ_AFTER = 25,
}
#[repr(C)]
#[derive(PartialEq, Debug, Clone, Copy)]
pub enum TlbType {
CPU = 0,
VIRTUAL = 1,
}
bitflags! {
#[repr(C)]
#[derive(Copy, Clone)]
pub struct HookType: i32 {
const INTR = 1;
const INSN = 2;
const CODE = 4;
const BLOCK = 8;
const MEM_READ_UNMAPPED = 0x10;
const MEM_WRITE_UNMAPPED = 0x20;
const MEM_FETCH_UNMAPPED = 0x40;
const MEM_UNMAPPED = Self::MEM_READ_UNMAPPED.bits() | Self::MEM_WRITE_UNMAPPED.bits() | Self::MEM_FETCH_UNMAPPED.bits();
const MEM_READ_PROT = 0x80;
const MEM_WRITE_PROT = 0x100;
const MEM_FETCH_PROT = 0x200;
const MEM_PROT = Self::MEM_READ_PROT.bits() | Self::MEM_WRITE_PROT.bits() | Self::MEM_FETCH_PROT.bits();
const MEM_READ = 0x400;
const MEM_WRITE = 0x800;
const MEM_FETCH = 0x1000;
const MEM_VALID = Self::MEM_READ.bits() | Self::MEM_WRITE.bits() | Self::MEM_FETCH.bits();
const MEM_READ_AFTER = 0x2000;
const INSN_INVALID = 0x4000;
const MEM_READ_INVALID = Self::MEM_READ_UNMAPPED.bits() | Self::MEM_READ_PROT.bits();
const MEM_WRITE_INVALID = Self::MEM_WRITE_UNMAPPED.bits() | Self::MEM_WRITE_PROT.bits();
const MEM_FETCH_INVALID = Self::MEM_FETCH_UNMAPPED.bits() | Self::MEM_FETCH_PROT.bits();
const MEM_INVALID = Self::MEM_READ_INVALID.bits() | Self::MEM_WRITE_INVALID.bits() | Self::MEM_FETCH_INVALID.bits();
const MEM_ALL = Self::MEM_VALID.bits() | Self::MEM_INVALID.bits();
const TCG_OPCODE = (1 << 16);
const TLB = (1 << 17);
}
}
bitflags! {
#[repr(C)]
#[derive(Copy, Clone)]
pub struct TcgOp: i32 {
const SUB = 0;
}
}
bitflags! {
#[repr(C)]
#[derive(Copy, Clone)]
pub struct TcgOpFlag: i32 {
const CMP = 1;
const DIRECT = 2;
}
}
#[repr(C)]
#[derive(PartialEq, Debug, Clone, Copy)]
#[allow(clippy::upper_case_acronyms)]
pub enum Query {
MODE = 1,
PAGE_SIZE = 2,
ARCH = 3,
TIMEOUT = 4,
}
bitflags! {
#[repr(C)]
#[derive(Copy, Clone, Debug)]
pub struct Permission : u32 {
const NONE = 0;
const READ = 1;
const WRITE = 2;
const EXEC = 4;
const ALL = Self::READ.bits() | Self::WRITE.bits() | Self::EXEC.bits();
}
}
#[repr(C)]
#[derive(Debug, Clone)]
pub struct MemRegion {
pub begin: u64,
pub end: u64,
pub perms: Permission,
}
#[repr(C)]
#[derive(PartialEq, Debug, Clone, Copy)]
pub enum Arch {
ARM = 1,
ARM64 = 2,
MIPS = 3,
X86 = 4,
PPC = 5,
SPARC = 6,
M68K = 7,
RISCV = 8,
S390X = 9,
TRICORE = 10,
MAX = 11,
}
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),
}
}
}
bitflags! {
#[derive(Copy, Clone)]
#[repr(C)]
pub struct Mode: i32 {
const LITTLE_ENDIAN = 0;
const BIG_ENDIAN = 0x4000_0000;
const ARM = 0;
const THUMB = 0x10;
const MCLASS = 0x20;
const V8 = 0x40;
const ARMBE8 = 0x400;
const ARM926 = 0x80;
const ARM946 = 0x100;
const ARM1176 = 0x200;
const MICRO = Self::THUMB.bits();
const MIPS3 = Self::MCLASS.bits();
const MIPS32R6 = Self::V8.bits();
const MIPS32 = 4;
const MIPS64 = 8;
const MODE_16 = 2;
const MODE_32 = Self::MIPS32.bits();
const MODE_64 = Self::MIPS64.bits();
const PPC32 = Self::MIPS32.bits();
const PPC64 = Self::MIPS64.bits();
const QPX = Self::THUMB.bits();
const SPARC32 = Self::MIPS32.bits();
const SPARC64 = Self::MIPS64.bits();
const V9 = Self::THUMB.bits();
const RISCV32 = Self::MIPS32.bits();
const RISCV64 = Self::MIPS64.bits();
}
}
// Represent a TranslationBlock.
#[repr(C)]
pub struct TranslationBlock {
pub pc: u64,
pub icount: u16,
pub size: u16,
}
macro_rules! UC_CTL_READ { macro_rules! UC_CTL_READ {
($expr:expr) => { ($expr:expr) => {
$expr as u32 | ControlType::UC_CTL_IO_READ as u32 ControlType($expr.0 | ControlType::IO_READ.0)
}; };
} }
macro_rules! UC_CTL_WRITE { macro_rules! UC_CTL_WRITE {
($expr:expr) => { ($expr:expr) => {
$expr as u32 | ControlType::UC_CTL_IO_WRITE as u32 ControlType($expr.0 | ControlType::IO_WRITE.0)
}; };
} }
macro_rules! UC_CTL_READ_WRITE { macro_rules! UC_CTL_READ_WRITE {
($expr:expr) => { ($expr:expr) => {
$expr as u32 | ControlType::UC_CTL_IO_WRITE as u32 | ControlType::UC_CTL_IO_READ as u32 ControlType($expr.0 | ControlType::IO_WRITE.0 | ControlType::IO_READ.0)
}; };
} }
#[allow(clippy::upper_case_acronyms)]
#[repr(u64)]
pub enum ControlType {
UC_CTL_UC_MODE = 0,
UC_CTL_UC_PAGE_SIZE = 1,
UC_CTL_UC_ARCH = 2,
UC_CTL_UC_TIMEOUT = 3,
UC_CTL_UC_USE_EXITS = 4,
UC_CTL_UC_EXITS_CNT = 5,
UC_CTL_UC_EXITS = 6,
UC_CTL_CPU_MODEL = 7,
UC_CTL_TB_REQUEST_CACHE = 8,
UC_CTL_TB_REMOVE_CACHE = 9,
UC_CTL_TB_FLUSH = 10,
UC_CTL_TLB_FLUSH = 11,
UC_CTL_TLB_TYPE = 12,
UC_CTL_TCG_BUFFER_SIZE = 13,
UC_CTL_CONTEXT_MODE = 14,
UC_CTL_IO_READ = 1 << 31,
UC_CTL_IO_WRITE = 1 << 30,
}
bitflags! {
#[derive(Debug, Copy, Clone)]
#[repr(C)]
pub struct ContextMode : u32 {
const CPU = 1;
const Memory = 2;
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct TlbEntry {
pub paddr: u64,
pub perms: Permission,
}

View File

@@ -1,337 +0,0 @@
#![allow(non_camel_case_types)]
// X86 registers
#[repr(C)]
#[derive(PartialEq, Debug, Clone, Copy)]
#[allow(clippy::upper_case_acronyms)]
pub enum RegisterX86 {
INVALID = 0,
AH = 1,
AL = 2,
AX = 3,
BH = 4,
BL = 5,
BP = 6,
BPL = 7,
BX = 8,
CH = 9,
CL = 10,
CS = 11,
CX = 12,
DH = 13,
DI = 14,
DIL = 15,
DL = 16,
DS = 17,
DX = 18,
EAX = 19,
EBP = 20,
EBX = 21,
ECX = 22,
EDI = 23,
EDX = 24,
EFLAGS = 25,
EIP = 26,
ES = 28,
ESI = 29,
ESP = 30,
FPSW = 31,
FS = 32,
GS = 33,
IP = 34,
RAX = 35,
RBP = 36,
RBX = 37,
RCX = 38,
RDI = 39,
RDX = 40,
RIP = 41,
RSI = 43,
RSP = 44,
SI = 45,
SIL = 46,
SP = 47,
SPL = 48,
SS = 49,
CR0 = 50,
CR1 = 51,
CR2 = 52,
CR3 = 53,
CR4 = 54,
CR8 = 58,
DR0 = 66,
DR1 = 67,
DR2 = 68,
DR3 = 69,
DR4 = 70,
DR5 = 71,
DR6 = 72,
DR7 = 73,
FP0 = 82,
FP1 = 83,
FP2 = 84,
FP3 = 85,
FP4 = 86,
FP5 = 87,
FP6 = 88,
FP7 = 89,
K0 = 90,
K1 = 91,
K2 = 92,
K3 = 93,
K4 = 94,
K5 = 95,
K6 = 96,
K7 = 97,
MM0 = 98,
MM1 = 99,
MM2 = 100,
MM3 = 101,
MM4 = 102,
MM5 = 103,
MM6 = 104,
MM7 = 105,
R8 = 106,
R9 = 107,
R10 = 108,
R11 = 109,
R12 = 110,
R13 = 111,
R14 = 112,
R15 = 113,
ST0 = 114,
ST1 = 115,
ST2 = 116,
ST3 = 117,
ST4 = 118,
ST5 = 119,
ST6 = 120,
ST7 = 121,
XMM0 = 122,
XMM1 = 123,
XMM2 = 124,
XMM3 = 125,
XMM4 = 126,
XMM5 = 127,
XMM6 = 128,
XMM7 = 129,
XMM8 = 130,
XMM9 = 131,
XMM10 = 132,
XMM11 = 133,
XMM12 = 134,
XMM13 = 135,
XMM14 = 136,
XMM15 = 137,
XMM16 = 138,
XMM17 = 139,
XMM18 = 140,
XMM19 = 141,
XMM20 = 142,
XMM21 = 143,
XMM22 = 144,
XMM23 = 145,
XMM24 = 146,
XMM25 = 147,
XMM26 = 148,
XMM27 = 149,
XMM28 = 150,
XMM29 = 151,
XMM30 = 152,
XMM31 = 153,
YMM0 = 154,
YMM1 = 155,
YMM2 = 156,
YMM3 = 157,
YMM4 = 158,
YMM5 = 159,
YMM6 = 160,
YMM7 = 161,
YMM8 = 162,
YMM9 = 163,
YMM10 = 164,
YMM11 = 165,
YMM12 = 166,
YMM13 = 167,
YMM14 = 168,
YMM15 = 169,
YMM16 = 170,
YMM17 = 171,
YMM18 = 172,
YMM19 = 173,
YMM20 = 174,
YMM21 = 175,
YMM22 = 176,
YMM23 = 177,
YMM24 = 178,
YMM25 = 179,
YMM26 = 180,
YMM27 = 181,
YMM28 = 182,
YMM29 = 183,
YMM30 = 184,
YMM31 = 185,
ZMM0 = 186,
ZMM1 = 187,
ZMM2 = 188,
ZMM3 = 189,
ZMM4 = 190,
ZMM5 = 191,
ZMM6 = 192,
ZMM7 = 193,
ZMM8 = 194,
ZMM9 = 195,
ZMM10 = 196,
ZMM11 = 197,
ZMM12 = 198,
ZMM13 = 199,
ZMM14 = 200,
ZMM15 = 201,
ZMM16 = 202,
ZMM17 = 203,
ZMM18 = 204,
ZMM19 = 205,
ZMM20 = 206,
ZMM21 = 207,
ZMM22 = 208,
ZMM23 = 209,
ZMM24 = 210,
ZMM25 = 211,
ZMM26 = 212,
ZMM27 = 213,
ZMM28 = 214,
ZMM29 = 215,
ZMM30 = 216,
ZMM31 = 217,
R8B = 218,
R9B = 219,
R10B = 220,
R11B = 221,
R12B = 222,
R13B = 223,
R14B = 224,
R15B = 225,
R8D = 226,
R9D = 227,
R10D = 228,
R11D = 229,
R12D = 230,
R13D = 231,
R14D = 232,
R15D = 233,
R8W = 234,
R9W = 235,
R10W = 236,
R11W = 237,
R12W = 238,
R13W = 239,
R14W = 240,
R15W = 241,
IDTR = 242,
GDTR = 243,
LDTR = 244,
TR = 245,
FPCW = 246,
FPTAG = 247,
MSR = 248,
MXCSR = 249,
FS_BASE = 250,
GS_BASE = 251,
FLAGS = 252,
RFLAGS = 253,
FIP = 254,
FCS = 255,
FDP = 256,
FDS = 257,
FOP = 258,
ENDING = 259,
}
impl From<RegisterX86> for i32 {
fn from(r: RegisterX86) -> Self {
r as i32
}
}
#[repr(C)]
#[derive(PartialEq, Debug, Clone, Copy)]
#[allow(clippy::upper_case_acronyms)]
pub enum InsnX86 {
IN = 218,
OUT = 500,
SYSCALL = 699,
SYSENTER = 700,
RET = 151,
}
#[repr(C)]
#[derive(PartialEq, Debug, Clone, Copy)]
#[allow(clippy::upper_case_acronyms)]
pub enum InsnSysX86 {
SYSCALL = InsnX86::SYSCALL as isize,
SYSENTER = InsnX86::SYSENTER as isize,
}
#[repr(C)]
#[derive(PartialEq, Debug, Clone, Copy)]
pub struct X86Mmr {
pub selector: u64,
pub base: u64,
pub limit: u32,
pub flags: u32,
}
#[repr(i32)]
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum X86CpuModel {
UC_CPU_X86_QEMU64 = 0,
UC_CPU_X86_PHENOM,
UC_CPU_X86_CORE2DUO,
UC_CPU_X86_KVM64,
UC_CPU_X86_QEMU32,
UC_CPU_X86_KVM32,
UC_CPU_X86_COREDUO,
UC_CPU_X86_486,
UC_CPU_X86_PENTIUM,
UC_CPU_X86_PENTIUM2,
UC_CPU_X86_PENTIUM3,
UC_CPU_X86_ATHLON,
UC_CPU_X86_N270,
UC_CPU_X86_CONROE,
UC_CPU_X86_PENRYN,
UC_CPU_X86_NEHALEM,
UC_CPU_X86_WESTMERE,
UC_CPU_X86_SANDYBRIDGE,
UC_CPU_X86_IVYBRIDGE,
UC_CPU_X86_HASWELL,
UC_CPU_X86_BROADWELL,
UC_CPU_X86_SKYLAKE_CLIENT,
UC_CPU_X86_SKYLAKE_SERVER,
UC_CPU_X86_CASCADELAKE_SERVER,
UC_CPU_X86_COOPERLAKE,
UC_CPU_X86_ICELAKE_CLIENT,
UC_CPU_X86_ICELAKE_SERVER,
UC_CPU_X86_DENVERTON,
UC_CPU_X86_SNOWRIDGE,
UC_CPU_X86_KNIGHTSMILL,
UC_CPU_X86_OPTERON_G1,
UC_CPU_X86_OPTERON_G2,
UC_CPU_X86_OPTERON_G3,
UC_CPU_X86_OPTERON_G4,
UC_CPU_X86_OPTERON_G5,
UC_CPU_X86_EPYC,
UC_CPU_X86_DHYANA,
UC_CPU_X86_EPYC_ROME,
}
impl From<X86CpuModel> for i32 {
fn from(value: X86CpuModel) -> Self {
value as i32
}
}
impl From<&X86CpuModel> for i32 {
fn from(value: &X86CpuModel) -> Self {
(*value) as i32
}
}

View 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 = []

View 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
View 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();
}
}
}

View 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
}
}

View File

@@ -61,7 +61,7 @@ typedef enum uc_cpu_mips64 {
} uc_cpu_mips64; } uc_cpu_mips64;
//> MIPS registers //> MIPS registers
typedef enum UC_MIPS_REG { typedef enum uc_mips_reg {
UC_MIPS_REG_INVALID = 0, UC_MIPS_REG_INVALID = 0,
//> General purpose registers //> General purpose registers
UC_MIPS_REG_PC, UC_MIPS_REG_PC,
@@ -271,7 +271,10 @@ typedef enum UC_MIPS_REG {
UC_MIPS_REG_LO1 = UC_MIPS_REG_HI1, UC_MIPS_REG_LO1 = UC_MIPS_REG_HI1,
UC_MIPS_REG_LO2 = UC_MIPS_REG_HI2, UC_MIPS_REG_LO2 = UC_MIPS_REG_HI2,
UC_MIPS_REG_LO3 = UC_MIPS_REG_HI3, UC_MIPS_REG_LO3 = UC_MIPS_REG_HI3,
} UC_MIPS_REG; } uc_mips_reg;
// This is only for backwards compatibility
typedef uc_mips_reg UC_MIPS_REG;
#ifdef __cplusplus #ifdef __cplusplus
} }

View File

@@ -7628,6 +7628,7 @@ static void ppc_tr_translate_insn(DisasContextBase *dcbase, CPUState *cs)
// Unicorn: end address tells us to stop emulation // Unicorn: end address tells us to stop emulation
if (uc_addr_is_exit(uc, ctx->base.pc_next)) { if (uc_addr_is_exit(uc, ctx->base.pc_next)) {
gen_wait(ctx); gen_wait(ctx);
dcbase->is_jmp = DISAS_NORETURN;
return; return;
} }

View File

@@ -1,849 +0,0 @@
extern crate alloc;
use alloc::rc::Rc;
use core::cell::RefCell;
use unicorn_engine::unicorn_const::{
uc_error, Arch, HookType, MemType, Mode, Permission, SECOND_SCALE, TlbEntry, TlbType
};
use unicorn_engine::{InsnSysX86, RegisterARM, RegisterMIPS, RegisterPPC, RegisterX86, Unicorn};
pub static X86_REGISTERS: [RegisterX86; 125] = [
RegisterX86::AH,
RegisterX86::AL,
RegisterX86::AX,
RegisterX86::BH,
RegisterX86::BL,
RegisterX86::BP,
RegisterX86::BPL,
RegisterX86::BX,
RegisterX86::CH,
RegisterX86::CL,
RegisterX86::CS,
RegisterX86::CX,
RegisterX86::DH,
RegisterX86::DI,
RegisterX86::DIL,
RegisterX86::DL,
RegisterX86::DS,
RegisterX86::DX,
RegisterX86::EAX,
RegisterX86::EBP,
RegisterX86::EBX,
RegisterX86::ECX,
RegisterX86::EDI,
RegisterX86::EDX,
RegisterX86::EFLAGS,
RegisterX86::EIP,
RegisterX86::ES,
RegisterX86::ESI,
RegisterX86::ESP,
RegisterX86::FPSW,
RegisterX86::FS,
RegisterX86::GS,
RegisterX86::IP,
RegisterX86::RAX,
RegisterX86::RBP,
RegisterX86::RBX,
RegisterX86::RCX,
RegisterX86::RDI,
RegisterX86::RDX,
RegisterX86::RIP,
RegisterX86::RSI,
RegisterX86::RSP,
RegisterX86::SI,
RegisterX86::SIL,
RegisterX86::SP,
RegisterX86::SPL,
RegisterX86::SS,
RegisterX86::CR0,
RegisterX86::CR1,
RegisterX86::CR2,
RegisterX86::CR3,
RegisterX86::CR4,
RegisterX86::CR8,
RegisterX86::DR0,
RegisterX86::DR1,
RegisterX86::DR2,
RegisterX86::DR3,
RegisterX86::DR4,
RegisterX86::DR5,
RegisterX86::DR6,
RegisterX86::DR7,
RegisterX86::FP0,
RegisterX86::FP1,
RegisterX86::FP2,
RegisterX86::FP3,
RegisterX86::FP4,
RegisterX86::FP5,
RegisterX86::FP6,
RegisterX86::FP7,
RegisterX86::K0,
RegisterX86::K1,
RegisterX86::K2,
RegisterX86::K3,
RegisterX86::K4,
RegisterX86::K5,
RegisterX86::K6,
RegisterX86::K7,
RegisterX86::MM0,
RegisterX86::MM1,
RegisterX86::MM2,
RegisterX86::MM3,
RegisterX86::MM4,
RegisterX86::MM5,
RegisterX86::MM6,
RegisterX86::MM7,
RegisterX86::R8,
RegisterX86::R9,
RegisterX86::R10,
RegisterX86::R11,
RegisterX86::R12,
RegisterX86::R13,
RegisterX86::R14,
RegisterX86::R15,
RegisterX86::ST0,
RegisterX86::ST1,
RegisterX86::ST2,
RegisterX86::ST3,
RegisterX86::ST4,
RegisterX86::ST5,
RegisterX86::ST6,
RegisterX86::ST7,
RegisterX86::R8B,
RegisterX86::R9B,
RegisterX86::R10B,
RegisterX86::R11B,
RegisterX86::R12B,
RegisterX86::R13B,
RegisterX86::R14B,
RegisterX86::R15B,
RegisterX86::R8D,
RegisterX86::R9D,
RegisterX86::R10D,
RegisterX86::R11D,
RegisterX86::R12D,
RegisterX86::R13D,
RegisterX86::R14D,
RegisterX86::R15D,
RegisterX86::R8W,
RegisterX86::R9W,
RegisterX86::R10W,
RegisterX86::R11W,
RegisterX86::R12W,
RegisterX86::R13W,
RegisterX86::R14W,
RegisterX86::R15W,
];
#[test]
fn emulate_x86() {
let x86_code32: Vec<u8> = vec![0x41, 0x4a]; // INC ecx; DEC edx
let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32)
.expect("failed to initialize unicorn instance");
assert_eq!(emu.reg_write(RegisterX86::EAX, 123), Ok(()));
assert_eq!(emu.reg_read(RegisterX86::EAX), Ok(123));
// Attempt to write to memory before mapping it.
assert_eq!(
emu.mem_write(0x1000, &x86_code32),
(Err(uc_error::WRITE_UNMAPPED))
);
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
assert_eq!(
emu.mem_read_as_vec(0x1000, x86_code32.len()),
Ok(x86_code32.clone())
);
assert_eq!(emu.reg_write(RegisterX86::ECX, 10), Ok(()));
assert_eq!(emu.reg_write(RegisterX86::EDX, 50), Ok(()));
assert_eq!(
emu.emu_start(
0x1000,
(0x1000 + x86_code32.len()) as u64,
10 * SECOND_SCALE,
1000
),
Ok(())
);
assert_eq!(emu.reg_read(RegisterX86::ECX), Ok(11));
assert_eq!(emu.reg_read(RegisterX86::EDX), Ok(49));
}
#[test]
fn x86_code_callback() {
#[derive(PartialEq, Debug)]
struct CodeExpectation(u64, u32);
let expects = vec![CodeExpectation(0x1000, 1), CodeExpectation(0x1001, 1)];
let codes: Vec<CodeExpectation> = Vec::new();
let codes_cell = Rc::new(RefCell::new(codes));
let callback_codes = codes_cell.clone();
let callback = move |_: &mut Unicorn<'_, ()>, address: u64, size: u32| {
let mut codes = callback_codes.borrow_mut();
codes.push(CodeExpectation(address, size));
};
let x86_code32: Vec<u8> = vec![0x41, 0x4a]; // INC ecx; DEC edx
let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32)
.expect("failed to initialize unicorn instance");
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
let hook = emu
.add_code_hook(0x1000, 0x2000, callback)
.expect("failed to add code hook");
assert_eq!(
emu.emu_start(0x1000, 0x1002, 10 * SECOND_SCALE, 1000),
Ok(())
);
assert_eq!(expects, *codes_cell.borrow());
assert_eq!(emu.remove_hook(hook), Ok(()));
}
#[test]
fn x86_intr_callback() {
#[derive(PartialEq, Debug)]
struct IntrExpectation(u32);
let expect = IntrExpectation(0x80);
let intr_cell = Rc::new(RefCell::new(IntrExpectation(0)));
let callback_intr = intr_cell.clone();
let callback = move |_: &mut Unicorn<'_, ()>, intno: u32| {
*callback_intr.borrow_mut() = IntrExpectation(intno);
};
let x86_code32: Vec<u8> = vec![0xcd, 0x80]; // INT 0x80;
let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32)
.expect("failed to initialize unicorn instance");
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
let hook = emu
.add_intr_hook(callback)
.expect("failed to add intr hook");
assert_eq!(
emu.emu_start(
0x1000,
0x1000 + x86_code32.len() as u64,
10 * SECOND_SCALE,
1000
),
Ok(())
);
assert_eq!(expect, *intr_cell.borrow());
assert_eq!(emu.remove_hook(hook), Ok(()));
}
#[test]
fn x86_mem_callback() {
#[derive(PartialEq, Debug)]
struct MemExpectation(MemType, u64, usize, i64);
let expects = vec![
MemExpectation(MemType::WRITE, 0x2000, 4, 0xdeadbeef),
MemExpectation(MemType::READ_UNMAPPED, 0x10000, 4, 0),
MemExpectation(MemType::READ, 0x10000, 4, 0),
];
let mems: Vec<MemExpectation> = Vec::new();
let mems_cell = Rc::new(RefCell::new(mems));
let callback_mems = mems_cell.clone();
let callback = move |uc: &mut Unicorn<'_, ()>,
mem_type: MemType,
address: u64,
size: usize,
value: i64| {
let mut mems = callback_mems.borrow_mut();
mems.push(MemExpectation(mem_type, address, size, value));
if mem_type == MemType::READ_UNMAPPED {
uc.mem_map(address, 0x1000, Permission::ALL).unwrap();
}
true
};
// mov eax, 0xdeadbeef;
// mov [0x2000], eax;
// mov eax, [0x10000];
let x86_code32: Vec<u8> = vec![
0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0xA3, 0x00, 0x20, 0x00, 0x00, 0xA1, 0x00, 0x00, 0x01, 0x00,
];
let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32)
.expect("failed to initialize unicorn instance");
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
let hook = emu
.add_mem_hook(HookType::MEM_ALL, 0, u64::MAX, callback)
.expect("failed to add memory hook");
assert_eq!(emu.reg_write(RegisterX86::EAX, 0x123), Ok(()));
assert_eq!(
emu.emu_start(
0x1000,
0x1000 + x86_code32.len() as u64,
10 * SECOND_SCALE,
0x1000
),
Ok(())
);
assert_eq!(expects, *mems_cell.borrow());
assert_eq!(emu.remove_hook(hook), Ok(()));
}
#[test]
fn x86_insn_in_callback() {
#[derive(PartialEq, Debug)]
struct InsnInExpectation(u32, usize);
let expect = InsnInExpectation(0x10, 4);
let insn_cell = Rc::new(RefCell::new(InsnInExpectation(0, 0)));
let callback_insn = insn_cell.clone();
let callback = move |_: &mut Unicorn<()>, port: u32, size: usize| {
*callback_insn.borrow_mut() = InsnInExpectation(port, size);
42
};
let x86_code32: Vec<u8> = vec![0xe5, 0x10]; // IN eax, 0x10;
let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32)
.expect("failed to initialize unicorn instance");
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
let hook = emu
.add_insn_in_hook(callback)
.expect("failed to add in hook");
assert_eq!(
emu.emu_start(
0x1000,
0x1000 + x86_code32.len() as u64,
10 * SECOND_SCALE,
1000
),
Ok(())
);
assert_eq!(expect, *insn_cell.borrow());
assert_eq!(emu.reg_read(RegisterX86::EAX), Ok(42));
assert_eq!(emu.remove_hook(hook), Ok(()));
}
#[test]
fn x86_insn_out_callback() {
#[derive(PartialEq, Debug)]
struct InsnOutExpectation(u32, usize, u32);
let expect = InsnOutExpectation(0x46, 1, 0x32);
let insn_cell = Rc::new(RefCell::new(InsnOutExpectation(0, 0, 0)));
let callback_insn = insn_cell.clone();
let callback = move |_: &mut Unicorn<'_, ()>, port: u32, size: usize, value: u32| {
*callback_insn.borrow_mut() = InsnOutExpectation(port, size, value);
};
let x86_code32: Vec<u8> = vec![0xb0, 0x32, 0xe6, 0x46]; // MOV al, 0x32; OUT 0x46, al;
let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32)
.expect("failed to initialize unicorn instance");
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
let hook = emu
.add_insn_out_hook(callback)
.expect("failed to add out hook");
assert_eq!(
emu.emu_start(
0x1000,
0x1000 + x86_code32.len() as u64,
10 * SECOND_SCALE,
1000
),
Ok(())
);
assert_eq!(expect, *insn_cell.borrow());
assert_eq!(emu.remove_hook(hook), Ok(()));
}
#[test]
fn x86_insn_sys_callback() {
#[derive(PartialEq, Debug)]
struct InsnSysExpectation(u64);
let expect = InsnSysExpectation(0xdeadbeef);
let insn_cell = Rc::new(RefCell::new(InsnSysExpectation(0)));
let callback_insn = insn_cell.clone();
let callback = move |uc: &mut Unicorn<'_, ()>| {
println!("!!!!");
let rax = uc.reg_read(RegisterX86::RAX).unwrap();
*callback_insn.borrow_mut() = InsnSysExpectation(rax);
};
// MOV rax, 0xdeadbeef; SYSCALL;
let x86_code: Vec<u8> = vec![
0x48, 0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x05,
];
let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_64)
.expect("failed to initialize unicorn instance");
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
assert_eq!(emu.mem_write(0x1000, &x86_code), Ok(()));
let hook = emu
.add_insn_sys_hook(InsnSysX86::SYSCALL, 1, 0, callback)
.expect("failed to add syscall hook");
assert_eq!(
emu.emu_start(
0x1000,
0x1000 + x86_code.len() as u64,
10 * SECOND_SCALE,
1000
),
Ok(())
);
assert_eq!(expect, *insn_cell.borrow());
assert_eq!(emu.remove_hook(hook), Ok(()));
}
#[test]
fn x86_mmio() {
#[derive(PartialEq, Debug)]
struct MmioReadExpectation(u64, usize);
#[derive(PartialEq, Debug)]
struct MmioWriteExpectation(u64, usize, u64);
let read_expect = MmioReadExpectation(4, 4);
let write_expect = MmioWriteExpectation(8, 2, 42);
let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_64)
.expect("failed to initialize unicorn instance");
assert_eq!(emu.mem_map(0x1000, 0x1000, Permission::ALL), Ok(()));
{
// MOV eax, [0x2004]; MOV [0x2008], ax;
let x86_code: Vec<u8> = vec![
0x8B, 0x04, 0x25, 0x04, 0x20, 0x00, 0x00, 0x66, 0x89, 0x04, 0x25, 0x08, 0x20, 0x00,
0x00,
];
let read_cell = Rc::new(RefCell::new(MmioReadExpectation(0, 0)));
let cb_read_cell = read_cell.clone();
let read_callback = move |_: &mut Unicorn<'_, ()>, offset, size| {
*cb_read_cell.borrow_mut() = MmioReadExpectation(offset, size);
42
};
let write_cell = Rc::new(RefCell::new(MmioWriteExpectation(0, 0, 0)));
let cb_write_cell = write_cell.clone();
let write_callback = move |_: &mut Unicorn<'_, ()>, offset, size, value| {
*cb_write_cell.borrow_mut() = MmioWriteExpectation(offset, size, value);
};
assert_eq!(emu.mem_write(0x1000, &x86_code), Ok(()));
assert_eq!(
emu.mmio_map(0x2000, 0x1000, Some(read_callback), Some(write_callback)),
Ok(())
);
assert_eq!(
emu.emu_start(
0x1000,
0x1000 + x86_code.len() as u64,
10 * SECOND_SCALE,
1000
),
Ok(())
);
assert_eq!(read_expect, *read_cell.borrow());
assert_eq!(write_expect, *write_cell.borrow());
assert_eq!(emu.mem_unmap(0x2000, 0x1000), Ok(()));
}
{
// MOV eax, [0x2004];
let x86_code: Vec<u8> = vec![0x8B, 0x04, 0x25, 0x04, 0x20, 0x00, 0x00];
let read_cell = Rc::new(RefCell::new(MmioReadExpectation(0, 0)));
let cb_read_cell = read_cell.clone();
let read_callback = move |_: &mut Unicorn<'_, ()>, offset, size| {
*cb_read_cell.borrow_mut() = MmioReadExpectation(offset, size);
42
};
assert_eq!(emu.mem_write(0x1000, &x86_code), Ok(()));
assert_eq!(emu.mmio_map_ro(0x2000, 0x1000, read_callback), Ok(()));
assert_eq!(
emu.emu_start(
0x1000,
0x1000 + x86_code.len() as u64,
10 * SECOND_SCALE,
1000
),
Ok(())
);
assert_eq!(read_expect, *read_cell.borrow());
assert_eq!(emu.mem_unmap(0x2000, 0x1000), Ok(()));
}
{
// MOV ax, 42; MOV [0x2008], ax;
let x86_code: Vec<u8> = vec![
0x66, 0xB8, 0x2A, 0x00, 0x66, 0x89, 0x04, 0x25, 0x08, 0x20, 0x00, 0x00,
];
let write_cell = Rc::new(RefCell::new(MmioWriteExpectation(0, 0, 0)));
let cb_write_cell = write_cell.clone();
let write_callback = move |_: &mut Unicorn<'_, ()>, offset, size, value| {
*cb_write_cell.borrow_mut() = MmioWriteExpectation(offset, size, value);
};
assert_eq!(emu.mem_write(0x1000, &x86_code), Ok(()));
assert_eq!(emu.mmio_map_wo(0x2000, 0x1000, write_callback), Ok(()));
assert_eq!(
emu.emu_start(
0x1000,
0x1000 + x86_code.len() as u64,
10 * SECOND_SCALE,
1000
),
Ok(())
);
assert_eq!(write_expect, *write_cell.borrow());
assert_eq!(emu.mem_unmap(0x2000, 0x1000), Ok(()));
}
}
#[test]
fn emulate_arm() {
let arm_code32: Vec<u8> = vec![0x83, 0xb0]; // sub sp, #0xc
let mut emu = unicorn_engine::Unicorn::new(Arch::ARM, Mode::THUMB)
.expect("failed to initialize unicorn instance");
assert_eq!(emu.reg_write(RegisterARM::R1, 123), Ok(()));
assert_eq!(emu.reg_read(RegisterARM::R1), Ok(123));
// Attempt to write to memory before mapping it.
assert_eq!(
emu.mem_write(0x1000, &arm_code32),
(Err(uc_error::WRITE_UNMAPPED))
);
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
assert_eq!(emu.mem_write(0x1000, &arm_code32), Ok(()));
assert_eq!(
emu.mem_read_as_vec(0x1000, arm_code32.len()),
Ok(arm_code32.clone())
);
assert_eq!(emu.reg_write(RegisterARM::SP, 12), Ok(()));
assert_eq!(emu.reg_write(RegisterARM::R0, 10), Ok(()));
// ARM checks the least significant bit of the address to know
// if the code is in Thumb mode.
assert_eq!(
emu.emu_start(
0x1000 | 0x01,
(0x1000 | (0x01 + arm_code32.len())) as u64,
10 * SECOND_SCALE,
1000
),
Ok(())
);
assert_eq!(emu.reg_read(RegisterARM::SP), Ok(0));
assert_eq!(emu.reg_read(RegisterARM::R0), Ok(10));
}
#[test]
fn emulate_mips() {
let mips_code32 = vec![0x56, 0x34, 0x21, 0x34]; // ori $at, $at, 0x3456;
let mut emu = unicorn_engine::Unicorn::new(Arch::MIPS, Mode::MODE_32)
.expect("failed to initialize unicorn instance");
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
assert_eq!(emu.mem_write(0x1000, &mips_code32), Ok(()));
assert_eq!(
emu.mem_read_as_vec(0x1000, mips_code32.len()),
Ok(mips_code32.clone())
);
assert_eq!(emu.reg_write(RegisterMIPS::AT, 0), Ok(()));
assert_eq!(
emu.emu_start(
0x1000,
(0x1000 + mips_code32.len()) as u64,
10 * SECOND_SCALE,
1000
),
Ok(())
);
assert_eq!(emu.reg_read(RegisterMIPS::AT), Ok(0x3456));
}
#[test]
fn emulate_ppc() {
let ppc_code32 = vec![0x7F, 0x46, 0x1A, 0x14]; // add 26, 6, 3
let mut emu = unicorn_engine::Unicorn::new(Arch::PPC, Mode::PPC32 | Mode::BIG_ENDIAN)
.expect("failed to initialize unicorn instance");
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
assert_eq!(emu.mem_write(0x1000, &ppc_code32), Ok(()));
assert_eq!(
emu.mem_read_as_vec(0x1000, ppc_code32.len()),
Ok(ppc_code32.clone())
);
assert_eq!(emu.reg_write(RegisterPPC::R3, 42), Ok(()));
assert_eq!(emu.reg_write(RegisterPPC::R6, 1337), Ok(()));
assert_eq!(
emu.emu_start(
0x1000,
(0x1000 + ppc_code32.len()) as u64,
10 * SECOND_SCALE,
1000
),
Ok(())
);
assert_eq!(emu.reg_read(RegisterPPC::R26), Ok(1379));
}
#[test]
fn mem_unmapping() {
let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32)
.expect("failed to initialize unicorn instance");
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
assert_eq!(emu.mem_unmap(0x1000, 0x4000), Ok(()));
}
#[test]
fn mem_map_ptr() {
// Use an array for the emulator memory.
let mut mem: [u8; 4000] = [0; 4000];
let x86_code32: Vec<u8> = vec![0x41, 0x4a]; // INC ecx; DEC edx
let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32)
.expect("failed to initialize unicorn instance");
// Attempt to write to memory before mapping it.
assert_eq!(
emu.mem_write(0x1000, &x86_code32),
(Err(uc_error::WRITE_UNMAPPED))
);
assert_eq!(
unsafe { emu.mem_map_ptr(0x1000, 0x4000, Permission::ALL, mem.as_mut_ptr() as _) },
Ok(())
);
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
assert_eq!(
emu.mem_read_as_vec(0x1000, x86_code32.len()),
Ok(x86_code32.clone())
);
assert_eq!(emu.reg_write(RegisterX86::ECX, 10), Ok(()));
assert_eq!(emu.reg_write(RegisterX86::EDX, 50), Ok(()));
assert_eq!(
emu.emu_start(
0x1000,
(0x1000 + x86_code32.len()) as u64,
10 * SECOND_SCALE,
1000
),
Ok(())
);
assert_eq!(emu.reg_read(RegisterX86::ECX), Ok(11));
assert_eq!(emu.reg_read(RegisterX86::EDX), Ok(49));
assert_eq!(emu.mem_unmap(0x1000, 0x4000), Ok(()));
// Use a Vec for the emulator memory.
let mut mem: Vec<u8> = Vec::new();
mem.reserve(4000);
// Attempt to write to memory before mapping it.
assert_eq!(
emu.mem_write(0x1000, &x86_code32),
(Err(uc_error::WRITE_UNMAPPED))
);
assert_eq!(
unsafe { emu.mem_map_ptr(0x1000, 0x4000, Permission::ALL, mem.as_mut_ptr() as _) },
Ok(())
);
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
assert_eq!(
emu.mem_read_as_vec(0x1000, x86_code32.len()),
Ok(x86_code32.clone())
);
assert_eq!(emu.reg_write(RegisterX86::ECX, 10), Ok(()));
assert_eq!(emu.reg_write(RegisterX86::EDX, 50), Ok(()));
assert_eq!(
emu.emu_start(
0x1000,
(0x1000 + x86_code32.len()) as u64,
10 * SECOND_SCALE,
1000
),
Ok(())
);
assert_eq!(emu.reg_read(RegisterX86::ECX), Ok(11));
assert_eq!(emu.reg_read(RegisterX86::EDX), Ok(49));
assert_eq!(emu.mem_unmap(0x1000, 0x4000), Ok(()));
}
#[test]
fn x86_context_save_and_restore() {
for mode in [Mode::MODE_32, Mode::MODE_64] {
let x86_code = [
0x48, 0xB8, 0xEF, 0xBE, 0xAD, 0xDE, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x05,
];
let mut emu = unicorn_engine::Unicorn::new(Arch::X86, mode)
.expect("failed to initialize unicorn instance");
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
assert_eq!(emu.mem_write(0x1000, &x86_code), Ok(()));
let _ = emu.emu_start(
0x1000,
(0x1000 + x86_code.len()) as u64,
10 * SECOND_SCALE,
1000,
);
/* now, save the context... */
let context = emu.context_init();
let context = context.unwrap();
/* and create a new emulator, into which we will "restore" that context */
let emu2 = unicorn_engine::Unicorn::new(Arch::X86, mode)
.expect("failed to initialize unicorn instance");
assert_eq!(emu2.context_restore(&context), Ok(()));
for register in X86_REGISTERS.iter() {
println!("Testing register {:?}", register);
assert_eq!(emu2.reg_read(*register), emu.reg_read(*register));
}
}
}
#[test]
fn x86_block_callback() {
#[derive(PartialEq, Debug)]
struct BlockExpectation(u64, u32);
let expects = vec![BlockExpectation(0x1000, 2)];
let blocks: Vec<BlockExpectation> = Vec::new();
let blocks_cell = Rc::new(RefCell::new(blocks));
let callback_blocks = blocks_cell.clone();
let callback = move |_: &mut Unicorn<'_, ()>, address: u64, size: u32| {
let mut blocks = callback_blocks.borrow_mut();
blocks.push(BlockExpectation(address, size));
};
let x86_code32: Vec<u8> = vec![0x41, 0x4a]; // INC ecx; DEC edx
let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_32)
.expect("failed to initialize unicorn instance");
assert_eq!(emu.mem_map(0x1000, 0x4000, Permission::ALL), Ok(()));
assert_eq!(emu.mem_write(0x1000, &x86_code32), Ok(()));
let hook = emu
.add_block_hook(1, 0, callback)
.expect("failed to add block hook");
assert_eq!(
emu.emu_start(0x1000, 0x1002, 10 * SECOND_SCALE, 1000),
Ok(())
);
assert_eq!(expects, *blocks_cell.borrow());
assert_eq!(emu.remove_hook(hook), Ok(()));
}
#[test]
fn x86_tlb_callback() {
#[derive(PartialEq, Debug)]
struct BlockExpectation(u64, u32);
let expects:u64 = 4;
let count: u64 = 0;
let count_cell = Rc::new(RefCell::new(count));
let callback_counter = count_cell.clone();
let tlb_callback = move |_: &mut Unicorn<'_, ()>, address: u64, _: MemType| -> Option<TlbEntry> {
let mut blocks = callback_counter.borrow_mut();
*blocks += 1;
return Some(TlbEntry{paddr: address, perms: Permission::ALL});
};
let syscall_callback = move |uc: &mut Unicorn<'_, ()>| {
assert_eq!(uc.ctl_flush_tlb(), Ok(()));
};
let code: Vec<u8> = vec![0xa3,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00,0x0f,0x05,0xa3,0x00,0x00,0x20,0x00,0x00,0x00,0x00,0x00]; // movabs dword ptr [0x200000], eax; syscall; movabs dword ptr [0x200000], eax
let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_64)
.expect("failed to initialize unicorn instance");
assert_eq!(emu.ctl_tlb_type(TlbType::VIRTUAL), Ok(()));
assert_eq!(emu.mem_map(0x1000, 0x1000, Permission::ALL), Ok(()));
assert_eq!(emu.mem_map(0x200000, 0x1000, Permission::ALL), Ok(()));
assert_eq!(emu.mem_write(0x1000, &code), Ok(()));
let tlb_hook = emu
.add_tlb_hook(0, !0u64, tlb_callback)
.expect("failed to add tlb hook");
let syscall_hook = emu
.add_insn_sys_hook(InsnSysX86::SYSCALL, 0, !0u64, syscall_callback)
.expect("failed to add syscall hook");
assert_eq!(
emu.emu_start(0x1000, (0x1000 + code.len()) as u64, 0, 0),
Ok(())
);
assert_eq!(expects, *count_cell.borrow());
assert_eq!(emu.remove_hook(tlb_hook), Ok(()));
assert_eq!(emu.remove_hook(syscall_hook), Ok(()));
}
#[test]
fn x86_reg_rw_batch() {
// mov rax, 0x10, mov rbx, 0x20, mov rcx, 0x30, mov rdx, 0x40
let code: Vec<u8> = vec![0x48, 0xC7, 0xC0, 0x10, 0x00, 0x00, 0x00, 0x48, 0xC7, 0xC3, 0x20, 0x00, 0x00, 0x00, 0x48, 0xC7, 0xC1, 0x30, 0x00, 0x00, 0x00, 0x48, 0xC7, 0xC2, 0x40, 0x00, 0x00, 0x00];
let expect: Vec<u64> = vec![0x10, 0x20, 0x30, 0x40];
let mut emu = unicorn_engine::Unicorn::new(Arch::X86, Mode::MODE_64)
.expect("failed to initialize unicorn instance");
assert_eq!(emu.mem_map(0x1000, 0x1000, Permission::ALL), Ok(()));
assert_eq!(emu.mem_write(0x1000, &code), Ok(()));
assert_eq!(
emu.emu_start(0x1000, (0x1000 + code.len()) as u64, 0, 0),
Ok(())
);
let regids = vec![RegisterX86::RAX, RegisterX86::RBX, RegisterX86::RCX, RegisterX86::RDX];
assert_eq!(
emu.reg_read_batch(&regids, 4),
Ok(expect)
);
let regvals = vec![0x50, 0x60, 0x70, 0x80];
assert_eq!(
emu.reg_write_batch(&regids, &regvals, 4),
Ok(())
);
assert_eq!(
emu.reg_read_batch(&regids, 4),
Ok(vec![0x50, 0x60, 0x70, 0x80])
);
}

View File

@@ -590,9 +590,7 @@ static void test_arm_mem_access_abort(void)
r_r0 = 0x990000; r_r0 = 0x990000;
OK(uc_reg_write(uc, UC_ARM_REG_R0, &r_r0)); OK(uc_reg_write(uc, UC_ARM_REG_R0, &r_r0));
OK(uc_hook_add(uc, &hk, OK(uc_hook_add(uc, &hk, UC_HOOK_MEM_UNMAPPED,
UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED |
UC_HOOK_MEM_FETCH_UNMAPPED,
test_arm_mem_access_abort_hook_mem, (void *)&r_pc_in_hook, 1, test_arm_mem_access_abort_hook_mem, (void *)&r_pc_in_hook, 1,
0)); 0));
OK(uc_hook_add(uc, &hkk, UC_HOOK_INSN_INVALID, OK(uc_hook_add(uc, &hkk, UC_HOOK_INSN_INVALID,

View File

@@ -584,15 +584,11 @@ static void test_arm64_mem_prot_regress(void)
test_arm64_mem_prot_regress_hook_mem, NULL, 1, 0)); test_arm64_mem_prot_regress_hook_mem, NULL, 1, 0));
uc_hook hh_prot; uc_hook hh_prot;
OK(uc_hook_add(uc, &hh_prot, OK(uc_hook_add(uc, &hh_prot, UC_HOOK_MEM_PROT,
UC_HOOK_MEM_READ_PROT | UC_HOOK_MEM_WRITE_PROT |
UC_HOOK_MEM_FETCH_PROT,
test_arm64_mem_prot_regress_hook_prot, NULL, 1, 0)); test_arm64_mem_prot_regress_hook_prot, NULL, 1, 0));
uc_hook hh_unm; uc_hook hh_unm;
OK(uc_hook_add(uc, &hh_unm, OK(uc_hook_add(uc, &hh_unm, UC_HOOK_MEM_UNMAPPED,
UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED |
UC_HOOK_MEM_FETCH_UNMAPPED,
test_arm64_mem_prot_regress_hook_unm, NULL, 1, 0)); test_arm64_mem_prot_regress_hook_unm, NULL, 1, 0));
const uint64_t value = 0x801b; const uint64_t value = 0x801b;

View File

@@ -30,7 +30,11 @@ static void test_move_to_sr(void)
OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0)); OK(uc_emu_start(uc, code_start, code_start + sizeof(code) - 1, 0, 0));
OK(uc_reg_read(uc, UC_M68K_REG_SR, &r_sr));
TEST_CHECK(r_sr == 0x2700);
OK(uc_close(uc)); OK(uc_close(uc));
} }
TEST_LIST = {{"test_move_to_sr", test_move_to_sr}, {NULL, NULL}}; TEST_LIST = {{"test_move_to_sr", test_move_to_sr}, {NULL, NULL}};

View File

@@ -235,7 +235,7 @@ static uint64_t test_mem_protect_mmio_read_cb(struct uc_struct *uc,
{ {
TEST_CHECK(addr == 0x20); // note, it's not 0x1020 TEST_CHECK(addr == 0x20); // note, it's not 0x1020
*(uint64_t *)user_data = *(uint64_t *)user_data + 1; *(uint64_t *)user_data += 1;
return 0x114514; return 0x114514;
} }