diff --git a/.github/workflows/Crate-publishing.yml b/.github/workflows/Crate-publishing.yml index 1b19f9a103..229cf49c0c 100644 --- a/.github/workflows/Crate-publishing.yml +++ b/.github/workflows/Crate-publishing.yml @@ -25,59 +25,41 @@ jobs: fail-fast: false matrix: config: - - { - os: windows-2022, - arch: x64, - name: 'Windows 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' - } + - { os: windows-2022, arch: x64, name: "Windows 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: - uses: actions/checkout@v4 - - name: '🛠️ Set up Rust' - uses: dtolnay/rust-toolchain@stable + - name: Set up Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 - - name: '🛠️ Activate Developer Command Prompt' + - name: "🛠️ Activate Developer Command Prompt" if: contains(matrix.config.os, 'win') uses: ilammy/msvc-dev-cmd@v1 with: arch: ${{ matrix.config.arch }} - - name: '🛠️ Win build dependencies' + - name: "🛠️ Win build dependencies" if: contains(matrix.config.os, 'win') shell: bash run: | choco install ninja - - name: '🛠️ macOS build dependencies' + - name: "🛠️ macOS build dependencies" if: contains(matrix.config.os, 'macOS') shell: bash run: | brew install ninja - - name: '🚧 Cargo test' + - name: "🚧 Cargo test" if: "!startsWith(github.ref, 'refs/tags')" run: | 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') - env: - TOKEN: ${{ secrets.cratesio_token }} - UNICORN_VERSION: dev - run: | - cargo login $TOKEN && cargo test && cargo publish + uses: katyo/publish-crates@v2 + with: + registry-token: ${{ secrets.cratesio_token }} diff --git a/Cargo.toml b/Cargo.toml index 2136e417a6..81fa746ef0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,57 +1,31 @@ -[package] -name = "unicorn-engine" +[workspace] +default-members = ["bindings/rust"] +members = ["bindings/rust", "bindings/rust/sys"] +resolver = "2" + +[workspace.package] +rust-version = "1.85.0" version = "2.1.3" -authors = ["Ziqiao Kong", "Lukas Seidel"] +authors = ["Ziqiao Kong", "Lukas Seidel", "Amaan Qureshi "] +keywords = ["unicorn", "cpu", "emulator", "bindings"] +categories = ["api-bindings", "emulators", "no-std", "virtualization"] documentation = "https://github.com/unicorn-engine/unicorn/wiki" -edition = "2021" +edition = "2024" license = "GPL-2.0" readme = "README.md" repository = "https://github.com/unicorn-engine/unicorn" 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] -path = "bindings/rust/src/lib.rs" - -[dependencies] -bitflags = "2.3.3" -libc = "0.2" - -[build-dependencies] -cc = { version = "1.0" } -cmake = { version = "0.1" } -pkg-config = { version = "0.3" } -[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 = [] -# 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 = [] +[workspace.lints.clippy] +cast_lossless = "allow" +cast_possible_truncation = "allow" +cast_possible_wrap = "allow" +cast_sign_loss = "allow" +missing_errors_doc = "allow" +missing_panics_doc = "allow" +similar_names = "allow" +unreadable_literal = "allow" +use_self = "allow" +pedantic = { level = "warn", priority = -1 } +nursery = { level = "warn", priority = -1 } +cargo = { level = "warn", priority = -1 } diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml new file mode 100644 index 0000000000..139b41efde --- /dev/null +++ b/bindings/rust/Cargo.toml @@ -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"] diff --git a/bindings/rust/README.md b/bindings/rust/README.md index 9555ec3138..7fa357a8d5 100644 --- a/bindings/rust/README.md +++ b/bindings/rust/README.md @@ -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). ```rust -use unicorn_engine::{Unicorn, RegisterARM}; -use unicorn_engine::unicorn_const::{Arch, Mode, Permission, SECOND_SCALE}; +use unicorn_engine::{Arch, Mode, Prot, SECOND_SCALE, Unicorn, RegisterARM}; fn main() { let arm_code32: Vec = 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 emu = &mut unicorn; - emu.mem_map(0x1000, 0x4000, Permission::ALL).expect("failed to map code page"); + let mut emu = Unicorn::new(Arch::ARM, Mode::LITTLE_ENDIAN).expect("failed to initialize Unicorn instance"); + emu.mem_map(0x1000, 0x4000, Prot::ALL).expect("failed to map code page"); 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::R5, 1337).expect("failed write R5"); - let _ = emu.emu_start(0x1000, (0x1000 + arm_code32.len()) as u64, 10 * SECOND_SCALE, 1000); - assert_eq!(emu.reg_read(RegisterARM::R0), Ok(100)); - assert_eq!(emu.reg_read(RegisterARM::R5), Ok(1337)); + 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).unwrap(), 100); + 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 @@ -37,7 +36,6 @@ unicorn-engine = "2.1.1" ## 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. Thanks to all contributors. - diff --git a/bindings/rust/build.rs b/bindings/rust/build.rs deleted file mode 100644 index 821367d39b..0000000000 --- a/bindings/rust/build.rs +++ /dev/null @@ -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::() - .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::>(); - 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 "/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(); - } - }; -} diff --git a/bindings/rust/src/arm.rs b/bindings/rust/src/arm.rs deleted file mode 100644 index df43245d7d..0000000000 --- a/bindings/rust/src/arm.rs +++ /dev/null @@ -1,223 +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, - ENDING = 140, -} - -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 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 for i32 { - fn from(value: ArmCpuModel) -> Self { - value as i32 - } -} - -impl From<&ArmCpuModel> for i32 { - fn from(value: &ArmCpuModel) -> Self { - *value as i32 - } -} diff --git a/bindings/rust/src/arm64.rs b/bindings/rust/src/arm64.rs deleted file mode 100644 index 10f9f25b9c..0000000000 --- a/bindings/rust/src/arm64.rs +++ /dev/null @@ -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 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 for i32 { - fn from(value: Arm64CpuModel) -> Self { - value as i32 - } -} - -impl From<&Arm64CpuModel> for i32 { - fn from(value: &Arm64CpuModel) -> Self { - (*value) as i32 - } -} diff --git a/bindings/rust/src/ffi.rs b/bindings/rust/src/ffi.rs deleted file mode 100644 index 7f7a205bf9..0000000000 --- a/bindings/rust/src/ffi.rs +++ /dev/null @@ -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>>, -} - -pub trait IsUcHook<'a> {} - -impl<'a, D, F> IsUcHook<'a> for UcHook<'a, D, F> {} - -pub unsafe extern "C" fn mmio_read_callback_proxy( - uc: uc_handle, - offset: u64, - size: usize, - user_data: *mut UcHook, -) -> u64 -where - F: FnMut(&mut crate::Unicorn, 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( - uc: uc_handle, - offset: u64, - size: usize, - value: u64, - user_data: *mut UcHook, -) where - F: FnMut(&mut crate::Unicorn, 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( - uc: uc_handle, - address: u64, - size: u32, - user_data: *mut UcHook, -) where - F: FnMut(&mut crate::Unicorn, 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( - uc: uc_handle, - address: u64, - size: u32, - user_data: *mut UcHook, -) where - F: FnMut(&mut crate::Unicorn, 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( - uc: uc_handle, - mem_type: MemType, - address: u64, - size: u32, - value: i64, - user_data: *mut UcHook, -) -> bool -where - F: FnMut(&mut crate::Unicorn, 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( - uc: uc_handle, - value: u32, - user_data: *mut UcHook, -) where - F: FnMut(&mut crate::Unicorn, 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( - uc: uc_handle, - port: u32, - size: usize, - user_data: *mut UcHook, -) -> u32 -where - F: FnMut(&mut crate::Unicorn, 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( - uc: uc_handle, - user_data: *mut UcHook, -) -> bool -where - F: FnMut(&mut crate::Unicorn) -> 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( - uc: uc_handle, - port: u32, - size: usize, - value: u32, - user_data: *mut UcHook, -) where - F: FnMut(&mut crate::Unicorn, 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(uc: uc_handle, user_data: *mut UcHook) -where - F: FnMut(&mut crate::Unicorn), -{ - 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( - uc: uc_handle, - vaddr: u64, - mem_type: MemType, - result: *mut TlbEntry, - user_data: *mut UcHook, -) -> bool -where - F: FnMut(&mut crate::Unicorn, u64, MemType) -> Option, -{ - 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() -} diff --git a/bindings/rust/src/hook.rs b/bindings/rust/src/hook.rs new file mode 100644 index 0000000000..2dd22464a0 --- /dev/null +++ b/bindings/rust/src/hook.rs @@ -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>>, +} + +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( + uc: *mut uc_engine, + offset: u64, + size: u32, + // user_data: *mut UcHook, + user_data: *mut c_void, +) -> u64 +where + F: FnMut(&mut crate::Unicorn, u64, usize) -> u64, +{ + let user_data = unsafe { &mut *user_data.cast::>() }; + 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( + uc: *mut uc_engine, + offset: u64, + size: u32, + value: u64, + user_data: *mut c_void, +) where + F: FnMut(&mut crate::Unicorn, u64, usize, u64), +{ + let user_data = unsafe { &mut *user_data.cast::>() }; + 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( + uc: *mut uc_engine, + address: u64, + size: u32, + user_data: *mut UcHook, +) where + F: FnMut(&mut crate::Unicorn, 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( + uc: *mut uc_engine, + address: u64, + size: u32, + user_data: *mut UcHook, +) where + F: FnMut(&mut crate::Unicorn, 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( + uc: *mut uc_engine, + mem_type: sys::MemType, + address: u64, + size: u32, + value: i64, + user_data: *mut UcHook, +) -> bool +where + F: FnMut(&mut crate::Unicorn, 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( + uc: *mut uc_engine, + value: u32, + user_data: *mut UcHook, +) where + F: FnMut(&mut crate::Unicorn, 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( + uc: *mut uc_engine, + port: u32, + size: usize, + user_data: *mut UcHook, +) -> u32 +where + F: FnMut(&mut crate::Unicorn, 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( + uc: *mut uc_engine, + user_data: *mut UcHook, +) -> bool +where + F: FnMut(&mut crate::Unicorn) -> 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( + uc: *mut uc_engine, + port: u32, + size: usize, + value: u32, + user_data: *mut UcHook, +) where + F: FnMut(&mut crate::Unicorn, 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(uc: *mut uc_engine, user_data: *mut UcHook) +where + F: FnMut(&mut crate::Unicorn), +{ + 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( + uc: *mut uc_engine, + reg: sys::RegisterARM64, + cp_reg: *const sys::RegisterARM64_CP, + user_data: *mut UcHook, +) -> bool +where + F: FnMut(&mut crate::Unicorn, 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( + uc: *mut uc_engine, + vaddr: u64, + mem_type: sys::MemType, + result: *mut sys::TlbEntry, + user_data: *mut UcHook, +) -> bool +where + F: FnMut(&mut crate::Unicorn, u64, sys::MemType) -> Option, +{ + 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( + uc: *mut uc_engine, + addr: u64, + arg1: u64, + arg2: u64, + size: u32, + user_data: *mut UcHook, +) where + F: FnMut(&mut Unicorn, 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); +} diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index 7cc95a3946..0f5217ffaa 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -5,125 +5,82 @@ //! # Example use //! //! ```rust -//! -//! use unicorn_engine::RegisterARM; -//! use unicorn_engine::unicorn_const::{Arch, Mode, Permission, SECOND_SCALE}; +//! use unicorn_engine::{ +//! RegisterARM, +//! unicorn_const::{Arch, Mode, Prot, SECOND_SCALE}, +//! }; //! //! fn emulate() { //! let arm_code32 = [0x17, 0x00, 0x40, 0xe2]; // sub r0, #23 //! -//! let mut emu = unicorn_engine::Unicorn::new(Arch::ARM, Mode::LITTLE_ENDIAN).expect("failed to initialize Unicorn instance"); -//! emu.mem_map(0x1000, 0x4000, Permission::ALL).expect("failed to map code page"); -//! emu.mem_write(0x1000, &arm_code32).expect("failed to write instructions"); +//! let mut emu = unicorn_engine::Unicorn::new(Arch::ARM, Mode::LITTLE_ENDIAN) +//! .expect("failed to initialize Unicorn instance"); +//! emu.mem_map(0x1000, 0x4000, Prot::ALL) +//! .expect("failed to map code page"); +//! 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::R5, 1337).expect("failed write R5"); +//! emu.reg_write(RegisterARM::R0, 123) +//! .expect("failed to write to R0"); +//! emu.reg_write(RegisterARM::R5, 1337) +//! .expect("failed to write to R5"); //! -//! emu.emu_start(0x1000, (0x1000 + arm_code32.len()) as u64, 10 * SECOND_SCALE, 1000).unwrap(); +//! emu.emu_start( +//! 0x1000, +//! (0x1000 + arm_code32.len()) as u64, +//! 10 * SECOND_SCALE, +//! 1000, +//! ) +//! .unwrap(); //! assert_eq!(emu.reg_read(RegisterARM::R0), Ok(100)); //! assert_eq!(emu.reg_read(RegisterARM::R5), Ok(1337)); //! } //! ``` -//! #![no_std] #[macro_use] extern crate alloc; -extern crate std; - -use alloc::boxed::Box; -use alloc::rc::Rc; -use alloc::vec::Vec; -use core::cell::UnsafeCell; -use core::ptr; -use libc::c_void; - -use ffi::uc_handle; +use alloc::{boxed::Box, rc::Rc, vec::Vec}; +use core::{cell::UnsafeCell, ffi::c_void, ptr}; #[macro_use] pub mod unicorn_const; pub use unicorn_const::*; -pub mod ffi; // lets consumers call ffi if desired - -// include arm support if conditionally compiled in -#[cfg(feature = "arch_arm")] -mod arm; -#[cfg(feature = "arch_arm")] -pub use crate::arm::*; - -// include arm64 support if conditionally compiled in -#[cfg(feature = "arch_aarch64")] -mod arm64; -#[cfg(feature = "arch_aarch64")] -pub use crate::arm64::*; - -// include m68k support if conditionally compiled in -#[cfg(feature = "arch_m68k")] -mod m68k; -#[cfg(feature = "arch_m68k")] -pub use crate::m68k::*; - -// include mips support if conditionally compiled in -#[cfg(feature = "arch_mips")] -mod mips; -#[cfg(feature = "arch_mips")] -pub use crate::mips::*; - -// include ppc support if conditionally compiled in -#[cfg(feature = "arch_ppc")] -mod ppc; -#[cfg(feature = "arch_ppc")] -pub use crate::ppc::*; - -// include riscv support if conditionally compiled in -#[cfg(feature = "arch_riscv")] -mod riscv; -#[cfg(feature = "arch_riscv")] -pub use crate::riscv::*; - -// include s390x support if conditionally compiled in -#[cfg(feature = "arch_s390x")] -mod s390x; -#[cfg(feature = "arch_s390x")] -pub use crate::s390x::*; - -// include sparc support if conditionally compiled in -#[cfg(feature = "arch_sparc")] -mod sparc; -#[cfg(feature = "arch_sparc")] -pub use crate::sparc::*; - -// include tricore support if conditionally compiled in -#[cfg(feature = "arch_tricore")] -mod tricore; -#[cfg(feature = "arch_tricore")] -pub use crate::tricore::*; - -// include x86 support if conditionally compiled in -#[cfg(feature = "arch_x86")] -mod x86; -#[cfg(feature = "arch_x86")] -pub use crate::x86::*; +pub mod hook; // lets consumers call hooks + +#[cfg(test)] +mod tests; #[derive(Debug)] pub struct Context { - context: ffi::uc_context, + context: *mut uc_context, } impl Context { #[must_use] - pub fn is_initialized(&self) -> bool { + pub const fn is_initialized(&self) -> bool { !self.context.is_null() } + + pub fn reg_read>(&self, regid: T) -> Result { + let mut value = 0; + unsafe { uc_context_reg_read(self.context, regid.into(), (&raw mut value).cast()) } + .and(Ok(value)) + } + + pub fn reg_write>(&mut self, regid: T, value: u64) -> Result<(), uc_error> { + unsafe { uc_context_reg_write(self.context, regid.into(), (&raw const value).cast()) } + .into() + } } impl Drop for Context { fn drop(&mut self) { if self.is_initialized() { unsafe { - ffi::uc_context_free(self.context); + uc_context_free(self.context); } } self.context = ptr::null_mut(); @@ -132,22 +89,22 @@ impl Drop for Context { pub struct MmioCallbackScope<'a> { pub regions: Vec<(u64, usize)>, - pub read_callback: Option + 'a>>, - pub write_callback: Option + 'a>>, + pub read_callback: Option + 'a>>, + pub write_callback: Option + 'a>>, } -impl<'a> MmioCallbackScope<'a> { +impl MmioCallbackScope<'_> { fn has_regions(&self) -> bool { !self.regions.is_empty() } fn unmap(&mut self, begin: u64, size: usize) { - let end: u64 = begin + size as u64; + let end = begin + size as u64; self.regions = self .regions .iter() .flat_map(|(b, s)| { - let e: u64 = b + *s as u64; + let e = b + *s as u64; if begin > *b { if begin >= e { // The unmapped region is completely after this region @@ -181,24 +138,23 @@ impl<'a> MmioCallbackScope<'a> { } #[derive(Clone, Copy, PartialEq, Eq, Debug)] -pub struct UcHookId(ffi::uc_hook); +pub struct UcHookId(uc_hook); pub struct UnicornInner<'a, D> { - pub handle: uc_handle, + pub handle: *mut uc_engine, pub ffi: bool, pub arch: Arch, /// to keep ownership over the hook for this uc instance's lifetime - pub hooks: Vec<(UcHookId, Box + 'a>)>, + pub hooks: Vec<(UcHookId, Box + 'a>)>, /// To keep ownership over the mmio callbacks for this uc instance's lifetime pub mmio_callbacks: Vec>, pub data: D, } -/// Drop UC -impl<'a, D> Drop for UnicornInner<'a, D> { +impl Drop for UnicornInner<'_, D> { fn drop(&mut self) { if !self.ffi && !self.handle.is_null() { - unsafe { ffi::uc_close(self.handle) }; + unsafe { uc_close(self.handle) }; } self.handle = ptr::null_mut(); } @@ -217,17 +173,17 @@ impl<'a> Unicorn<'a, ()> { } /// # Safety - /// The function has to be called with a valid uc_handle pointer - /// that was previously allocated by a call to uc_open. + /// The function has to be called with a valid [`uc_engine`] pointer + /// that was previously allocated by a call to [`uc_open`]. /// Calling the function with a non null pointer value that /// does not point to a unicorn instance will cause undefined /// behavior. - pub unsafe fn from_handle(handle: uc_handle) -> Result, uc_error> { + pub unsafe fn from_handle(handle: *mut uc_engine) -> Result, uc_error> { if handle.is_null() { return Err(uc_error::HANDLE); } - let mut arch: libc::size_t = Default::default(); - let err = unsafe { ffi::uc_query(handle, Query::ARCH, &mut arch) }; + let mut arch = 0; + let err = unsafe { uc_query(handle, Query::ARCH, &mut arch) }; if err != uc_error::OK { return Err(err); } @@ -252,7 +208,7 @@ where /// and hardware mode. pub fn new_with_data(arch: Arch, mode: Mode, data: D) -> Result, uc_error> { let mut handle = ptr::null_mut(); - unsafe { ffi::uc_open(arch, mode, &mut handle) }.and_then(|| { + unsafe { uc_open(arch, mode, &mut handle) }.and_then(|| { Ok(Unicorn { inner: Rc::new(UnsafeCell::from(UnicornInner { handle, @@ -267,7 +223,7 @@ where } } -impl<'a, D> core::fmt::Debug for Unicorn<'a, D> { +impl core::fmt::Debug for Unicorn<'_, D> { fn fmt(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { write!(formatter, "Unicorn {{ uc: {:p} }}", self.get_handle()) } @@ -305,21 +261,21 @@ impl<'a, D> Unicorn<'a, D> { /// Return the handle of the current emulator. #[must_use] - pub fn get_handle(&self) -> uc_handle { + pub fn get_handle(&self) -> *mut uc_engine { self.inner().handle } /// Returns a vector with the memory regions that are mapped in the emulator. pub fn mem_regions(&self) -> Result, uc_error> { - let mut nb_regions: u32 = 0; - let p_regions: *const MemRegion = ptr::null_mut(); - unsafe { ffi::uc_mem_regions(self.get_handle(), &p_regions, &mut nb_regions) }.and_then( + let mut nb_regions = 0; + let mut p_regions = ptr::null_mut(); + unsafe { uc_mem_regions(self.get_handle(), &raw mut p_regions, &mut nb_regions) }.and_then( || { let mut regions = Vec::new(); for i in 0..nb_regions { regions.push(unsafe { core::mem::transmute_copy(&*p_regions.add(i as usize)) }); } - unsafe { libc::free(p_regions as _) }; + unsafe { uc_free(p_regions.cast()) }; Ok(regions) }, ) @@ -327,18 +283,35 @@ impl<'a, D> Unicorn<'a, D> { /// Read a range of bytes from memory at the specified emulated physical address. pub fn mem_read(&self, address: u64, buf: &mut [u8]) -> Result<(), uc_error> { - unsafe { ffi::uc_mem_read(self.get_handle(), address, buf.as_mut_ptr(), buf.len()) }.into() + unsafe { + uc_mem_read( + self.get_handle(), + address, + buf.as_mut_ptr().cast(), + buf.len(), + ) + } + .into() } /// Return a range of bytes from memory at the specified emulated physical address as vector. pub fn mem_read_as_vec(&self, address: u64, size: usize) -> Result, uc_error> { let mut buf = vec![0; size]; - unsafe { ffi::uc_mem_read(self.get_handle(), address, buf.as_mut_ptr(), size) }.and(Ok(buf)) + unsafe { uc_mem_read(self.get_handle(), address, buf.as_mut_ptr().cast(), size) } + .and(Ok(buf)) } /// Write the data in `bytes` to the emulated physical address `address` pub fn mem_write(&mut self, address: u64, bytes: &[u8]) -> Result<(), uc_error> { - unsafe { ffi::uc_mem_write(self.get_handle(), address, bytes.as_ptr(), bytes.len()) }.into() + unsafe { + uc_mem_write( + self.get_handle(), + address, + bytes.as_ptr().cast(), + bytes.len(), + ) + } + .into() } /// Map an existing memory region in the emulator at the specified address. @@ -358,79 +331,77 @@ impl<'a, D> Unicorn<'a, D> { &mut self, address: u64, size: usize, - perms: Permission, + perms: Prot, ptr: *mut c_void, ) -> Result<(), uc_error> { - ffi::uc_mem_map_ptr(self.get_handle(), address, size, perms.bits(), ptr).into() + unsafe { uc_mem_map_ptr(self.get_handle(), address, size, perms.0 as _, ptr).into() } } /// Map a memory region in the emulator at the specified address. /// /// `address` must be aligned to 4kb or this will return `Error::ARG`. /// `size` must be a multiple of 4kb or this will return `Error::ARG`. - pub fn mem_map( - &mut self, - address: u64, - size: libc::size_t, - perms: Permission, - ) -> Result<(), uc_error> { - unsafe { ffi::uc_mem_map(self.get_handle(), address, size, perms.bits()) }.into() + pub fn mem_map(&mut self, address: u64, size: usize, perms: Prot) -> Result<(), uc_error> { + unsafe { uc_mem_map(self.get_handle(), address, size, perms.0 as _) }.into() } /// Map in am MMIO region backed by callbacks. /// /// `address` must be aligned to 4kb or this will return `Error::ARG`. /// `size` must be a multiple of 4kb or this will return `Error::ARG`. - pub fn mmio_map( + pub fn mmio_map( &mut self, address: u64, - size: libc::size_t, + size: usize, read_callback: Option, write_callback: Option, ) -> Result<(), uc_error> where - R: FnMut(&mut Unicorn, u64, usize) -> u64, - W: FnMut(&mut Unicorn, u64, usize, u64), + R: FnMut(&mut Unicorn<'_, D>, u64, usize) -> u64 + 'a, + W: FnMut(&mut Unicorn<'_, D>, u64, usize, u64) + 'a, { let mut read_data = read_callback.map(|c| { - Box::new(ffi::UcHook { + Box::new(hook::UcHook { callback: c, uc: Rc::downgrade(&self.inner), }) }); let mut write_data = write_callback.map(|c| { - Box::new(ffi::UcHook { + Box::new(hook::UcHook { callback: c, uc: Rc::downgrade(&self.inner), }) }); + let (read_cb, user_data_read) = read_data.as_mut().map_or((None, ptr::null_mut()), |d| { + ( + Some(hook::mmio_read_callback_proxy:: as _), + core::ptr::from_mut(d.as_mut()).cast(), + ) + }); + + let (write_cb, user_data_write) = + write_data.as_mut().map_or((None, ptr::null_mut()), |d| { + ( + Some(hook::mmio_write_callback_proxy:: as _), + core::ptr::from_mut(d.as_mut()).cast(), + ) + }); + unsafe { - ffi::uc_mmio_map( + uc_mmio_map( self.get_handle(), address, size, - match read_data { - Some(_) => ffi::mmio_read_callback_proxy:: as _, - None => ptr::null_mut(), - }, - match read_data { - Some(ref mut d) => d.as_mut() as *mut _ as _, - None => ptr::null_mut(), - }, - match write_data { - Some(_) => ffi::mmio_write_callback_proxy:: as _, - None => ptr::null_mut(), - }, - match write_data { - Some(ref mut d) => d.as_mut() as *mut _ as _, - None => ptr::null_mut(), - }, + read_cb, + user_data_read, + write_cb, + user_data_write, ) } .and_then(|| { - let rd = read_data.map(|c| c as Box); - let wd = write_data.map(|c| c as Box); + let rd = read_data.map(|c| c as Box); + let wd = write_data.map(|c| c as Box); self.inner_mut().mmio_callbacks.push(MmioCallbackScope { regions: vec![(address, size)], read_callback: rd, @@ -445,14 +416,9 @@ impl<'a, D> Unicorn<'a, D> { /// /// `address` must be aligned to 4kb or this will return `Error::ARG`. /// `size` must be a multiple of 4kb or this will return `Error::ARG`. - pub fn mmio_map_ro( - &mut self, - address: u64, - size: libc::size_t, - callback: F, - ) -> Result<(), uc_error> + pub fn mmio_map_ro(&mut self, address: u64, size: usize, callback: F) -> Result<(), uc_error> where - F: FnMut(&mut Unicorn, u64, usize) -> u64, + F: FnMut(&mut Unicorn, u64, usize) -> u64 + 'a, { self.mmio_map( address, @@ -466,14 +432,9 @@ impl<'a, D> Unicorn<'a, D> { /// /// `address` must be aligned to 4kb or this will return `Error::ARG`. /// `size` must be a multiple of 4kb or this will return `Error::ARG`. - pub fn mmio_map_wo( - &mut self, - address: u64, - size: libc::size_t, - callback: F, - ) -> Result<(), uc_error> + pub fn mmio_map_wo(&mut self, address: u64, size: usize, callback: F) -> Result<(), uc_error> where - F: FnMut(&mut Unicorn, u64, usize, u64), + F: FnMut(&mut Unicorn, u64, usize, u64) + 'a, { self.mmio_map( address, @@ -487,56 +448,57 @@ impl<'a, D> Unicorn<'a, D> { /// /// `address` must be aligned to 4kb or this will return `Error::ARG`. /// `size` must be a multiple of 4kb or this will return `Error::ARG`. - pub fn mem_unmap(&mut self, address: u64, size: libc::size_t) -> Result<(), uc_error> { - let err = unsafe { ffi::uc_mem_unmap(self.get_handle(), address, size) }; + pub fn mem_unmap(&mut self, address: u64, size: usize) -> Result<(), uc_error> { + let err = unsafe { uc_mem_unmap(self.get_handle(), address, size) }; self.mmio_unmap(address, size); err.into() } - fn mmio_unmap(&mut self, address: u64, size: libc::size_t) { - for scope in self.inner_mut().mmio_callbacks.iter_mut() { + fn mmio_unmap(&mut self, address: u64, size: usize) { + for scope in &mut self.inner_mut().mmio_callbacks { scope.unmap(address, size); } self.inner_mut() .mmio_callbacks - .retain(|scope| scope.has_regions()); + .retain(MmioCallbackScope::has_regions); } /// Set the memory permissions for an existing memory region. /// /// `address` must be aligned to 4kb or this will return `Error::ARG`. /// `size` must be a multiple of 4kb or this will return `Error::ARG`. - pub fn mem_protect( - &mut self, - address: u64, - size: libc::size_t, - perms: Permission, - ) -> Result<(), uc_error> { - unsafe { ffi::uc_mem_protect(self.get_handle(), address, size, perms.bits()) }.into() + pub fn mem_protect(&mut self, address: u64, size: usize, perms: Prot) -> Result<(), uc_error> { + unsafe { uc_mem_protect(self.get_handle(), address, size, perms.0 as _) }.into() } /// Write an unsigned value from a register. pub fn reg_write>(&mut self, regid: T, value: u64) -> Result<(), uc_error> { - unsafe { ffi::uc_reg_write(self.get_handle(), regid.into(), &value as *const _ as _) } - .into() + unsafe { uc_reg_write(self.get_handle(), regid.into(), (&raw const value).cast()) }.into() } /// Write values into batch of registers - pub fn reg_write_batch>( + pub fn reg_write_batch( &self, regids: &[T], values: &[u64], count: i32, - ) -> Result<(), uc_error> { - let mut values_ptrs: Vec<*const u64> = vec![0 as *const u64; count as usize]; + ) -> Result<(), uc_error> + where + T: Copy + Into, + { + let mut values_ptrs = vec![core::ptr::null::(); count as usize]; + let mut regids = regids + .iter() + .map(|regid| (*regid).into()) + .collect::>(); for i in 0..values.len() { - values_ptrs[i as usize] = &values[i] as *const u64; + values_ptrs[i] = &raw const values[i]; } unsafe { - ffi::uc_reg_write_batch( + uc_reg_write_batch( self.get_handle(), - regids.as_ptr() as *const i32, - values_ptrs.as_ptr() as *const *const c_void, + regids.as_mut_ptr(), + values_ptrs.as_ptr().cast::<*mut c_void>(), count, ) } @@ -548,36 +510,39 @@ impl<'a, D> Unicorn<'a, D> { /// The user has to make sure that the buffer length matches the register size. /// This adds support for registers >64 bit (GDTR/IDTR, XMM, YMM, ZMM (x86); Q, V (arm64)). pub fn reg_write_long>(&self, regid: T, value: &[u8]) -> Result<(), uc_error> { - unsafe { ffi::uc_reg_write(self.get_handle(), regid.into(), value.as_ptr() as _) }.into() + unsafe { uc_reg_write(self.get_handle(), regid.into(), value.as_ptr().cast()) }.into() } /// Read an unsigned value from a register. /// /// Not to be used with registers larger than 64 bit. pub fn reg_read>(&self, regid: T) -> Result { - let mut value: u64 = 0; - unsafe { ffi::uc_reg_read(self.get_handle(), regid.into(), &mut value as *mut u64 as _) } + let mut value = 0; + unsafe { uc_reg_read(self.get_handle(), regid.into(), (&raw mut value).cast()) } .and(Ok(value)) } /// Read batch of registers /// /// Not to be used with registers larger than 64 bit - pub fn reg_read_batch>( - &self, - regids: &[T], - count: i32, - ) -> Result, uc_error> { + pub fn reg_read_batch(&self, regids: &mut [T], count: i32) -> Result, uc_error> + where + T: Copy + Into, + { unsafe { let mut addrs_vec = vec![0u64; count as usize]; let addrs = addrs_vec.as_mut_slice(); + let mut regids = regids + .iter() + .map(|regid| (*regid).into()) + .collect::>(); for i in 0..count { - addrs[i as usize] = &mut addrs[i as usize] as *mut u64 as u64; + addrs[i as usize] = &raw mut addrs[i as usize] as u64; } - let res = ffi::uc_reg_read_batch( + let res = uc_reg_read_batch( self.get_handle(), - regids.as_ptr() as *const i32, - addrs.as_ptr() as *const *mut c_void, + regids.as_mut_ptr(), + addrs.as_mut_ptr().cast::<*mut c_void>(), count, ); match res { @@ -589,7 +554,8 @@ impl<'a, D> Unicorn<'a, D> { /// Read 128, 256 or 512 bit register value into heap allocated byte array. /// - /// This adds safe support for registers >64 bit (GDTR/IDTR, XMM, YMM, ZMM, ST (x86); Q, V (arm64)). + /// This adds safe support for registers >64 bit (GDTR/IDTR, XMM, YMM, ZMM, ST (x86); Q, V + /// (arm64)). pub fn reg_read_long>(&self, regid: T) -> Result, uc_error> { let curr_reg_id = regid.into(); let curr_arch = self.get_arch(); @@ -602,10 +568,51 @@ impl<'a, D> Unicorn<'a, D> { _ => Err(uc_error::ARCH)?, }; let mut value = vec![0; value_size]; - unsafe { ffi::uc_reg_read(self.get_handle(), curr_reg_id, value.as_mut_ptr() as _) } + unsafe { uc_reg_read(self.get_handle(), curr_reg_id, value.as_mut_ptr().cast()) } .and_then(|| Ok(value.into_boxed_slice())) } + /// Read ARM Coprocessor register + pub fn reg_read_arm_coproc(&self, reg: &mut RegisterARM_CP) -> Result<(), uc_error> { + let curr_arch = self.get_arch(); + match curr_arch { + #[cfg(feature = "arch_arm")] + Arch::ARM => {} + _ => return Err(uc_error::ARCH), + } + + unsafe { + uc_reg_read( + self.get_handle(), + RegisterARM::CP_REG.into(), + core::ptr::from_mut::(reg).cast(), + ) + } + .into() + } + + /// Read ARM64 Coprocessor register + pub fn reg_read_arm64_coproc(&self) -> Result { + let curr_arch = self.get_arch(); + match curr_arch { + #[cfg(feature = "arch_aarch64")] + Arch::ARM64 => {} + _ => return Err(uc_error::ARCH), + } + + let regid = RegisterARM64::CP_REG; + let mut reg = RegisterARM64_CP { + crn: 0, + crm: 0, + op0: 0, + op1: 0, + op2: 0, + val: 0, + }; + + unsafe { uc_reg_read(self.get_handle(), regid as i32, (&raw mut reg).cast()) }.and(Ok(reg)) + } + #[cfg(feature = "arch_arm")] fn value_size_arm64(curr_reg_id: i32) -> Result { match curr_reg_id { @@ -636,13 +643,13 @@ impl<'a, D> Unicorn<'a, D> { /// Read a signed 32-bit value from a register. pub fn reg_read_i32>(&self, regid: T) -> Result { - let mut value: i32 = 0; - unsafe { ffi::uc_reg_read(self.get_handle(), regid.into(), &mut value as *mut i32 as _) } + let mut value = 0; + unsafe { uc_reg_read(self.get_handle(), regid.into(), (&raw mut value).cast()) } .and(Ok(value)) } /// Add a code hook. - pub fn add_code_hook( + pub fn add_code_hook( &mut self, begin: u64, end: u64, @@ -651,19 +658,19 @@ impl<'a, D> Unicorn<'a, D> { where F: FnMut(&mut Unicorn, u64, u32) + 'a, { - let mut hook_id = ptr::null_mut(); - let mut user_data = Box::new(ffi::UcHook { + let mut hook_id = 0; + let mut user_data = Box::new(hook::UcHook { callback, uc: Rc::downgrade(&self.inner), }); unsafe { - ffi::uc_hook_add( + uc_hook_add( self.get_handle(), - &mut hook_id, - HookType::CODE, - ffi::code_hook_proxy:: as _, - user_data.as_mut() as *mut _ as _, + (&raw mut hook_id).cast(), + HookType::CODE.0 as i32, + hook::code_hook_proxy:: as _, + core::ptr::from_mut(user_data.as_mut()).cast(), begin, end, ) @@ -676,28 +683,28 @@ impl<'a, D> Unicorn<'a, D> { } /// Add a block hook. - pub fn add_block_hook( + pub fn add_block_hook( &mut self, begin: u64, end: u64, callback: F, ) -> Result where - F: FnMut(&mut Unicorn, u64, u32), + F: FnMut(&mut Unicorn, u64, u32) + 'a, { - let mut hook_id = ptr::null_mut(); - let mut user_data = Box::new(ffi::UcHook { + let mut hook_id = 0; + let mut user_data = Box::new(hook::UcHook { callback, uc: Rc::downgrade(&self.inner), }); unsafe { - ffi::uc_hook_add( + uc_hook_add( self.get_handle(), - &mut hook_id, - HookType::BLOCK, - ffi::block_hook_proxy:: as _, - user_data.as_mut() as *mut _ as _, + (&raw mut hook_id).cast(), + HookType::BLOCK.0 as i32, + hook::block_hook_proxy:: as _, + core::ptr::from_mut(user_data.as_mut()).cast(), begin, end, ) @@ -710,7 +717,7 @@ impl<'a, D> Unicorn<'a, D> { } /// Add a memory hook. - pub fn add_mem_hook( + pub fn add_mem_hook( &mut self, hook_type: HookType, begin: u64, @@ -718,25 +725,25 @@ impl<'a, D> Unicorn<'a, D> { callback: F, ) -> Result where - F: FnMut(&mut Unicorn, MemType, u64, usize, i64) -> bool, + F: FnMut(&mut Unicorn, MemType, u64, usize, i64) -> bool + 'a, { - if !(HookType::MEM_ALL | HookType::MEM_READ_AFTER).contains(hook_type) { + if hook_type & (HookType::MEM_ALL | HookType::MEM_READ_AFTER) != hook_type { return Err(uc_error::ARG); } - let mut hook_id = ptr::null_mut(); - let mut user_data = Box::new(ffi::UcHook { + let mut hook_id = 0; + let mut user_data = Box::new(hook::UcHook { callback, uc: Rc::downgrade(&self.inner), }); unsafe { - ffi::uc_hook_add( + uc_hook_add( self.get_handle(), - &mut hook_id, - hook_type, - ffi::mem_hook_proxy:: as _, - user_data.as_mut() as *mut _ as _, + (&raw mut hook_id).cast(), + hook_type.0 as i32, + hook::mem_hook_proxy:: as _, + core::ptr::from_mut(user_data.as_mut()).cast(), begin, end, ) @@ -749,23 +756,23 @@ impl<'a, D> Unicorn<'a, D> { } /// Add an interrupt hook. - pub fn add_intr_hook(&mut self, callback: F) -> Result + pub fn add_intr_hook(&mut self, callback: F) -> Result where - F: FnMut(&mut Unicorn, u32), + F: FnMut(&mut Unicorn, u32) + 'a, { - let mut hook_id = ptr::null_mut(); - let mut user_data = Box::new(ffi::UcHook { + let mut hook_id = 0; + let mut user_data = Box::new(hook::UcHook { callback, uc: Rc::downgrade(&self.inner), }); unsafe { - ffi::uc_hook_add( + uc_hook_add( self.get_handle(), - &mut hook_id, - HookType::INTR, - ffi::intr_hook_proxy:: as _, - user_data.as_mut() as *mut _ as _, + (&raw mut hook_id).cast(), + HookType::INTR.0 as i32, + hook::intr_hook_proxy:: as _, + core::ptr::from_mut(user_data.as_mut()).cast(), 0, 0, ) @@ -778,23 +785,23 @@ impl<'a, D> Unicorn<'a, D> { } /// Add hook for invalid instructions - pub fn add_insn_invalid_hook(&mut self, callback: F) -> Result + pub fn add_insn_invalid_hook(&mut self, callback: F) -> Result where - F: FnMut(&mut Unicorn) -> bool, + F: FnMut(&mut Unicorn) -> bool + 'a, { - let mut hook_id = ptr::null_mut(); - let mut user_data = Box::new(ffi::UcHook { + let mut hook_id = 0; + let mut user_data = Box::new(hook::UcHook { callback, uc: Rc::downgrade(&self.inner), }); unsafe { - ffi::uc_hook_add( + uc_hook_add( self.get_handle(), - &mut hook_id, - HookType::INSN_INVALID, - ffi::insn_invalid_hook_proxy:: as _, - user_data.as_mut() as *mut _ as _, + (&raw mut hook_id).cast(), + HookType::INSN_INVALID.0 as i32, + hook::insn_invalid_hook_proxy:: as _, + core::ptr::from_mut(user_data.as_mut()).cast(), 0, 0, ) @@ -808,26 +815,26 @@ impl<'a, D> Unicorn<'a, D> { /// Add hook for x86 IN instruction. #[cfg(feature = "arch_x86")] - pub fn add_insn_in_hook(&mut self, callback: F) -> Result + pub fn add_insn_in_hook(&mut self, callback: F) -> Result where - F: FnMut(&mut Unicorn, u32, usize) -> u32, + F: FnMut(&mut Unicorn, u32, usize) -> u32 + 'a, { - let mut hook_id = ptr::null_mut(); - let mut user_data = Box::new(ffi::UcHook { + let mut hook_id = 0; + let mut user_data = Box::new(hook::UcHook { callback, uc: Rc::downgrade(&self.inner), }); unsafe { - ffi::uc_hook_add( + uc_hook_add( self.get_handle(), - &mut hook_id, - HookType::INSN, - ffi::insn_in_hook_proxy:: as _, - user_data.as_mut() as *mut _ as _, + (&raw mut hook_id).cast(), + HookType::INSN.0 as i32, + hook::insn_in_hook_proxy:: as _, + core::ptr::from_mut(user_data.as_mut()).cast(), 0, 0, - InsnX86::IN, + X86Insn::IN, ) } .and_then(|| { @@ -839,26 +846,26 @@ impl<'a, D> Unicorn<'a, D> { /// Add hook for x86 OUT instruction. #[cfg(feature = "arch_x86")] - pub fn add_insn_out_hook(&mut self, callback: F) -> Result + pub fn add_insn_out_hook(&mut self, callback: F) -> Result where - F: FnMut(&mut Unicorn, u32, usize, u32), + F: FnMut(&mut Unicorn, u32, usize, u32) + 'a, { - let mut hook_id = ptr::null_mut(); - let mut user_data = Box::new(ffi::UcHook { + let mut hook_id = 0; + let mut user_data = Box::new(hook::UcHook { callback, uc: Rc::downgrade(&self.inner), }); unsafe { - ffi::uc_hook_add( + uc_hook_add( self.get_handle(), - &mut hook_id, - HookType::INSN, - ffi::insn_out_hook_proxy:: as _, - user_data.as_mut() as *mut _ as _, + (&raw mut hook_id).cast(), + HookType::INSN.0 as i32, + hook::insn_out_hook_proxy:: as _, + core::ptr::from_mut(user_data.as_mut()).cast(), 0, 0, - InsnX86::OUT, + X86Insn::OUT, ) } .and_then(|| { @@ -872,7 +879,7 @@ impl<'a, D> Unicorn<'a, D> { #[cfg(feature = "arch_x86")] pub fn add_insn_sys_hook( &mut self, - insn_type: InsnSysX86, + insn_type: X86Insn, begin: u64, end: u64, callback: F, @@ -880,19 +887,59 @@ impl<'a, D> Unicorn<'a, D> { where F: FnMut(&mut Unicorn) + 'a, { - let mut hook_id = ptr::null_mut(); - let mut user_data = Box::new(ffi::UcHook { + let mut hook_id = 0; + let mut user_data = Box::new(hook::UcHook { callback, uc: Rc::downgrade(&self.inner), }); unsafe { - ffi::uc_hook_add( + uc_hook_add( self.get_handle(), - &mut hook_id, - HookType::INSN, - ffi::insn_sys_hook_proxy:: as _, - user_data.as_mut() as *mut _ as _, + (&raw mut hook_id).cast(), + HookType::INSN.0 as i32, + hook::insn_sys_hook_proxy:: as _, + core::ptr::from_mut(user_data.as_mut()).cast(), + begin, + end, + insn_type, + ) + } + .and_then(|| { + let hook_id = UcHookId(hook_id); + self.inner_mut().hooks.push((hook_id, user_data)); + Ok(hook_id) + }) + } + + /// Add hook for ARM MRS/MSR/SYS/SYSL instructions. + /// + /// If the callback returns true, the read/write to system registers would be skipped (even + /// though that may cause exceptions!). Note one callback per instruction is allowed. + #[cfg(feature = "arch_aarch64")] + pub fn add_insn_sys_hook_arm64( + &mut self, + insn_type: Arm64Insn, + begin: u64, + end: u64, + callback: F, + ) -> Result + where + F: FnMut(&mut Unicorn, RegisterARM64, &RegisterARM64_CP) -> bool + 'a, + { + let mut hook_id = 0; + let mut user_data = Box::new(hook::UcHook { + callback, + uc: Rc::downgrade(&self.inner), + }); + + unsafe { + uc_hook_add( + self.get_handle(), + (&raw mut hook_id).cast(), + HookType::INSN.0 as i32, + hook::insn_sys_hook_proxy_arm64:: as _, + core::ptr::from_mut(user_data.as_mut()).cast(), begin, end, insn_type, @@ -914,19 +961,19 @@ impl<'a, D> Unicorn<'a, D> { where F: FnMut(&mut Unicorn, u64, MemType) -> Option + 'a, { - let mut hook_id = ptr::null_mut(); - let mut user_data = Box::new(ffi::UcHook { + let mut hook_id = 0; + let mut user_data = Box::new(hook::UcHook { callback, uc: Rc::downgrade(&self.inner), }); unsafe { - ffi::uc_hook_add( + uc_hook_add( self.get_handle(), - &mut hook_id, - HookType::TLB, - ffi::tlb_lookup_hook_proxy:: as _, - user_data.as_mut() as *mut _ as _, + (&raw mut hook_id).cast(), + HookType::TLB_FILL.0 as i32, + hook::tlb_lookup_hook_proxy:: as _, + core::ptr::from_mut(user_data.as_mut()).cast(), begin, end, ) @@ -938,6 +985,43 @@ impl<'a, D> Unicorn<'a, D> { }) } + pub fn add_tcg_hook( + &mut self, + code: TcgOpCode, + flag: TcgOpFlag, + begin: u64, + end: u64, + callback: F, + ) -> Result + where + F: FnMut(&mut Unicorn, u64, u64, u64, usize) + 'a, + { + let mut hook_id = 0; + let mut user_data = Box::new(hook::UcHook { + callback, + uc: Rc::downgrade(&self.inner), + }); + + unsafe { + uc_hook_add( + self.get_handle(), + (&raw mut hook_id).cast(), + HookType::TCG_OPCODE.0 as i32, + hook::tcg_proxy:: as _, + core::ptr::from_mut(user_data.as_mut()).cast(), + begin, + end, + code as i32, + flag.0 as i32, + ) + .and_then(|| { + let hook_id = UcHookId(hook_id); + self.inner_mut().hooks.push((hook_id, user_data)); + Ok(hook_id) + }) + } + } + /// Remove a hook. /// /// `hook_id` is the value returned by `add_*_hook` functions. @@ -946,22 +1030,22 @@ impl<'a, D> Unicorn<'a, D> { let inner = self.inner_mut(); inner.hooks.retain(|(id, _)| id != &hook_id); - unsafe { ffi::uc_hook_del(inner.handle, hook_id.0) }.into() + unsafe { uc_hook_del(inner.handle, hook_id.0) }.into() } /// Allocate and return an empty Unicorn context. /// /// To be populated via `context_save`. pub fn context_alloc(&self) -> Result { - let mut empty_context: ffi::uc_context = ptr::null_mut(); - unsafe { ffi::uc_context_alloc(self.get_handle(), &mut empty_context) }.and(Ok(Context { + let mut empty_context = ptr::null_mut(); + unsafe { uc_context_alloc(self.get_handle(), &raw mut empty_context) }.and(Ok(Context { context: empty_context, })) } /// Save current Unicorn context to previously allocated Context struct. pub fn context_save(&self, context: &mut Context) -> Result<(), uc_error> { - unsafe { ffi::uc_context_save(self.get_handle(), context.context) }.into() + unsafe { uc_context_save(self.get_handle(), context.context) }.into() } /// Allocate and return a Context struct initialized with the current CPU context. @@ -970,16 +1054,15 @@ impl<'a, D> Unicorn<'a, D> { /// In case of many non-concurrent context saves, use `context_alloc` and *_save /// individually to avoid unnecessary allocations. pub fn context_init(&self) -> Result { - let mut new_context: ffi::uc_context = ptr::null_mut(); + let mut new_context = ptr::null_mut(); unsafe { - ffi::uc_context_alloc(self.get_handle(), &mut new_context).and_then(|| { - ffi::uc_context_save(self.get_handle(), new_context) + uc_context_alloc(self.get_handle(), &raw mut new_context).and_then(|| { + uc_context_save(self.get_handle(), new_context) .and(Ok(Context { context: new_context, })) - .map_err(|e| { - ffi::uc_context_free(new_context); - e + .inspect_err(|_| { + uc_context_free(new_context); }) }) } @@ -991,7 +1074,7 @@ impl<'a, D> Unicorn<'a, D> { /// internal metadata. Contexts may not be shared across engine instances with /// differing arches or modes. Memory has to be restored manually, if needed. pub fn context_restore(&self, context: &Context) -> Result<(), uc_error> { - unsafe { ffi::uc_context_restore(self.get_handle(), context.context) }.into() + unsafe { uc_context_restore(self.get_handle(), context.context) }.into() } /// Emulate machine code for a specified duration. @@ -1007,7 +1090,7 @@ impl<'a, D> Unicorn<'a, D> { timeout: u64, count: usize, ) -> Result<(), uc_error> { - unsafe { ffi::uc_emu_start(self.get_handle(), begin, until, timeout, count as _) }.into() + unsafe { uc_emu_start(self.get_handle(), begin, until, timeout, count as _) }.into() } /// Stop the emulation. @@ -1015,22 +1098,21 @@ impl<'a, D> Unicorn<'a, D> { /// This is usually called from callback function in hooks. /// NOTE: For now, this will stop the execution only after the current block. pub fn emu_stop(&mut self) -> Result<(), uc_error> { - unsafe { ffi::uc_emu_stop(self.get_handle()).into() } + unsafe { uc_emu_stop(self.get_handle()).into() } } /// Query the internal status of the engine. /// /// supported: `MODE`, `PAGE_SIZE`, `ARCH` pub fn query(&self, query: Query) -> Result { - let mut result: libc::size_t = Default::default(); - unsafe { ffi::uc_query(self.get_handle(), query, &mut result) }.and(Ok(result)) + let mut result = 0; + unsafe { uc_query(self.get_handle(), query, &mut result) }.and(Ok(result)) } /// Get the `i32` register value for the program counter for the specified architecture. /// /// If an architecture is not compiled in, this function will return `uc_error::ARCH`. - #[inline] - fn arch_to_pc_register(arch: Arch) -> Result { + const fn arch_to_pc_register(arch: Arch) -> Result { match arch { #[cfg(feature = "arch_x86")] Arch::X86 => Ok(RegisterX86::RIP as i32), @@ -1059,7 +1141,6 @@ impl<'a, D> Unicorn<'a, D> { } /// Gets the current program counter for this `unicorn` instance. - #[inline] pub fn pc_read(&self) -> Result { let arch = self.get_arch(); @@ -1067,7 +1148,6 @@ impl<'a, D> Unicorn<'a, D> { } /// Sets the program counter for this `unicorn` instance. - #[inline] pub fn set_pc(&mut self, value: u64) -> Result<(), uc_error> { let arch = self.get_arch(); @@ -1075,34 +1155,34 @@ impl<'a, D> Unicorn<'a, D> { } pub fn ctl_get_mode(&self) -> Result { - let mut result: i32 = Default::default(); + let mut result = 0; unsafe { - ffi::uc_ctl( + uc_ctl( self.get_handle(), - UC_CTL_READ!(ControlType::UC_CTL_UC_MODE), + UC_CTL_READ!(ControlType::UC_MODE), &mut result, ) } - .and_then(|| Ok(Mode::from_bits_truncate(result))) + .and_then(|| Ok(Mode::try_from(result)))? } pub fn ctl_get_page_size(&self) -> Result { - let mut result: u32 = Default::default(); + let mut result = 0; unsafe { - ffi::uc_ctl( + uc_ctl( self.get_handle(), - UC_CTL_READ!(ControlType::UC_CTL_UC_PAGE_SIZE), + UC_CTL_READ!(ControlType::UC_PAGE_SIZE), &mut result, ) } .and_then(|| Ok(result)) } - pub fn ctl_set_page_size(&self, page_size: u32) -> Result<(), uc_error> { + pub fn ctl_set_page_size(&mut self, page_size: u32) -> Result<(), uc_error> { unsafe { - ffi::uc_ctl( + uc_ctl( self.get_handle(), - UC_CTL_WRITE!(ControlType::UC_CTL_UC_PAGE_SIZE), + UC_CTL_WRITE!(ControlType::UC_PAGE_SIZE), page_size, ) } @@ -1110,11 +1190,11 @@ impl<'a, D> Unicorn<'a, D> { } pub fn ctl_get_arch(&self) -> Result { - let mut result: i32 = Default::default(); + let mut result = 0; unsafe { - ffi::uc_ctl( + uc_ctl( self.get_handle(), - UC_CTL_READ!(ControlType::UC_CTL_UC_ARCH), + UC_CTL_READ!(ControlType::UC_ARCH), &mut result, ) } @@ -1122,33 +1202,33 @@ impl<'a, D> Unicorn<'a, D> { } pub fn ctl_get_timeout(&self) -> Result { - let mut result: u64 = Default::default(); + let mut result = 0; unsafe { - ffi::uc_ctl( + uc_ctl( self.get_handle(), - UC_CTL_READ!(ControlType::UC_CTL_UC_TIMEOUT), + UC_CTL_READ!(ControlType::UC_TIMEOUT), &mut result, ) } .and(Ok(result)) } - pub fn ctl_exits_enable(&self) -> Result<(), uc_error> { + pub fn ctl_exits_enable(&mut self) -> Result<(), uc_error> { unsafe { - ffi::uc_ctl( + uc_ctl( self.get_handle(), - UC_CTL_WRITE!(ControlType::UC_CTL_UC_USE_EXITS), + UC_CTL_WRITE!(ControlType::UC_USE_EXITS), 1, ) } .into() } - pub fn ctl_exits_disable(&self) -> Result<(), uc_error> { + pub fn ctl_exits_disable(&mut self) -> Result<(), uc_error> { unsafe { - ffi::uc_ctl( + uc_ctl( self.get_handle(), - UC_CTL_WRITE!(ControlType::UC_CTL_UC_USE_EXITS), + UC_CTL_WRITE!(ControlType::UC_USE_EXITS), 0, ) } @@ -1156,11 +1236,11 @@ impl<'a, D> Unicorn<'a, D> { } pub fn ctl_get_exits_count(&self) -> Result { - let mut result: libc::size_t = 0usize; + let mut result = 0; unsafe { - ffi::uc_ctl( + uc_ctl( self.get_handle(), - UC_CTL_READ!(ControlType::UC_CTL_UC_EXITS_CNT), + UC_CTL_READ!(ControlType::UC_EXITS_CNT), &mut result, ) } @@ -1168,12 +1248,12 @@ impl<'a, D> Unicorn<'a, D> { } pub fn ctl_get_exits(&self) -> Result, uc_error> { - let exits_count: libc::size_t = self.ctl_get_exits_count()?; - let mut exits: Vec = Vec::with_capacity(exits_count); + let exits_count = self.ctl_get_exits_count()?; + let mut exits = Vec::with_capacity(exits_count); unsafe { - ffi::uc_ctl( + uc_ctl( self.get_handle(), - UC_CTL_READ!(ControlType::UC_CTL_UC_EXITS), + UC_CTL_READ!(ControlType::UC_EXITS), exits.as_mut_ptr(), exits_count, ) @@ -1184,46 +1264,46 @@ impl<'a, D> Unicorn<'a, D> { }) } - pub fn ctl_set_exits(&self, exits: &[u64]) -> Result<(), uc_error> { + pub fn ctl_set_exits(&mut self, exits: &[u64]) -> Result<(), uc_error> { unsafe { - ffi::uc_ctl( + uc_ctl( self.get_handle(), - UC_CTL_WRITE!(ControlType::UC_CTL_UC_EXITS), + UC_CTL_WRITE!(ControlType::UC_EXITS), exits.as_ptr(), - exits.len() as libc::size_t, + exits.len(), ) } .into() } pub fn ctl_get_cpu_model(&self) -> Result { - let mut result: i32 = Default::default(); + let mut result = 0; unsafe { - ffi::uc_ctl( + uc_ctl( self.get_handle(), - UC_CTL_READ!(ControlType::UC_CTL_CPU_MODEL), + UC_CTL_READ!(ControlType::CPU_MODEL), &mut result, ) } .and(Ok(result)) } - pub fn ctl_set_cpu_model(&self, cpu_model: i32) -> Result<(), uc_error> { + pub fn ctl_set_cpu_model(&mut self, cpu_model: i32) -> Result<(), uc_error> { unsafe { - ffi::uc_ctl( + uc_ctl( self.get_handle(), - UC_CTL_WRITE!(ControlType::UC_CTL_CPU_MODEL), + UC_CTL_WRITE!(ControlType::CPU_MODEL), cpu_model, ) } .into() } - pub fn ctl_remove_cache(&self, address: u64, end: u64) -> Result<(), uc_error> { + pub fn ctl_remove_cache(&mut self, address: u64, end: u64) -> Result<(), uc_error> { unsafe { - ffi::uc_ctl( + uc_ctl( self.get_handle(), - UC_CTL_WRITE!(ControlType::UC_CTL_TB_REMOVE_CACHE), + UC_CTL_WRITE!(ControlType::TB_REMOVE_CACHE), address, end, ) @@ -1234,55 +1314,44 @@ impl<'a, D> Unicorn<'a, D> { pub fn ctl_request_cache( &self, address: u64, - tb: &mut TranslationBlock, + tb: Option<&mut TranslationBlock>, ) -> Result<(), uc_error> { + let tb_ptr = tb.map_or(ptr::null_mut(), core::ptr::from_mut); unsafe { - ffi::uc_ctl( + uc_ctl( self.get_handle(), - UC_CTL_READ_WRITE!(ControlType::UC_CTL_TB_REQUEST_CACHE), + UC_CTL_READ_WRITE!(ControlType::TB_REQUEST_CACHE), address, - tb, + tb_ptr, ) } .into() } - pub fn ctl_flush_tb(&self) -> Result<(), uc_error> { - unsafe { - ffi::uc_ctl( - self.get_handle(), - UC_CTL_WRITE!(ControlType::UC_CTL_TB_FLUSH), - ) - } - .into() + pub fn ctl_flush_tb(&mut self) -> Result<(), uc_error> { + unsafe { uc_ctl(self.get_handle(), UC_CTL_WRITE!(ControlType::TB_FLUSH)) }.into() } - pub fn ctl_flush_tlb(&self) -> Result<(), uc_error> { - unsafe { - ffi::uc_ctl( - self.get_handle(), - UC_CTL_WRITE!(ControlType::UC_CTL_TLB_FLUSH), - ) - } - .into() + pub fn ctl_flush_tlb(&mut self) -> Result<(), uc_error> { + unsafe { uc_ctl(self.get_handle(), UC_CTL_WRITE!(ControlType::TLB_FLUSH)) }.into() } - pub fn ctl_context_mode(&self, mode: ContextMode) -> Result<(), uc_error> { + pub fn ctl_set_context_mode(&mut self, mode: ContextMode) -> Result<(), uc_error> { unsafe { - ffi::uc_ctl( + uc_ctl( self.get_handle(), - UC_CTL_WRITE!(ControlType::UC_CTL_CONTEXT_MODE), + UC_CTL_WRITE!(ControlType::CONTEXT_MODE), mode, ) } .into() } - pub fn ctl_tlb_type(&self, t: TlbType) -> Result<(), uc_error> { + pub fn ctl_set_tlb_type(&mut self, t: TlbType) -> Result<(), uc_error> { unsafe { - ffi::uc_ctl( + uc_ctl( self.get_handle(), - UC_CTL_WRITE!(ControlType::UC_CTL_TLB_TYPE), + UC_CTL_WRITE!(ControlType::TLB_TYPE), t as i32, ) } diff --git a/bindings/rust/src/m68k.rs b/bindings/rust/src/m68k.rs deleted file mode 100644 index ccc8cfc295..0000000000 --- a/bindings/rust/src/m68k.rs +++ /dev/null @@ -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 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 for i32 { - fn from(value: M68kCpuModel) -> Self { - value as i32 - } -} - -impl From<&M68kCpuModel> for i32 { - fn from(value: &M68kCpuModel) -> Self { - (*value) as i32 - } -} diff --git a/bindings/rust/src/mips.rs b/bindings/rust/src/mips.rs deleted file mode 100644 index 208c251b2b..0000000000 --- a/bindings/rust/src/mips.rs +++ /dev/null @@ -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 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 for i32 { - fn from(value: Mips32CpuModel) -> Self { - value as i32 - } -} - -impl From<&Mips32CpuModel> for i32 { - fn from(value: &Mips32CpuModel) -> Self { - *value as i32 - } -} diff --git a/bindings/rust/src/ppc.rs b/bindings/rust/src/ppc.rs deleted file mode 100644 index 63b26e1f33..0000000000 --- a/bindings/rust/src/ppc.rs +++ /dev/null @@ -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 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 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 for i32 { - fn from(value: Ppc64CpuModel) -> Self { - value as i32 - } -} - -impl From<&Ppc64CpuModel> for i32 { - fn from(value: &Ppc64CpuModel) -> Self { - (*value) as i32 - } -} diff --git a/bindings/rust/src/riscv.rs b/bindings/rust/src/riscv.rs deleted file mode 100644 index 53c5990bc3..0000000000 --- a/bindings/rust/src/riscv.rs +++ /dev/null @@ -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 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 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 for i32 { - fn from(value: Riscv64CpuModel) -> Self { - value as i32 - } -} - -impl From<&Riscv64CpuModel> for i32 { - fn from(value: &Riscv64CpuModel) -> Self { - (*value) as i32 - } -} diff --git a/bindings/rust/src/s390x.rs b/bindings/rust/src/s390x.rs deleted file mode 100644 index e158cf3d10..0000000000 --- a/bindings/rust/src/s390x.rs +++ /dev/null @@ -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 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 for i32 { - fn from(value: S390xCpuModel) -> Self { - value as i32 - } -} - -impl From<&S390xCpuModel> for i32 { - fn from(value: &S390xCpuModel) -> Self { - (*value) as i32 - } -} diff --git a/bindings/rust/src/sparc.rs b/bindings/rust/src/sparc.rs deleted file mode 100644 index c0bd4f2abb..0000000000 --- a/bindings/rust/src/sparc.rs +++ /dev/null @@ -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 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 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 for i32 { - fn from(value: Sparc64CpuModel) -> Self { - value as i32 - } -} - -impl From<&Sparc64CpuModel> for i32 { - fn from(value: &Sparc64CpuModel) -> Self { - (*value) as i32 - } -} diff --git a/bindings/rust/src/tests/arm.rs b/bindings/rust/src/tests/arm.rs new file mode 100644 index 0000000000..68aa65964b --- /dev/null +++ b/bindings/rust/src/tests/arm.rs @@ -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 . +#[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); +} diff --git a/bindings/rust/src/tests/arm64.rs b/bindings/rust/src/tests/arm64.rs new file mode 100644 index 0000000000..8c881c609a --- /dev/null +++ b/bindings/rust/src/tests/arm64.rs @@ -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::(), + ) + .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); +} diff --git a/bindings/rust/src/tests/ctl.rs b/bindings/rust/src/tests/ctl.rs new file mode 100644 index 0000000000..c10af73903 --- /dev/null +++ b/bindings/rust/src/tests/ctl.rs @@ -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); +} diff --git a/bindings/rust/src/tests/m68k.rs b/bindings/rust/src/tests/m68k.rs new file mode 100644 index 0000000000..1f8c31b0e3 --- /dev/null +++ b/bindings/rust/src/tests/m68k.rs @@ -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); +} diff --git a/bindings/rust/src/tests/mem.rs b/bindings/rust/src/tests/mem.rs new file mode 100644 index 0000000000..a17b760fde --- /dev/null +++ b/bindings/rust/src/tests/mem.rs @@ -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); +} diff --git a/bindings/rust/src/tests/mips.rs b/bindings/rust/src/tests/mips.rs new file mode 100644 index 0000000000..288ffa80b8 --- /dev/null +++ b/bindings/rust/src/tests/mips.rs @@ -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( + arch: Arch, + mode: Mode, + cpu_model: Option, + 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); +} diff --git a/bindings/rust/src/tests/mod.rs b/bindings/rust/src/tests/mod.rs new file mode 100644 index 0000000000..cc5b9488bb --- /dev/null +++ b/bindings/rust/src/tests/mod.rs @@ -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( + arch: Arch, + mode: Mode, + cpu_model: Option, + 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 +} diff --git a/bindings/rust/src/tests/ppc.rs b/bindings/rust/src/tests/ppc.rs new file mode 100644 index 0000000000..8e6eced572 --- /dev/null +++ b/bindings/rust/src/tests/ppc.rs @@ -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(); +} diff --git a/bindings/rust/src/tests/riscv.rs b/bindings/rust/src/tests/riscv.rs new file mode 100644 index 0000000000..5ae7f63eb1 --- /dev/null +++ b/bindings/rust/src/tests/riscv.rs @@ -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); +} diff --git a/bindings/rust/src/tests/s390x.rs b/bindings/rust/src/tests/s390x.rs new file mode 100644 index 0000000000..0d0eae84a6 --- /dev/null +++ b/bindings/rust/src/tests/s390x.rs @@ -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); +} diff --git a/bindings/rust/src/tricore.rs b/bindings/rust/src/tricore.rs deleted file mode 100644 index 3d03a21050..0000000000 --- a/bindings/rust/src/tricore.rs +++ /dev/null @@ -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 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 for i32 { - fn from(value: TricoreCpuModel) -> Self { - value as i32 - } -} - -impl From<&TricoreCpuModel> for i32 { - fn from(value: &TricoreCpuModel) -> Self { - (*value) as i32 - } -} diff --git a/bindings/rust/src/unicorn_const.rs b/bindings/rust/src/unicorn_const.rs index e5a7d0c015..0311641455 100644 --- a/bindings/rust/src/unicorn_const.rs +++ b/bindings/rust/src/unicorn_const.rs @@ -1,5 +1,4 @@ -#![allow(non_camel_case_types)] -use bitflags::bitflags; +pub use unicorn_engine_sys::*; pub const API_MAJOR: u64 = 2; 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_EXTRA: u64 = 255; pub const SECOND_SCALE: u64 = 1_000_000; -pub const MILISECOND_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 Result>(self, op: F) -> Result { - 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(self, res: Result) -> Result { - if let Self::OK = self { - res - } else { - Err(self) - } - } -} - -impl From 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 for Arch { - type Error = uc_error; - - fn try_from(v: usize) -> Result { - 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, -} +pub const MILLISECOND_SCALE: u64 = 1_000; macro_rules! UC_CTL_READ { ($expr:expr) => { - $expr as u32 | ControlType::UC_CTL_IO_READ as u32 + ControlType($expr.0 | ControlType::IO_READ.0) }; } macro_rules! UC_CTL_WRITE { ($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 { ($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, -} diff --git a/bindings/rust/sys/Cargo.toml b/bindings/rust/sys/Cargo.toml new file mode 100644 index 0000000000..ad8c5f4499 --- /dev/null +++ b/bindings/rust/sys/Cargo.toml @@ -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 = [] diff --git a/bindings/rust/sys/README.md b/bindings/rust/sys/README.md new file mode 100644 index 0000000000..ca03938ca6 --- /dev/null +++ b/bindings/rust/sys/README.md @@ -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" +``` diff --git a/bindings/rust/sys/build.rs b/bindings/rust/sys/build.rs new file mode 100644 index 0000000000..9bef3607c7 --- /dev/null +++ b/bindings/rust/sys/build.rs @@ -0,0 +1,352 @@ +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::() + .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(); + } + + // 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 "/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 { + // 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 { + 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::().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(); + } + } +} diff --git a/bindings/rust/sys/src/lib.rs b/bindings/rust/sys/src/lib.rs new file mode 100644 index 0000000000..94425f7dd0 --- /dev/null +++ b/bindings/rust/sys/src/lib.rs @@ -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 Result>(self, op: F) -> Result { + 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(self, res: Result) -> Result { + if self == Self::OK { res } else { Err(self) } + } +} + +impl From for Result<(), uc_error> { + fn from(value: uc_error) -> Self { + if value == uc_error::OK { + Ok(()) + } else { + Err(value) + } + } +} + +impl TryFrom for Arch { + type Error = uc_error; + + fn try_from(v: usize) -> Result { + 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 for Mode { + type Error = uc_error; + + #[allow(clippy::cognitive_complexity)] + fn try_from(v: i32) -> Result { + 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 for i32 { + fn from(value: RegisterM68K) -> Self { + value as Self + } +} + +impl From for i32 { + fn from(value: RegisterX86) -> Self { + value as Self + } +} + +impl From for i32 { + fn from(value: RegisterARM) -> Self { + value as Self + } +} + +impl From for i32 { + fn from(value: RegisterARM64) -> Self { + value as Self + } +} + +impl From for i32 { + fn from(value: RegisterMIPS) -> Self { + value as Self + } +} + +impl From for i32 { + fn from(value: RegisterSPARC) -> Self { + value as Self + } +} + +impl From for i32 { + fn from(value: RegisterPPC) -> Self { + value as Self + } +} + +impl From for i32 { + fn from(value: RegisterRISCV) -> Self { + value as Self + } +} + +impl From for i32 { + fn from(value: RegisterS390X) -> Self { + value as Self + } +} + +impl From for i32 { + fn from(value: RegisterTRICORE) -> Self { + value as Self + } +} diff --git a/include/unicorn/mips.h b/include/unicorn/mips.h index 7a4c9c1cb8..5b8ac26be5 100644 --- a/include/unicorn/mips.h +++ b/include/unicorn/mips.h @@ -61,7 +61,7 @@ typedef enum uc_cpu_mips64 { } uc_cpu_mips64; //> MIPS registers -typedef enum UC_MIPS_REG { +typedef enum uc_mips_reg { UC_MIPS_REG_INVALID = 0, //> General purpose registers UC_MIPS_REG_PC, @@ -271,7 +271,10 @@ typedef enum UC_MIPS_REG { UC_MIPS_REG_LO1 = UC_MIPS_REG_HI1, UC_MIPS_REG_LO2 = UC_MIPS_REG_HI2, 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 } diff --git a/tests/rust-tests/main.rs b/tests/rust-tests/main.rs deleted file mode 100644 index 1661890e8d..0000000000 --- a/tests/rust-tests/main.rs +++ /dev/null @@ -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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 = 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 { - 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 = 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 = 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 = 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(®ids, 4), - Ok(expect) - ); - let regvals = vec![0x50, 0x60, 0x70, 0x80]; - assert_eq!( - emu.reg_write_batch(®ids, ®vals, 4), - Ok(()) - ); - assert_eq!( - emu.reg_read_batch(®ids, 4), - Ok(vec![0x50, 0x60, 0x70, 0x80]) - ); - -} diff --git a/tests/unit/test_arm.c b/tests/unit/test_arm.c index 538a91c0e2..1ea21e669c 100644 --- a/tests/unit/test_arm.c +++ b/tests/unit/test_arm.c @@ -590,9 +590,7 @@ static void test_arm_mem_access_abort(void) r_r0 = 0x990000; OK(uc_reg_write(uc, UC_ARM_REG_R0, &r_r0)); - OK(uc_hook_add(uc, &hk, - UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED | - UC_HOOK_MEM_FETCH_UNMAPPED, + OK(uc_hook_add(uc, &hk, UC_HOOK_MEM_UNMAPPED, test_arm_mem_access_abort_hook_mem, (void *)&r_pc_in_hook, 1, 0)); OK(uc_hook_add(uc, &hkk, UC_HOOK_INSN_INVALID, diff --git a/tests/unit/test_arm64.c b/tests/unit/test_arm64.c index e6e9156533..cc0ee05803 100644 --- a/tests/unit/test_arm64.c +++ b/tests/unit/test_arm64.c @@ -584,15 +584,11 @@ static void test_arm64_mem_prot_regress(void) test_arm64_mem_prot_regress_hook_mem, NULL, 1, 0)); uc_hook hh_prot; - OK(uc_hook_add(uc, &hh_prot, - UC_HOOK_MEM_READ_PROT | UC_HOOK_MEM_WRITE_PROT | - UC_HOOK_MEM_FETCH_PROT, + OK(uc_hook_add(uc, &hh_prot, UC_HOOK_MEM_PROT, test_arm64_mem_prot_regress_hook_prot, NULL, 1, 0)); uc_hook hh_unm; - OK(uc_hook_add(uc, &hh_unm, - UC_HOOK_MEM_READ_UNMAPPED | UC_HOOK_MEM_WRITE_UNMAPPED | - UC_HOOK_MEM_FETCH_UNMAPPED, + OK(uc_hook_add(uc, &hh_unm, UC_HOOK_MEM_UNMAPPED, test_arm64_mem_prot_regress_hook_unm, NULL, 1, 0)); const uint64_t value = 0x801b; diff --git a/tests/unit/test_m68k.c b/tests/unit/test_m68k.c index 20a4e61c4e..335caf2bc0 100644 --- a/tests/unit/test_m68k.c +++ b/tests/unit/test_m68k.c @@ -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_reg_read(uc, UC_M68K_REG_SR, &r_sr)); + + TEST_CHECK(r_sr == 0x2700); + OK(uc_close(uc)); } -TEST_LIST = {{"test_move_to_sr", test_move_to_sr}, {NULL, NULL}}; \ No newline at end of file +TEST_LIST = {{"test_move_to_sr", test_move_to_sr}, {NULL, NULL}}; diff --git a/tests/unit/test_mem.c b/tests/unit/test_mem.c index eac4774085..6e3056ffb1 100644 --- a/tests/unit/test_mem.c +++ b/tests/unit/test_mem.c @@ -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 - *(uint64_t *)user_data = *(uint64_t *)user_data + 1; + *(uint64_t *)user_data += 1; return 0x114514; }