capstone-0.7.0/.gitignore010066400017500001750000000000271364465622700135630ustar0000000000000000demo target Cargo.lock capstone-0.7.0/CHANGELOG.md010066400017500001750000000104101364465622700134010ustar0000000000000000# Changelog Notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [0.7.0] - 2020-03-16 ### Added - `no_std` compatibility - Parallel disassemble example - X86: add `X86Operand.access` field - Implement `From` for *_ins enums ### Changed - Bump minimum Rust version to 1.36.0 ## [0.6.0] - 2019-04-17 ### Added - Architectures: EVM, M68K, M680X, TMS320C64X - Mips modes: `Mips2`, `Mips3`, `Mips32`, `Mips64` - Trait `EnumList` to allow you to enumerate enum variants - X86: `X86InsnDetail::xop_cc()` getter ### Changed - Bump minimum Rust version to 1.29.2 - Upgraded internal capstone C library to version 4.0 - Moved `capstone-sys` repository into `capstone-rs` repository - Converted operand `Imm` variant from `i32` to `i64` for PPC, SPARC - `X86InsnDetail::disp()` returns `i64` instead of `i32` ### Removed - Mips modes: `Mode32`, `Mode64`, `MipsGP64` - `X86OperandType` variant `Fp` ## [0.5.0] - 2018-09-21 ### Added - `InsnDetail` to preamble ### Changed - Flattened `Error` enum ### Removed - `X86InsnDetail::avx_rm()` ## [0.4.0] - 2018-06-02 ### Added - [Criterion](https://github.com/japaric/criterion.rs) [benchmark](benches) - [cstool example](examples/cstool.rs) - [Codecov](https://codecov.io/gh/capstone-rust/capstone-rs) code coverage integration - `PartialOrd`/`Ord` implementation for `InsnId`, `InsnGroupId`, `RegId` - Lifetime to `Capstone`/`Insn` struct - `alloc_system` feature to use the system allocator instead of the default allocator (currently requires nightly) ### Changed - Minimum Rust version to 1.23.0 - `Capstone::disasm()` methods take `&mut self` instead of `&self` and returns a new lifetime - `Capstone` is no longer `Send`/`Sync` (it was mistakenly auto-implemented) - `Capstone::new()` builder pattern methods take `self` instead of `&mut self` - `Capstone::set_endian()` is now public (allowed since internal Capstone version was upgraded) ### Removed - Duplicate/unneeded `Capstone` methods that have equivalents in `InsnDetail` - `insn_belongs_to_group()`, `insn_group_ids()`, `register_id_is_read()`, `read_register_ids()`, `register_id_is_written()`, `write_register_ids()` ### Fixed - Race condition and memory unsafety in issue most easily observed on Mac OS (issue [#26](https://github.com/capstone-rust/capstone-rs/issues/26)) ## [0.3.1] - 2018-03-26 ### Fixed - Documentation URL ## [0.3.0] - 2018-03-26 ### Added - Architecture-specific detail API with `InsnDetail::arch_detail()` method - README badges! ### Changed - `Capstone::disasm()` (and related methods) return empty `Instructions` instead of an error - Make `Instructions::from_raw_parts()` private ## [0.2.0] - 2017-11-01 ### Added - `Capstone::new_raw()` has the same interface as the old `Capstone::new_raw()` - Add setters to modify mode, syntax, etc. ### Changed - `Capstone::new()` uses the builder pattern - Partition `Mode` enum into: `Mode`, `ExtraMode`, and `Endian` - Rename `Capstone` methods that return IDs to include `_ids` in name - Example: `read_registers()` renamed to `read_register_ids()` - Minimum Rust version is 1.20.0 ### Removed - `libc` dependency ## [0.1.0] - 2017-09-29 ### Added - `Capstone` methods to set syntax and mode - Travis continuous integration ### Changed - Use [`capstone-sys`](https://github.com/capstone-rust/capstone-sys) crate for low-level Capstone bindings - `Capstone::new()` takes a `arch` and `mode` arguments - `Capstone::disasm()` replaced with `Capstone::disasm_all()`/`Capstone::disasm_count()` ### Removed - Dependency [0.7.0]: https://github.com/capstone-rust/capstone-rs/compare/capstone-v0.6.0...capstone-v0.7.0 [0.6.0]: https://github.com/capstone-rust/capstone-rs/compare/v0.5.0...capstone-v0.6.0 [0.5.0]: https://github.com/capstone-rust/capstone-rs/compare/v0.4.0...v0.5.0 [0.4.0]: https://github.com/capstone-rust/capstone-rs/compare/v0.3.1...v0.4.0 [0.3.1]: https://github.com/capstone-rust/capstone-rs/compare/v0.3.0...v0.3.1 [0.3.0]: https://github.com/capstone-rust/capstone-rs/compare/v0.2.0...v0.3.0 [0.2.0]: https://github.com/capstone-rust/capstone-rs/compare/v0.1.0...v0.2.0 [0.1.0]: https://github.com/capstone-rust/capstone-rs/releases/tag/v0.1.0 capstone-0.7.0/Cargo.toml.orig010066400017500001750000000014571364465622700144720ustar0000000000000000[package] authors = ["m4b ", "Richo Healey ", "Travis Finkenauer "] description = "High level bindings to capstone disassembly engine (https://capstone-engine.org/)" keywords = ["disassemble"] license = "MIT" name = "capstone" repository = "https://github.com/capstone-rust/capstone-rs" readme = "README.md" edition = "2018" version = "0.7.0" [badges] travis-ci = { repository = "capstone-rust/capstone-rs" } [dependencies] capstone-sys = { path = "../capstone-sys", version = "0.11.0" } libc = { version = "0.2", default-features = false } [dev-dependencies] macho = "0.*" criterion = "0.2" rayon = "1.1" [[bench]] name = "my_benchmark" harness = false [features] default = [] use_bindgen = ["capstone-sys/use_bindgen"] capstone-0.7.0/Cargo.toml0000644000000025051364465636200107710ustar00# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies # # If you believe there's an error in this file please file an # issue against the rust-lang/cargo repository. If you're # editing this file be aware that the upstream Cargo.toml # will likely look very different (and much more reasonable) [package] edition = "2018" name = "capstone" version = "0.7.0" authors = ["m4b ", "Richo Healey ", "Travis Finkenauer "] description = "High level bindings to capstone disassembly engine (https://capstone-engine.org/)" readme = "README.md" keywords = ["disassemble"] license = "MIT" repository = "https://github.com/capstone-rust/capstone-rs" [[bench]] name = "my_benchmark" harness = false [dependencies.capstone-sys] version = "0.11.0" [dependencies.libc] version = "0.2" default-features = false [dev-dependencies.criterion] version = "0.2" [dev-dependencies.macho] version = "0.*" [dev-dependencies.rayon] version = "1.1" [features] default = [] use_bindgen = ["capstone-sys/use_bindgen"] [badges.travis-ci] repository = "capstone-rust/capstone-rs" capstone-0.7.0/Cargo.toml.orig0000644000000014571364465636200117350ustar00[package] authors = ["m4b ", "Richo Healey ", "Travis Finkenauer "] description = "High level bindings to capstone disassembly engine (https://capstone-engine.org/)" keywords = ["disassemble"] license = "MIT" name = "capstone" repository = "https://github.com/capstone-rust/capstone-rs" readme = "README.md" edition = "2018" version = "0.7.0" [badges] travis-ci = { repository = "capstone-rust/capstone-rs" } [dependencies] capstone-sys = { path = "../capstone-sys", version = "0.11.0" } libc = { version = "0.2", default-features = false } [dev-dependencies] macho = "0.*" criterion = "0.2" rayon = "1.1" [[bench]] name = "my_benchmark" harness = false [features] default = [] use_bindgen = ["capstone-sys/use_bindgen"] capstone-0.7.0/LICENSE010066400017500001750000000021101364465622700125730ustar0000000000000000The MIT License Copyright (c) 2015 Richo Healey Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. capstone-0.7.0/README.md010066400017500001750000000074221364465622700130600ustar0000000000000000# capstone-rs [![Crates.io Badge](https://img.shields.io/crates/v/capstone.svg)](https://crates.io/crates/capstone) Linux/MacOS [![Travis CI Badge](https://travis-ci.org/capstone-rust/capstone-rs.svg?branch=master)](https://travis-ci.org/capstone-rust/capstone-rs) | Windows [![Appveyor CI Badge](https://ci.appveyor.com/api/projects/status/github/capstone-rust/capstone-rs?svg=true&branch=master)](https://ci.appveyor.com/project/tmfink/capstone-rs) | FreeBSD [![Cirrus CI Badge](https://api.cirrus-ci.com/github/capstone-rust/capstone-rs.svg)](https://cirrus-ci.com/github/capstone-rust/capstone-rs) [![codecov](https://codecov.io/gh/capstone-rust/capstone-rs/branch/master/graph/badge.svg)](https://codecov.io/gh/capstone-rust/capstone-rs) **[API Documentation](https://docs.rs/capstone/)** Bindings to the [capstone library][upstream] disassembly framework. # Requirements `capstone-rs` uses the [`capstone-sys`](capstone-sys) crate to provide the low-level bindings to the Capstone C library. See the [`capstone-sys`](capstone-sys) page for the requirements and supported platforms. * Minimum Rust Version: `1.36.0` # Example ```rust extern crate capstone; use capstone::prelude::*; const X86_CODE: &'static [u8] = b"\x55\x48\x8b\x05\xb8\x13\x00\x00\xe9\x14\x9e\x08\x00\x45\x31\xe4"; /// Print register names fn reg_names(cs: &Capstone, regs: T) -> String where T: Iterator, I: Into, { let names: Vec = regs.map(|x| cs.reg_name(x.into()).unwrap()).collect(); names.join(", ") } /// Print instruction group names fn group_names(cs: &Capstone, regs: T) -> String where T: Iterator, I: Into, { let names: Vec = regs.map(|x| cs.group_name(x.into()).unwrap()).collect(); names.join(", ") } fn example() -> CsResult<()> { let cs = Capstone::new() .x86() .mode(arch::x86::ArchMode::Mode64) .syntax(arch::x86::ArchSyntax::Att) .detail(true) .build()?; let insns = cs.disasm_all(X86_CODE, 0x1000)?; println!("Found {} instructions", insns.len()); for i in insns.iter() { println!(""); println!("{}", i); let detail: InsnDetail = cs.insn_detail(&i)?; let output: &[(&str, String)] = &[ ("read regs:", reg_names(&cs, detail.regs_read())), ("write regs:", reg_names(&cs, detail.regs_write())), ("insn groups:", group_names(&cs, detail.groups())), ]; for &(ref name, ref message) in output.iter() { println!(" {:12} {}", name, message); } } Ok(()) } fn main() { if let Err(err) = example() { println!("Error: {}", err); } } ``` Produces: ``` Found 4 instructions 0x1000: pushq %rbp read regs: rsp write regs: rsp insn groups: mode64 0x1001: movq 0x13b8(%rip), %rax read regs: write regs: insn groups: 0x1008: jmp 0x8ae21 read regs: write regs: insn groups: jump 0x100d: xorl %r12d, %r12d read regs: write regs: rflags insn groups: ``` To see more demos, see the [`examples/`](examples) directory. More complex demos welcome! # Features - `use_bindgen`: run `bindgen` to generate Rust bindings to Capstone C library instead of using pre-generated bindings (not recommended). # Reporting Issues Please open a [Github issue](https://github.com/capstone-rust/capstone-rs/issues) # Author - Library Author: Nguyen Anh Quynh - Binding Author(s): - m4b - Richo Healey - Travis Finkenauer You may find a [full list of contributors on Github](https://github.com/capstone-rust/capstone-rs/graphs/contributors). # License [MIT](LICENSE) [upstream]: https://www.capstone-engine.org/ capstone-0.7.0/THIRD_PARTY.txt010066400017500001750000000033301364465622700141650ustar0000000000000000capstone-rs contains code from Capstone, whose license follows: This is the software license for Capstone disassembly framework. Capstone has been designed & implemented by Nguyen Anh Quynh See http://www.capstone-engine.org for further information. Copyright (c) 2013, COSEINC. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the developer(s) nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. capstone-0.7.0/benches/my_benchmark.rs010066400017500001750000000024561364465622700162170ustar0000000000000000extern crate capstone; #[macro_use] extern crate criterion; use capstone::prelude::*; use capstone::{Arch, Endian, ExtraMode, Mode, NO_EXTRA_MODE}; use criterion::{black_box, Criterion}; const X86_CODE: &[u8] = include_bytes!("../test-inputs/x86_64.bin_ls.bin"); /// Disassemble code and print information fn arch_bench>( code: &[u8], arch: Arch, mode: Mode, extra_mode: T, endian: Option, detail: bool, ) { let mut cs = Capstone::new_raw(arch, mode, extra_mode, endian).expect("failed to make capstone"); cs.set_detail(detail).expect("failed to set detail"); let insns = cs.disasm_all(code, 0x1000).expect("failed to disassemble"); for i in insns.iter() { black_box(i); } } fn criterion_benchmark(c: &mut Criterion) { macro_rules! bench { ($name:expr; $( $args:expr ),+ ) => { c.bench_function($name, |b| { b.iter(|| arch_bench($( $args, )+ false)) }); c.bench_function(concat!($name, "_detail"), move |b| { b.iter(|| arch_bench($( $args, )+ true)) }); } } bench!("disasm_x86"; X86_CODE, Arch::X86, Mode::Mode64, NO_EXTRA_MODE, None); } criterion_group!(benches, criterion_benchmark); criterion_main!(benches); capstone-0.7.0/ci/test.sh010077500017500001750000000151011364465622700135030ustar0000000000000000#!/usr/bin/env bash # # Modified `ci/test.sh` from capstone-sys # Environment variables: # # FEATURES: (none by default) # NO_DEFAULT_FEATURES: enables --no-default-features # JOB: {*test,valgrind-test,bench,cov} # PROFILES: list of {debug,release} [debug release] # SHOULD_FAIL: (disabled by default; set to non-empty string to enable) # VALGRIND_TESTS: run tests under Valgrind set -euo pipefail if [ $(basename "$0") = "test.sh" ]; then cd "$(dirname "$0")/.." else echo "Script is sourced" fi RUST_BACKTRACE=1 SHOULD_FAIL=${SHOULD_FAIL:-} # Default to false VALGRIND_TESTS=${VALGRIND_TESTS:-} FEATURES="${FEATURES-}" # Default to no features NO_DEFAULT_FEATURES="${NO_DEFAULT_FEATURES:+--no-default-features}" PROJECT_NAME="$(grep ^name Cargo.toml | head -n1 | xargs -n1 | tail -n1)" TARGET="../target" TARGET_COV="${TARGET}/cov" SIMPLE_RUN_EXAMPLES="${SIMPLE_RUN_EXAMPLES:-demo parallel}" export USER="${USER:-$(id -u -n)}" PASS="PASS" FAIL="FAIL" if [ "$SHOULD_FAIL" ]; then EXPECTED_RESULT="$FAIL" else EXPECTED_RESULT="$PASS" fi echo "Running as USER=$USER" echo "Test should $EXPECTED_RESULT" Error() { echo "Error:" "$@" >&2 exit 1 } if ! [ "${OS_NAME:-}" ]; then case "$(uname)" in Linux) OS_NAME=linux ;; Darwin) OS_NAME=osx ;; FreeBSD) OS_NAME=freebsd ;; esac fi # Usage: SHOULD_FAIL [ARG1 [ARG2 [...]]] expect_exit_status() { local SHOULD_FAIL="$1" shift echo "Running command: $*" if "$@"; then ACTUAL_RESULT="$PASS" else ACTUAL_RESULT="$FAIL" fi if [ "$EXPECTED_RESULT" = "$ACTUAL_RESULT" ]; then echo "Correctly got expected result $EXPECTED_RESULT" else Error "Got result $ACTUAL_RESULT, expected result $EXPECTED_RESULT" fi } install_kcov() { if [ -f ./kcov-install/usr/local/bin/kcov ]; then echo "kcov already installed" return fi ( wget https://github.com/SimonKagstrom/kcov/archive/master.tar.gz tar xzf master.tar.gz cd kcov-master rm -rf build mkdir build cd build cmake .. make -j make install DESTDIR=../../kcov-install ) } install_valgrind() { case "${OS_NAME}" in linux) sudo apt-get install valgrind -y ;; osx) sudo brew install valgrind ;; *) Error "Valgrind not supported on" ;; esac } # target/ dir is cached, so we need to remove old coverage files cleanup_cov() { rm -rf ${TARGET_COV} } run_kcov() { KCOV="${KCOV:-kcov}" COVERALLS_ARG="${TRAVIS_JOB_ID:+--coveralls-id=$TRAVIS_JOB_ID}" # Build binaries cargo test --no-run -v for example in $SIMPLE_RUN_EXAMPLES; do cargo build --example "$example" done EXAMPLE_BINS=$(echo "$SIMPLE_RUN_EXAMPLES" | xargs -n1 | sed "s,^,${TARGET}/${PROFILE}/examples/,") mkdir -p "${TARGET_COV}" ( set -x for file in ${TARGET}/${PROFILE}/${PROJECT_NAME}-*[^\.d] ${EXAMPLE_BINS} ; do "$KCOV" \ $COVERALLS_ARG \ --include-pattern=capstone-rs \ --exclude-pattern=/.cargo,/usr/lib,/out/capstone.rs,capstone-sys \ --verify "${TARGET_COV}" "$file" done ) } cov() { echo "Running coverage" install_kcov cleanup_cov KCOV=./kcov-install/usr/local/bin/kcov run_kcov if [[ "${TRAVIS_JOB_ID:+Z}" = Z ]]; then bash <(curl -s https://codecov.io/bash) echo "Uploaded code coverage" else echo "Not uploading coverage since we are not in a CI job" fi } bench() { cargo bench } profile_args() { case "$PROFILE" in debug) ;; release) echo "--release" ;; *) Error "Unknown PROFILE $PROFILE" ;; esac } # Test rust file by making a temporary project # Must have a main() function defined test_rust_file() { ( tmp_dir="$(mktemp -d /tmp/rust.testdir.XXXXXXXXXX)" [ -d "$tmp_dir" ] || Error "Could not make temp dir" capstone_dir="$(pwd)" cd "$tmp_dir" cargo new --bin test_project -v cd test_project echo "capstone = { path = \"$capstone_dir\" }" >> Cargo.toml cat Cargo.toml cat > src/main.rs pwd # Do not include features arguments cargo_cmd_args=( $(profile_args) --verbose ) cargo check "${cargo_cmd_args[@]}" || exit 1 rm -rf "$tmp_dir" ) || exit 1 } run_tests() { TMPFILE="$(mktemp /tmp/capstone-rs.XXXXXXXXXX)" [ -f "$TMPFILE" ] || Error "Could not make temp file" for PROFILE in $PROFILES; do echo "Cargo tests without Valgrind" cargo_cmd_args=( $(profile_args) $NO_DEFAULT_FEATURES --features "$FEATURES" --verbose ) expect_exit_status "$SHOULD_FAIL" \ cargo test "${cargo_cmd_args[@]}" \ --color=always -- --color=always \ 2>&1 | tee "$TMPFILE" # Use 2>&1 above instead of '|&' because OS X uses Bash 3 for example in $SIMPLE_RUN_EXAMPLES; do cargo run "${cargo_cmd_args[@]}" --example "$example" done ( cd ../cstool cargo run $(profile_args) -- \ --arch x86 --mode mode64 --file ../capstone-rs/test-inputs/x86_64.bin_ls.bin | head -n20 ) cat README.md | \ sed -n '/^```rust/,/^```/p' | grep -vE '^```' | \ test_rust_file if [ ! "${VALGRIND_TESTS}" ]; then continue fi test_binary="$(cat "$TMPFILE" | grep -E 'Running[^`]*`' | sed 's/^.*Running[^`]*`\([^` ]*\).*$/\1/' | grep -vE '^rustc|rustdoc$' | grep -E '/capstone-[^ -]+$' )" [ -f "$test_binary" ] || Error "Unable to determine test binary (for Valgrind); found '$test_binary'" echo "Cargo tests WITH Valgrind" valgrind --error-exitcode=1 "$test_binary" done rm "$TMPFILE" } PROFILES="${PROFILES-debug}" for PROFILE in $PROFILES; do profile_args "$PROFILE" done # Note that `$PROFILE` is never in quotes so that it expands to nothing # (not even an empty string argument) when the variable is empty. This is # necessary so we don't pass an unexpected flag to cargo. if [ $(basename "$0") = "test.sh" ]; then JOB="${JOB:-test}" set -x case "$JOB" in test) run_tests ;; valgrind-test) VALGRIND_TESTS=: run_tests ;; cov) PROFILE=debug $JOB ;; bench) PROFILE=release $JOB ;; *) echo "Error! Unknown \$JOB: '$JOB'" exit 1 esac fi capstone-0.7.0/examples/darwin010077500017500001750000000203601364465622700146250ustar0000000000000000 H__PAGEZERO(__TEXT__text__TEXT < __stubs__TEXT\\__stub_helper__TEXTdd__cstring__TEXT~ ~__unwind_info__TEXTH__eh_frame__TEXT__DATA__nl_symbol_ptr__DATA__la_symbol_ptr__DATAH__LINKEDIT  "0   0 0h  8 P  /usr/lib/dylddN Oe9ˠ' $ *(  8 /usr/lib/libSystem.B.dylib&` )h UHHH=OE*EE*Eu 1EH]%LAS%hHaving done stuff, c is now %d  44]4 zRx t"Q@dyld_stub_binderQrr@_printf__mh_execute_header!main% $@ __mh_execute_header_main_printfdyld_stub_bindercapstone-0.7.0/examples/demo.rs010066400017500001750000000050151364465622700147050ustar0000000000000000extern crate capstone; use capstone::prelude::*; use capstone::InsnDetail; const MIPS_CODE: &'static [u8] = b"\x56\x34\x21\x34\xc2\x17\x01\x00"; const X86_CODE: &'static [u8] = b"\x55\x48\x8b\x05\xb8\x13\x00\x00\xe9\x14\x9e\x08\x00\x45\x31\xe4"; /// Print register names fn reg_names(cs: &Capstone, regs: T) -> String where T: Iterator, I: Into, { let names: Vec = regs.map(|x| cs.reg_name(x.into()).unwrap()).collect(); names.join(", ") } /// Print instruction group names fn group_names(cs: &Capstone, regs: T) -> String where T: Iterator, I: Into, { let names: Vec = regs.map(|x| cs.group_name(x.into()).unwrap()).collect(); names.join(", ") } /// Disassemble code and print information fn arch_example(cs: &mut Capstone, code: &[u8]) -> CsResult<()> { let insns = cs.disasm_all(code, 0x1000)?; println!("Found {} instructions", insns.len()); for i in insns.iter() { println!(); println!("{}", i); let detail: InsnDetail = cs.insn_detail(&i)?; let arch_detail: ArchDetail = detail.arch_detail(); let ops = arch_detail.operands(); let output: &[(&str, String)] = &[ ("insn id:", format!("{:?}", i.id().0)), ("bytes:", format!("{:?}", i.bytes())), ("read regs:", reg_names(&cs, detail.regs_read())), ("write regs:", reg_names(&cs, detail.regs_write())), ("insn groups:", group_names(&cs, detail.groups())), ]; for &(ref name, ref message) in output.iter() { println!("{:4}{:12} {}", "", name, message); } println!("{:4}operands: {}", "", ops.len()); for op in ops { println!("{:8}{:?}", "", op); } } Ok(()) } fn example() -> CsResult<()> { let cs_mips: Capstone = Capstone::new() .mips() .mode(arch::mips::ArchMode::Mips32R6) .detail(true) .build()?; let cs_x86 = Capstone::new() .x86() .mode(arch::x86::ArchMode::Mode64) .syntax(arch::x86::ArchSyntax::Att) .detail(true) .build()?; let mut examples = [("MIPS", cs_mips, MIPS_CODE), ("X86", cs_x86, X86_CODE)]; for &mut (arch, ref mut cs, code) in examples.iter_mut() { println!("\n*************************************"); println!("Architecture {}:", arch); arch_example(cs, &code)?; } Ok(()) } fn main() { if let Err(err) = example() { println!("Error: {}", err); } } capstone-0.7.0/examples/parallel.rs010066400017500001750000000025701364465622700155600ustar0000000000000000//! This example shows how to disassemble in parallel. You need a separate `Capstone` struct for //! each thread. //! //! We shard the input by using parallel iterators from the rayon crate. use capstone::prelude::*; use rayon::prelude::*; fn main() -> CsResult<()> { // Closure to create `Capstone` instance let create_cs = || -> CsResult { let cs = Capstone::new() .x86() .mode(arch::x86::ArchMode::Mode64) .detail(true) .build()?; Ok(cs) }; // Slice of code to disassemble let input_code: &[&[u8]] = &[ b"\x55\x48\x8b\x05\xb8\x13\x00\x00\xe9\x14\x9e\x08\x00\x45\x31\xe4", b"\x90\x41\xe8\x04\x03\x02\x01", b"\xff\xff\xff\xff\xff", ]; let results: Vec>> = input_code .par_iter() // iterate in parallel .map(|bytes| { // map input byte to output mnemonic let cs = create_cs()?; let insns = cs.disasm_all(bytes, 0x1000)?; let result: Option> = insns .iter() .map(|insn| -> Option { Some(insn.mnemonic()?.to_string()) }) .collect(); let result = result.ok_or(capstone::Error::CustomError("No mnemonic"))?; Ok(result) }) .collect(); println!("{:#?}", results); Ok(()) } capstone-0.7.0/examples/test.c010066400017500001750000000001751364465622700145400ustar0000000000000000#include int main(void) { int c = 42; c = c * c + 42; printf("Having done stuff, c is now %d\n", c); } capstone-0.7.0/scripts/gen_arch_enums.sh010077500017500001750000000014211364465622700165750ustar0000000000000000#!/bin/sh usage() { cat <&1)"; then echo "$output" echo echo "Failed after $iter tests" exit 1 fi iter=$(expr $iter + 1) done capstone-0.7.0/scripts/tag_release.sh010077500017500001750000000015551364465622700161030ustar0000000000000000#!/bin/sh # # Tag the HEAD based on version in Cargo.toml set -eu cd "$(dirname $0)/.." extract_toml_value() { grep "^$1" Cargo.toml | sed 's/^[^ =].*=.*"\([^"]\+\)"$/\1/' | head -n1 } PACKAGE_NAME="$(extract_toml_value name)" PACKAGE_VERSION="${PACKAGE_VERSION:-$(extract_toml_value version)}" DESCRIPTION="${PACKAGE_NAME} v${PACKAGE_VERSION}" TAG_NAME="${PACKAGE_NAME}-v${PACKAGE_VERSION}" GIT_COMMIT="${GIT_COMMIT:-$(git rev-parse HEAD)}" echo "Commit log:" git log -1 $GIT_COMMIT | cat echo echo -n "Create git tag: TAG_NAME=\"$TAG_NAME\" DESCRIPTION=\"$DESCRIPTION\" at $GIT_COMMIT? (y/N) " read -r answer case "$answer" in y|Y) ;; *) echo "Exiting" exit 1 ;; esac set -x git tag -s -m "${DESCRIPTION}" "${TAG_NAME}" "${GIT_COMMIT}" set +x echo echo "Don't forget to push tags upstream:" echo echo " git push origin --tags" capstone-0.7.0/src/arch/arm.rs010066400017500001750000000232731364465622700144340ustar0000000000000000//! Contains arm-specific types use core::convert::From; use core::{cmp, fmt, slice}; use capstone_sys::{ arm_op_mem, arm_op_type, cs_arm, cs_arm_op, arm_shifter, cs_arm_op__bindgen_ty_2}; use libc::c_uint; pub use crate::arch::arch_builder::arm::*; use crate::arch::DetailsArchInsn; use crate::instruction::{RegId, RegIdInt}; pub use capstone_sys::arm_insn_group as ArmInsnGroup; pub use capstone_sys::arm_insn as ArmInsn; pub use capstone_sys::arm_reg as ArmReg; pub use capstone_sys::arm_vectordata_type as ArmVectorData; pub use capstone_sys::arm_cpsmode_type as ArmCPSMode; pub use capstone_sys::arm_cpsflag_type as ArmCPSFlag; pub use capstone_sys::arm_cc as ArmCC; pub use capstone_sys::arm_mem_barrier as ArmMemBarrier; pub use capstone_sys::arm_setend_type as ArmSetendType; /// Contains ARM-specific details for an instruction pub struct ArmInsnDetail<'a>(pub(crate) &'a cs_arm); /// ARM shift amount #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub enum ArmShift { Invalid, /// Arithmetic shift right (immediate) Asr(u32), /// Logical shift lift (immediate) Lsl(u32), /// Logical shift right (immediate) Lsr(u32), /// Rotate right (immediate) Ror(u32), /// Rotate right with extend (immediate) Rrx(u32), /// Arithmetic shift right (register) AsrReg(RegId), /// Logical shift lift (register) LslReg(RegId), /// Logical shift right (register) LsrReg(RegId), /// Rotate right (register) RorReg(RegId), /// Rotate right with extend (register) RrxReg(RegId), } impl ArmShift { fn new(type_: arm_shifter, value: c_uint) -> ArmShift { use self::arm_shifter::*; use self::ArmShift::*; macro_rules! arm_shift_match { ( imm = [ $( $imm_r_enum:ident = $imm_c_enum:ident, )* ] reg = [ $( $reg_r_enum:ident = $reg_c_enum:ident, )* ] ) => { match type_ { ARM_SFT_INVALID => Invalid, $( $imm_c_enum => $imm_r_enum(value as u32) , )* $( $reg_c_enum => $reg_r_enum(RegId(value as RegIdInt)) , )* } } }; arm_shift_match!( imm = [ Asr = ARM_SFT_ASR, Lsl = ARM_SFT_LSL, Lsr = ARM_SFT_LSR, Ror = ARM_SFT_ROR, Rrx = ARM_SFT_RRX, ] reg = [ AsrReg = ARM_SFT_ASR_REG, LslReg = ARM_SFT_LSL_REG, LsrReg = ARM_SFT_LSR_REG, RorReg = ARM_SFT_ROR_REG, RrxReg = ARM_SFT_RRX_REG, ] ) } } impl ArmOperandType { fn new(op_type: arm_op_type, value: cs_arm_op__bindgen_ty_2) -> ArmOperandType { use self::arm_op_type::*; use self::ArmOperandType::*; match op_type { ARM_OP_INVALID => Invalid, ARM_OP_REG => Reg(RegId(unsafe { value.reg } as RegIdInt)), ARM_OP_IMM => Imm(unsafe { value.imm }), ARM_OP_MEM => Mem(ArmOpMem(unsafe { value.mem })), ARM_OP_FP => Fp(unsafe { value.fp }), ARM_OP_CIMM => Cimm(unsafe { value.imm }), ARM_OP_PIMM => Pimm(unsafe { value.imm }), ARM_OP_SETEND => Setend(unsafe { value.setend }), ARM_OP_SYSREG => SysReg(RegId(unsafe { value.reg } as RegIdInt)), } } } /// ARM operand #[derive(Clone, Debug, PartialEq)] pub struct ArmOperand { /// Vector Index for some vector operands pub vector_index: Option, /// Whether operand is subtracted pub subtracted: bool, pub shift: ArmShift, /// Operand type pub op_type: ArmOperandType, } /// ARM operand #[derive(Clone, Debug, PartialEq)] pub enum ArmOperandType { /// Register Reg(RegId), /// Immediate Imm(i32), /// Memory Mem(ArmOpMem), /// Floating point Fp(f64), /// C-IMM Cimm(i32), /// P-IMM Pimm(i32), /// SETEND instruction endianness Setend(ArmSetendType), /// Sysreg SysReg(RegId), /// Invalid Invalid, } /// ARM memory operand #[derive(Debug, Copy, Clone)] pub struct ArmOpMem(pub(crate) arm_op_mem); impl<'a> ArmInsnDetail<'a> { /// Whether the instruction is a user mode pub fn usermode(&self) -> bool { self.0.usermode } /// Vector size pub fn vector_size(&self) -> i32 { self.0.vector_size as i32 } /// Type of vector data pub fn vector_data(&self) -> ArmVectorData { self.0.vector_data } /// CPS mode for CPS instruction pub fn cps_mode(&self) -> ArmCPSMode { self.0.cps_mode } /// CPS flag for CPS instruction pub fn cps_flag(&self) -> ArmCPSFlag { self.0.cps_flag } /// Condition codes pub fn cc(&self) -> ArmCC { self.0.cc } /// Whether this insn updates flags pub fn update_flags(&self) -> bool { self.0.update_flags } /// Whether writeback is required pub fn writeback(&self) -> bool { self.0.writeback } /// Memory barrier pub fn mem_barrier(&self) -> ArmMemBarrier { self.0.mem_barrier } } impl_PartialEq_repr_fields!(ArmInsnDetail<'a> [ 'a ]; usermode, vector_size, vector_data, cps_mode, cps_flag, cc, update_flags, writeback, mem_barrier, operands ); impl ArmOpMem { /// Base register pub fn base(&self) -> RegId { RegId(self.0.base as RegIdInt) } /// Index value pub fn index(&self) -> RegId { RegId(self.0.index as RegIdInt) } /// Scale for index register (can be 1, or -1) pub fn scale(&self) -> i32 { self.0.scale as i32 } /// Disp value pub fn disp(&self) -> i32 { self.0.disp as i32 } } impl_PartialEq_repr_fields!(ArmOpMem; base, index, scale, disp ); impl cmp::Eq for ArmOpMem {} impl Default for ArmOperand { fn default() -> Self { ArmOperand { vector_index: None, subtracted: false, shift: ArmShift::Invalid, op_type: ArmOperandType::Invalid } } } impl<'a> From<&'a cs_arm_op> for ArmOperand { fn from(op: &cs_arm_op) -> ArmOperand { let shift = ArmShift::new(op.shift.type_, op.shift.value); let op_type = ArmOperandType::new(op.type_, op.__bindgen_anon_1); let vector_index = if op.vector_index >= 0 { Some(op.vector_index as u32) } else { None }; ArmOperand { vector_index, shift, op_type, subtracted: op.subtracted, } } } def_arch_details_struct!( InsnDetail = ArmInsnDetail; Operand = ArmOperand; OperandIterator = ArmOperandIterator; OperandIteratorLife = ArmOperandIterator<'a>; [ pub struct ArmOperandIterator<'a>(slice::Iter<'a, cs_arm_op>); ] cs_arch_op = cs_arm_op; cs_arch = cs_arm; ); #[cfg(test)] mod test { use super::*; use capstone_sys::*; #[test] fn test_armshift() { use super::arm_shifter::*; use super::ArmShift::*; use libc::c_uint; fn t(shift_type_value: (arm_shifter, c_uint), arm_shift: ArmShift) { let (shift_type, value) = shift_type_value; assert_eq!(arm_shift, ArmShift::new(shift_type, value)); } t((ARM_SFT_INVALID, 0), Invalid); t((ARM_SFT_ASR, 0), Asr(0)); t((ARM_SFT_ASR_REG, 42), AsrReg(RegId(42))); t((ARM_SFT_RRX_REG, 42), RrxReg(RegId(42))); } #[test] fn test_arm_op_type() { use super::arm_op_type::*; use super::ArmOperandType::*; fn t( op_type_value: (arm_op_type, cs_arm_op__bindgen_ty_2), expected_op_type: ArmOperandType, ) { let (op_type, op_value) = op_type_value; let op_type = ArmOperandType::new(op_type, op_value); assert_eq!(expected_op_type, op_type); } t( (ARM_OP_INVALID, cs_arm_op__bindgen_ty_2 { reg: 0 }), Invalid, ); t( (ARM_OP_REG, cs_arm_op__bindgen_ty_2 { reg: 0 }), Reg(RegId(0)), ); } #[test] fn test_arm_insn_detail_eq() { let a1 = cs_arm { usermode: false, vector_size: 0, vector_data: arm_vectordata_type::ARM_VECTORDATA_INVALID, cps_mode: arm_cpsmode_type::ARM_CPSMODE_INVALID, cps_flag: arm_cpsflag_type::ARM_CPSFLAG_INVALID, cc: arm_cc::ARM_CC_INVALID, update_flags: false, writeback: false, mem_barrier: arm_mem_barrier::ARM_MB_INVALID, op_count: 0, operands: [ cs_arm_op { vector_index: 0, shift: cs_arm_op__bindgen_ty_1 { type_: arm_shifter::ARM_SFT_INVALID, value: 0 }, type_: arm_op_type::ARM_OP_INVALID, __bindgen_anon_1: cs_arm_op__bindgen_ty_2 { imm: 0 }, subtracted: false, access: 0, neon_lane: 0, } ; 36] }; let a2 = cs_arm { usermode: true, ..a1 }; let a3 = cs_arm { op_count: 20, ..a1 }; let a4 = cs_arm { op_count: 19, ..a1 }; let a4_clone = a4.clone(); assert_eq!(ArmInsnDetail(&a1), ArmInsnDetail(&a1)); assert_ne!(ArmInsnDetail(&a1), ArmInsnDetail(&a2)); assert_ne!(ArmInsnDetail(&a1), ArmInsnDetail(&a3)); assert_ne!(ArmInsnDetail(&a3), ArmInsnDetail(&a4)); assert_eq!(ArmInsnDetail(&a4), ArmInsnDetail(&a4_clone)); } } capstone-0.7.0/src/arch/arm64.rs010066400017500001750000000222211364465622700145760ustar0000000000000000//! Contains arm64-specific types use libc::c_uint; pub use crate::arch::arch_builder::arm64::*; use crate::arch::DetailsArchInsn; use capstone_sys::{arm64_op_mem, arm64_op_type, cs_arm64, cs_arm64_op}; use crate::instruction::{RegId, RegIdInt}; use core::convert::From; use core::{cmp, fmt, mem, slice}; // Re-exports pub use capstone_sys::arm64_insn_group as Arm64InsnGroup; pub use capstone_sys::arm64_insn as Arm64Insn; pub use capstone_sys::arm64_reg as Arm64Reg; pub use capstone_sys::arm64_cc as Arm64CC; pub use capstone_sys::arm64_extender as Arm64Extender; pub use capstone_sys::arm64_vas as Arm64Vas; pub use capstone_sys::arm64_vess as Arm64Vess; pub use capstone_sys::arm64_pstate as Arm64Pstate; pub use capstone_sys::arm64_prefetch_op as ArmPrefetchOp; pub use capstone_sys::arm64_barrier_op as ArmBarrierOp; pub use capstone_sys::arm64_msr_reg as Arm64MsrReg; pub use capstone_sys::arm64_sysreg as Arm64Sysreg; pub use capstone_sys::arm64_ic_op as Arm64IcOp; pub use capstone_sys::arm64_dc_op as Arm64DcOp; pub use capstone_sys::arm64_at_op as Arm64AtOp; pub use capstone_sys::arm64_tlbi_op as Arm64TlbiOp; pub use capstone_sys::arm64_barrier_op as Arm64BarrierOp; use capstone_sys::cs_arm64_op__bindgen_ty_2; use capstone_sys::arm64_shifter; /// Contains ARM64-specific details for an instruction pub struct Arm64InsnDetail<'a>(pub(crate) &'a cs_arm64); /// ARM64 shift amount #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub enum Arm64Shift { Invalid, /// Logical shift lift Lsl(u32), /// Masking shift lift Msl(u32), /// Logical shift right Lsr(u32), /// Arithmetic shift right Asr(u32), /// Rotate right Ror(u32), } impl Arm64OperandType { fn new(op_type: arm64_op_type, value: cs_arm64_op__bindgen_ty_2) -> Arm64OperandType { use self::arm64_op_type::*; use self::Arm64OperandType::*; match op_type { ARM64_OP_INVALID => Invalid, ARM64_OP_REG => Reg(RegId(unsafe { value.reg } as RegIdInt)), ARM64_OP_IMM => Imm(unsafe { value.imm }), ARM64_OP_MEM => Mem(Arm64OpMem(unsafe { value.mem })), ARM64_OP_FP => Fp(unsafe { value.fp }), ARM64_OP_CIMM => Cimm(unsafe { value.imm }), ARM64_OP_REG_MRS => RegMrs(unsafe { mem::transmute(value.reg) }), ARM64_OP_REG_MSR => RegMsr(unsafe { mem::transmute(value.reg) }), ARM64_OP_PSTATE => Pstate(unsafe { value.pstate }), ARM64_OP_SYS => Sys(unsafe { value.sys }), ARM64_OP_PREFETCH => Prefetch(unsafe { value.prefetch }), ARM64_OP_BARRIER => Barrier(unsafe { value.barrier }), } } } /// ARM64 operand #[derive(Clone, Debug, PartialEq)] pub struct Arm64Operand { /// Vector Index for some vector operands pub vector_index: Option, /// Vector arrangement specifier (for FloatingPoint/Advanced SIMD insn) pub vas: Arm64Vas, /// Vector element size specifier pub vess: Arm64Vess, /// Shifter of this operand pub shift: Arm64Shift, /// Extender type of this operand pub ext: Arm64Extender, /// Operand type pub op_type: Arm64OperandType, } /// ARM64 operand #[derive(Clone, Debug, PartialEq)] pub enum Arm64OperandType { /// Register Reg(RegId), /// Immediate Imm(i64), /// Memory Mem(Arm64OpMem), /// Floating point Fp(f64), /// C-IMM Cimm(i64), /// System registers RegMrs(Arm64Sysreg), /// MSR registers RegMsr(Arm64MsrReg), /// System PState Field (MSR instruction) Pstate(Arm64Pstate), // XXX todo(tmfink) /// IC/DC/AT/TLBI operation (see Arm64IcOp, Arm64DcOp, Arm64AtOp, Arm64TlbiOp) Sys(u32), /// PRFM operation Prefetch(ArmPrefetchOp), /// Memory barrier operation (ISB/DMB/DSB instructions) Barrier(Arm64BarrierOp), /// Invalid Invalid, } /// ARM64 memory operand #[derive(Debug, Copy, Clone)] pub struct Arm64OpMem(pub(crate) arm64_op_mem); impl<'a> Arm64InsnDetail<'a> { /// Condition codes pub fn cc(&self) -> Arm64CC { self.0.cc } /// Whether this insn updates flags pub fn update_flags(&self) -> bool { self.0.update_flags } /// Whether writeback is required pub fn writeback(&self) -> bool { self.0.writeback } } impl_PartialEq_repr_fields!(Arm64InsnDetail<'a> [ 'a ]; cc, update_flags, writeback, operands ); impl Arm64OpMem { /// Base register pub fn base(&self) -> RegId { RegId(self.0.base as RegIdInt) } /// Index register pub fn index(&self) -> RegId { RegId(self.0.index as RegIdInt) } /// Disp value pub fn disp(&self) -> i32 { self.0.disp as i32 } } impl_PartialEq_repr_fields!(Arm64OpMem; base, index, disp ); impl cmp::Eq for Arm64OpMem {} impl Default for Arm64Operand { fn default() -> Self { Arm64Operand { vector_index: None, vas: Arm64Vas::ARM64_VAS_INVALID, vess: Arm64Vess::ARM64_VESS_INVALID, shift: Arm64Shift::Invalid, ext: Arm64Extender::ARM64_EXT_INVALID, op_type: Arm64OperandType::Invalid } } } impl Arm64Shift { fn new(type_: arm64_shifter, value: c_uint) -> Arm64Shift { use self::arm64_shifter::*; use self::Arm64Shift::*; macro_rules! arm64_shift_match { ( $( $imm_r_enum:ident = $imm_c_enum:ident, )* ) => { match type_ { ARM64_SFT_INVALID => Invalid, $( $imm_c_enum => $imm_r_enum(value as u32) , )* } } }; arm64_shift_match!( Lsl = ARM64_SFT_LSL, Msl = ARM64_SFT_MSL, Lsr = ARM64_SFT_LSR, Asr = ARM64_SFT_ASR, Ror = ARM64_SFT_ROR, ) } } impl<'a> From<&'a cs_arm64_op> for Arm64Operand { fn from(op: &cs_arm64_op) -> Arm64Operand { let shift = Arm64Shift::new(op.shift.type_, op.shift.value); let op_type = Arm64OperandType::new(op.type_, op.__bindgen_anon_1); let vector_index = if op.vector_index >= 0 { Some(op.vector_index as u32) } else { None }; Arm64Operand { vector_index, vas: op.vas, vess: op.vess, shift, ext: op.ext, op_type, } } } def_arch_details_struct!( InsnDetail = Arm64InsnDetail; Operand = Arm64Operand; OperandIterator = Arm64OperandIterator; OperandIteratorLife = Arm64OperandIterator<'a>; [ pub struct Arm64OperandIterator<'a>(slice::Iter<'a, cs_arm64_op>); ] cs_arch_op = cs_arm64_op; cs_arch = cs_arm64; ); #[cfg(test)] mod test { use super::*; #[test] fn test_arm64shift() { use super::arm64_shifter::*; use super::Arm64Shift::*; use libc::c_uint; fn t(shift_type_value: (arm64_shifter, c_uint), arm64_shift: Arm64Shift) { let (shift_type, value) = shift_type_value; assert_eq!(arm64_shift, Arm64Shift::new(shift_type, value)); } t((ARM64_SFT_INVALID, 0), Invalid); t((ARM64_SFT_ASR, 0), Asr(0)); } #[test] fn test_arm64_op_type() { use super::arm64_op_type::*; use super::Arm64OperandType::*; use super::Arm64Sysreg::*; use capstone_sys::*; use capstone_sys::arm64_prefetch_op::*; use capstone_sys::arm64_pstate::*; fn t( op_type_value: (arm64_op_type, cs_arm64_op__bindgen_ty_2), expected_op_type: Arm64OperandType, ) { let (op_type, op_value) = op_type_value; let op_type = Arm64OperandType::new(op_type, op_value); assert_eq!(expected_op_type, op_type); } t( (ARM64_OP_INVALID, cs_arm64_op__bindgen_ty_2 { reg: 0 }), Invalid, ); t( (ARM64_OP_REG, cs_arm64_op__bindgen_ty_2 { reg: 0 }), Reg(RegId(0)), ); t( (ARM64_OP_IMM, cs_arm64_op__bindgen_ty_2 { imm: 42 }), Imm(42), ); t( (ARM64_OP_REG_MRS, cs_arm64_op__bindgen_ty_2 { reg: ARM64_SYSREG_MDRAR_EL1 as u32 }), RegMrs(ARM64_SYSREG_MDRAR_EL1), ); t( (ARM64_OP_PSTATE, cs_arm64_op__bindgen_ty_2 { pstate: ARM64_PSTATE_SPSEL }), Pstate(Arm64Pstate::ARM64_PSTATE_SPSEL), ); t( (ARM64_OP_FP, cs_arm64_op__bindgen_ty_2 { fp: 0.0 }), Fp(0.0), ); t( (ARM64_OP_CIMM, cs_arm64_op__bindgen_ty_2 { imm: 42 }), Cimm(42), ); t( (ARM64_OP_REG_MSR, cs_arm64_op__bindgen_ty_2 { reg: arm64_msr_reg::ARM64_SYSREG_ICC_EOIR1_EL1 as u32 }), RegMsr(arm64_msr_reg::ARM64_SYSREG_ICC_EOIR1_EL1), ); t( (ARM64_OP_SYS, cs_arm64_op__bindgen_ty_2 { sys: 42 }), Sys(42), ); t( (ARM64_OP_PREFETCH, cs_arm64_op__bindgen_ty_2 { prefetch: ARM64_PRFM_PLDL2KEEP }), Prefetch(ARM64_PRFM_PLDL2KEEP), ); } } capstone-0.7.0/src/arch/evm.rs010066400017500001750000000051501364465622700144360ustar0000000000000000//! Contains EVM-specific types use core::fmt; use capstone_sys::cs_evm; // XXX todo(tmfink): create rusty versions pub use capstone_sys::evm_insn_group as EvmInsnGroup; pub use capstone_sys::evm_insn as EvmInsn; pub use crate::arch::arch_builder::evm::*; use crate::arch::DetailsArchInsn; /// Contains EVM-specific details for an instruction pub struct EvmInsnDetail<'a>(pub(crate) &'a cs_evm); impl<'a> EvmInsnDetail<'a> { /// Number of items popped from the stack pub fn popped_items(&self) -> u8 { self.0.pop } /// Number of items pushed into the stack pub fn pushed_items(&self) -> u8 { self.0.push } /// Gas fee for the instruction pub fn fee(&self) -> u32 { self.0.fee as u32 } } impl_PartialEq_repr_fields!(EvmInsnDetail<'a> [ 'a ]; popped_items, pushed_items, fee ); /// EVM has no operands, so this is a zero-size type. #[derive(Clone, Debug, Eq, PartialEq)] pub struct EvmOperand(()); impl Default for EvmOperand { fn default() -> Self { EvmOperand(()) } } // Do not use def_arch_details_struct! since EVM does not have operands /// Iterates over instruction operands #[derive(Clone)] pub struct EvmOperandIterator(()); impl EvmOperandIterator { fn new() -> EvmOperandIterator { EvmOperandIterator(()) } } impl Iterator for EvmOperandIterator { type Item = EvmOperand; fn next(&mut self) -> Option { None } } impl ExactSizeIterator for EvmOperandIterator { fn len(&self) -> usize { 0 } } impl PartialEq for EvmOperandIterator { fn eq(&self, _other: &EvmOperandIterator) -> bool { false } } impl fmt::Debug for EvmOperandIterator { fn fmt(&self, fmt: &mut fmt::Formatter) -> ::core::fmt::Result { fmt.debug_struct("EvmOperandIterator").finish() } } impl<'a> fmt::Debug for EvmInsnDetail<'a> { fn fmt(&self, fmt: &mut fmt::Formatter) -> ::core::fmt::Result { fmt.debug_struct("EvmInsnDetail") .field("cs_evm", &(self.0 as *const cs_evm)) .finish() } } impl<'a> DetailsArchInsn for EvmInsnDetail<'a> { type OperandIterator = EvmOperandIterator; type Operand = EvmOperand; fn operands(&self) -> EvmOperandIterator { EvmOperandIterator::new() } } #[cfg(test)] mod test { use super::*; #[test] fn test_evm_detail() { let cs_evm = cs_evm { pop: 1, push: 2, fee: 42, }; let d = EvmInsnDetail(&cs_evm); assert_eq!(d.popped_items(), 1); assert_eq!(d.pushed_items(), 2); assert_eq!(d.fee(), 42); } } capstone-0.7.0/src/arch/m680x.rs010066400017500001750000000246171364465622700145420ustar0000000000000000//! Contains m680x-specific types use core::convert::From; use core::{fmt, slice}; use capstone_sys::{ cs_m680x, cs_m680x_op, m680x_op_ext, m680x_op_idx, m680x_op_rel, m680x_op_type, }; // XXX todo(tmfink): create rusty versions pub use capstone_sys::m680x_insn as M680xInsn; pub use capstone_sys::m680x_reg as M680xReg; pub use crate::arch::arch_builder::m680x::*; use crate::arch::DetailsArchInsn; use crate::instruction::{RegId, RegIdInt}; /// Contains M680X-specific details for an instruction pub struct M680xInsnDetail<'a>(pub(crate) &'a cs_m680x); impl_PartialEq_repr_fields!(M680xInsnDetail<'a> [ 'a ]; operands, flags ); // M680X instruction flags const M680X_FIRST_OP_IN_MNEM: u8 = 1; const M680X_SECOND_OP_IN_MNEM: u8 = 2; define_impl_bitmask!( impl M680xInsnDetail<'a>; flags: u8 = { |self_: &M680xInsnDetail| self_.0.flags } test_mod = test_M680xInsnDetail; /// The first (register) operand is part of the instruction mnemonic => is_first_op_in_mnem = M680X_FIRST_OP_IN_MNEM; /// The second (register) operand is part of the instruction mnemonic => is_second_op_in_mnem = M680X_SECOND_OP_IN_MNEM; ); /// Instruction's operand referring to indexed addressing #[derive(Clone, Debug)] pub struct M680xOpIdx(pub(crate) m680x_op_idx); impl_PartialEq_repr_fields!(M680xOpIdx [ ]; base_reg, offset_reg, offset, offset_addr, offset_bits, inc_dec, flags ); impl Eq for M680xOpIdx {} macro_rules! define_m680x_register_option_getter { ( $( #[$enum_attr:meta] )* => $field:ident ) => { $( #[$enum_attr] )* pub fn $field(&self) -> Option { if (self.0).$field == M680xReg::M680X_REG_INVALID { None } else { Some(RegId((self.0).$field as RegIdInt)) } } } } impl M680xOpIdx { fn new(op_idx: &m680x_op_idx) -> Self { M680xOpIdx(*op_idx) } define_m680x_register_option_getter!( /// Base register => base_reg ); define_m680x_register_option_getter!( /// Offset register => offset_reg ); /// 5-,8- or 16-bit offset pub fn offset(&self) -> i16 { self.0.offset } /// Offset address /// /// if base_reg == M680X_REG_PC, then calculated as offset + PC pub fn offset_addr(&self) -> u16 { self.0.offset_addr } /// Offset bits pub fn offset_bits(&self) -> u8 { self.0.offset_bits } /// Increment or decrement value /// /// - `0`: no inc-/decrement /// - `1 .. 8`: increment by `1 .. 8` /// - `-1 .. -8`: decrement by `1 .. 8` /// /// if flag `M680X_IDX_POST_INC_DEC` set it is post /// inc-/decrement, otherwise pre inc-/decrement. pub fn inc_dec(&self) -> i8 { self.0.inc_dec } } // Comes from M680X_IDX_* #defines const M680X_IDX_INDIRECT: u8 = 1; const M680X_IDX_NO_COMMA: u8 = 2; const M680X_IDX_POST_INC_DEC: u8 = 4; define_impl_bitmask!( impl M680xOpIdx<>; flags: u8 = { |self_: &M680xOpIdx| self_.0.flags } test_mod = test_M680xOpIdx; /// Is index indirect? => is_indirect = M680X_IDX_INDIRECT; /// Is there no comma? => is_no_comma = M680X_IDX_NO_COMMA; /// Is index indirect? => is_post_inc_dec = M680X_IDX_POST_INC_DEC; ); /// M680X operand #[derive(Clone, Debug, Eq, PartialEq)] pub enum M680xOperandType { /// Register Reg(RegId), /// Immediate Imm(i32), /// Indexed addressing operand Indexed(M680xOpIdx), /// Extended addressing operand Extended { /// Absolute address address: u16, /// Whether extended indirect addressing indirect: bool, }, /// Direct addressing operand Direct { /// Direct address (lower 8-bit) direct_addr: u8, }, /// Relative addressing operand Relative { /// Absolute address address: u16, /// Offset/displacement value offset: i16, }, /// Constant operand (displayed as number only) /// /// Used e.g. for a bit index or page number. Constant(u8), /// Invalid Invalid, } impl Default for M680xOperandType { fn default() -> Self { M680xOperandType::Invalid } } impl<'a> From<&'a cs_m680x_op> for M680xOperand { fn from(op: &cs_m680x_op) -> M680xOperand { let op_type = match op.type_ { m680x_op_type::M680X_OP_REGISTER => { M680xOperandType::Reg(RegId(unsafe { op.__bindgen_anon_1.reg } as RegIdInt)) } m680x_op_type::M680X_OP_IMMEDIATE => { M680xOperandType::Imm(unsafe { op.__bindgen_anon_1.imm }) } m680x_op_type::M680X_OP_INDEXED => { M680xOperandType::Indexed(M680xOpIdx::new(unsafe { &op.__bindgen_anon_1.idx })) } m680x_op_type::M680X_OP_EXTENDED => { let op_ext: m680x_op_ext = unsafe { op.__bindgen_anon_1.ext }; M680xOperandType::Extended { address: op_ext.address, indirect: op_ext.indirect, } } m680x_op_type::M680X_OP_DIRECT => M680xOperandType::Direct { direct_addr: unsafe { op.__bindgen_anon_1.direct_addr }, }, m680x_op_type::M680X_OP_RELATIVE => { let op_rel: m680x_op_rel = unsafe { op.__bindgen_anon_1.rel }; M680xOperandType::Relative { address: op_rel.address, offset: op_rel.offset, } } m680x_op_type::M680X_OP_CONSTANT => { M680xOperandType::Constant(unsafe { op.__bindgen_anon_1.const_val }) } m680x_op_type::M680X_OP_INVALID => M680xOperandType::Invalid, }; M680xOperand { op_type, size: op.size, } } } /// M680X operand #[derive(Clone, Debug, Default, Eq, PartialEq)] pub struct M680xOperand { /// Operand type pub op_type: M680xOperandType, /// Size of this operand in bytes pub size: u8, } def_arch_details_struct!( InsnDetail = M680xInsnDetail; Operand = M680xOperand; OperandIterator = M680xOperandIterator; OperandIteratorLife = M680xOperandIterator<'a>; [ pub struct M680xOperandIterator<'a>(slice::Iter<'a, cs_m680x_op>); ] cs_arch_op = cs_m680x_op; cs_arch = cs_m680x; ); #[cfg(test)] mod test { use super::*; use capstone_sys::*; #[test] fn m680x_op_type() { let op_base = cs_m680x_op { type_: m680x_op_type::M680X_OP_INVALID, __bindgen_anon_1: cs_m680x_op__bindgen_ty_1 { reg: 0 }, size: 1, access: 0, }; assert_eq!( M680xOperand::from(&op_base).op_type, M680xOperandType::Invalid ); assert_eq!( M680xOperand::from(&cs_m680x_op { type_: m680x_op_type::M680X_OP_REGISTER, __bindgen_anon_1: cs_m680x_op__bindgen_ty_1 { reg: M680xReg::M680X_REG_E }, ..op_base }) .op_type, M680xOperandType::Reg(RegId(M680xReg::M680X_REG_E as RegIdInt)) ); assert_eq!( M680xOperand::from(&cs_m680x_op { type_: m680x_op_type::M680X_OP_CONSTANT, __bindgen_anon_1: cs_m680x_op__bindgen_ty_1 { const_val: 42 }, ..op_base }) .op_type, M680xOperandType::Constant(42) ); assert_eq!( M680xOperand::from(&cs_m680x_op { type_: m680x_op_type::M680X_OP_IMMEDIATE, __bindgen_anon_1: cs_m680x_op__bindgen_ty_1 { imm: 1037 }, ..op_base }) .op_type, M680xOperandType::Imm(1037) ); assert_eq!( M680xOperand::from(&cs_m680x_op { type_: m680x_op_type::M680X_OP_DIRECT, __bindgen_anon_1: cs_m680x_op__bindgen_ty_1 { direct_addr: 67 }, ..op_base }) .op_type, M680xOperandType::Direct { direct_addr: 67 } ); assert_eq!( M680xOperand::from(&cs_m680x_op { type_: m680x_op_type::M680X_OP_EXTENDED, __bindgen_anon_1: cs_m680x_op__bindgen_ty_1 { ext: m680x_op_ext { address: 45876, indirect: true, } }, ..op_base }) .op_type, M680xOperandType::Extended { address: 45876, indirect: true } ); let base_reg = m680x_reg::M680X_REG_A; let offset_reg = m680x_reg::M680X_REG_B; let offset = 5; let offset_addr = 0x1337; let offset_bits = 4; let inc_dec = -3; let cs_op_idx = m680x_op_idx { base_reg, offset_reg, offset, offset_addr, offset_bits, inc_dec, flags: 7, }; assert_eq!( M680xOperand::from(&cs_m680x_op { type_: m680x_op_type::M680X_OP_INDEXED, __bindgen_anon_1: cs_m680x_op__bindgen_ty_1 { idx: cs_op_idx }, ..op_base }) .op_type, M680xOperandType::Indexed(M680xOpIdx(cs_op_idx)) ); } #[test] fn op_idx() { let base_reg = m680x_reg::M680X_REG_A; let offset_reg = m680x_reg::M680X_REG_B; let offset = 5; let offset_addr = 0x1337; let offset_bits = 4; let inc_dec = -3; let mut idx = M680xOpIdx(m680x_op_idx { base_reg, offset_reg, offset, offset_addr, offset_bits, inc_dec, flags: 7, }); assert_eq!(idx.base_reg(), Some(RegId(base_reg as RegIdInt))); assert_eq!(idx.offset_reg(), Some(RegId(offset_reg as RegIdInt))); assert_eq!(idx.offset(), offset); assert_eq!(idx.offset_addr(), offset_addr); assert_eq!(idx.offset_bits(), offset_bits); assert_eq!(idx.inc_dec(), inc_dec); assert!(idx.is_indirect()); assert!(idx.is_no_comma()); assert!(idx.is_post_inc_dec()); idx.0.flags = 5; assert!(idx.is_indirect()); assert!(!idx.is_no_comma()); assert!(idx.is_post_inc_dec()); } } capstone-0.7.0/src/arch/m68k.rs010066400017500001750000000447271364465622700144510ustar0000000000000000//! Contains m68k-specific types use core::convert::From; use core::{cmp, fmt, slice}; use capstone_sys::{ cs_m68k, cs_m68k_op, cs_m68k_op__bindgen_ty_1, m68k_address_mode, m68k_cpu_size, m68k_fpu_size, m68k_op_br_disp, m68k_op_mem, m68k_op_size, m68k_op_type, m68k_reg, m68k_size_type, }; // XXX todo(tmfink): create rusty versions pub use capstone_sys::m68k_address_mode as M68kAddressMode; pub use capstone_sys::m68k_insn as M68kInsn; pub use capstone_sys::m68k_reg as M68kReg; pub use crate::arch::arch_builder::m68k::*; use crate::arch::DetailsArchInsn; use crate::Error; use crate::instruction::{RegId, RegIdInt}; use crate::prelude::*; /// Contains M68K-specific details for an instruction pub struct M68kInsnDetail<'a>(pub(crate) &'a cs_m68k); impl<'a> M68kInsnDetail<'a> { /// size of data operand works on in bytes (.b, .w, .l, etc) pub fn op_size(&self) -> Option { M68kOpSize::new(&self.0.op_size) } } define_cs_enum_wrapper_reverse!( [ /// Operation size of the CPU instructions => M68kCpuSize = m68k_cpu_size, ] /// Unsized or unspecified => None = M68K_CPU_SIZE_NONE; /// 1 byte in size => Byte = M68K_CPU_SIZE_BYTE; /// 2 bytes in size => Word = M68K_CPU_SIZE_WORD; /// 4 bytes in size => Long = M68K_CPU_SIZE_LONG; ); define_cs_enum_wrapper_reverse!( [ /// Operation size of the FPU instructions (notice that FPU instruction can also use CPU /// sizes if needed) => M68kFpuSize = m68k_fpu_size, ] /// Unsized or unspecified => None = M68K_FPU_SIZE_NONE; /// 1 byte in size => Single = M68K_FPU_SIZE_SINGLE; /// 2 bytes in size => Double = M68K_FPU_SIZE_DOUBLE; /// 4 bytes in size => Extended = M68K_FPU_SIZE_EXTENDED; ); /// Operation size of the current instruction (NOT the actually size of instruction) #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub enum M68kOpSize { Cpu(M68kCpuSize), Fpu(M68kFpuSize), } /// Data when operand is a branch displacement #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub struct M68kOpBranchDisplacement { /// Displacement value pub disp: i32, /// Size from M68kOpBranchDisplacement pub disp_size: u8, } impl From for M68kOpBranchDisplacement { fn from(other: m68k_op_br_disp) -> Self { M68kOpBranchDisplacement { disp: other.disp, disp_size: other.disp_size, } } } impl M68kOpSize { fn new(op: &m68k_op_size) -> Option { match op.type_ { m68k_size_type::M68K_SIZE_TYPE_INVALID => None, m68k_size_type::M68K_SIZE_TYPE_CPU => Some(M68kOpSize::Cpu( unsafe { op.__bindgen_anon_1.cpu_size }.into(), )), m68k_size_type::M68K_SIZE_TYPE_FPU => Some(M68kOpSize::Fpu( unsafe { op.__bindgen_anon_1.fpu_size }.into(), )), } } } impl_PartialEq_repr_fields!(M68kInsnDetail<'a> [ 'a ]; op_size, operands ); impl Default for M68kOperand { fn default() -> Self { M68kOperand::Invalid } } /// Contains bitfield used with M68kOperand::RegBits /// /// Contains register bits for movem etc. (always in d0-d7, a0-a7, fp0-fp7 order) #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct M68kRegisterBits { /// Internal bitfield /// /// INVARIANT: must only have bits set up to fp7 bits: u32, } /// Allowed bits are 1; disallowed bits are 0 const M68K_REGISTER_BITS_ALLOWED_MASK: u32 = (1_u32 << ((m68k_reg::M68K_REG_FP7 as u8 - m68k_reg::M68K_REG_D0 as u8) + 1_u8)) - 1; impl M68kRegisterBits { /// Create from a bitfield where 0th bit is d0, 1th bit is d1, ... /// /// Returns an error if invalid bits are set. pub fn from_bitfield(bitfield: u32) -> CsResult { if bitfield & !M68K_REGISTER_BITS_ALLOWED_MASK != 0 { Err(Error::InvalidM68kBitfieldRegister) } else { Ok(M68kRegisterBits { bits: bitfield }) } } /// Create from a bitfield where 0th bit is d0, 1th bit is d1, ... /// /// Invalid bits are ignored. pub fn from_bitfield_infallible(bitfield: u32) -> Self { M68kRegisterBits { bits: bitfield & M68K_REGISTER_BITS_ALLOWED_MASK, } } /// Create from iterator over registers: d0-d7, a0-a7, fp0-fp7 /// Invalid registers will cause an error pub fn from_register_iter, R: Into>( reg_iter: T, ) -> CsResult { let mut bits: u32 = 0; for reg in reg_iter { bits |= 1 << M68kRegisterBits::m68k_reg_to_bit_idx(reg.into())?; } Ok(M68kRegisterBits { bits }) } /// Maps an M68K register to a bitfield index /// /// Returns an error if the register is invalid #[inline] pub fn m68k_reg_to_bit_idx(reg: M68kReg::Type) -> CsResult { use capstone_sys::m68k_reg::*; if reg >= M68K_REG_D0 && reg <= M68K_REG_FP7 { Ok((reg - M68K_REG_D0) as u8) } else { Err(Error::InvalidM68kBitfieldRegister) } } /// Returns bitfield as integer #[inline] pub fn as_bits(&self) -> u32 { self.bits } } /// M68K operand type #[derive(Clone, Debug, PartialEq)] pub enum M68kOperand { /// Register Reg(RegId), /// Immediate Imm(u32), /// Memory Mem(M68kOpMem), /// Single precision floating-point FpSingle(f32), /// Double precision floating-point FpDouble(f64), /// Register bits move RegBits(M68kRegisterBits), /// Register pair in the same op (upper 4 bits for first reg, lower for second) RegPair(RegId, RegId), /// Branch displacement Displacement(M68kOpBranchDisplacement), /// Invalid Invalid, } impl M68kOperand { fn new(cs_op: &cs_m68k_op) -> M68kOperand { use self::m68k_op_type::*; use self::M68kOperand::*; let value: cs_m68k_op__bindgen_ty_1 = cs_op.__bindgen_anon_1; match cs_op.type_ { M68K_OP_REG => Reg(RegId(unsafe { value.reg } as RegIdInt)), M68K_OP_IMM => Imm(unsafe { value.imm } as u32), M68K_OP_MEM => Mem(M68kOpMem::new(&cs_op)), M68K_OP_FP_SINGLE => FpSingle(unsafe { value.simm }), M68K_OP_FP_DOUBLE => FpDouble(unsafe { value.dimm }), M68K_OP_REG_BITS => RegBits(M68kRegisterBits::from_bitfield_infallible( cs_op.register_bits, )), M68K_OP_REG_PAIR => { let reg_pair = unsafe { value.reg_pair }; RegPair( RegId(reg_pair.reg_0 as RegIdInt), RegId(reg_pair.reg_1 as RegIdInt), ) } M68K_OP_BR_DISP => Displacement(cs_op.br_disp.into()), M68K_OP_INVALID => Invalid, } } } //todo(tmfink: handle all cases /// Extra info accompanying `M68kOpMem` that is not part of union in `m68k_op_mem` #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub(crate) enum M68kOpMemExtraInfo { /// No extra info None, /// Register Reg(RegId), /// Immediate Imm(u32), } impl M68kOpMemExtraInfo { /// Register (if it exists) pub(crate) fn reg(&self) -> Option { if let M68kOpMemExtraInfo::Reg(reg) = self { Some(*reg) } else { None } } /// Immediate (if it exists) pub(crate) fn imm(&self) -> Option { if let M68kOpMemExtraInfo::Imm(imm) = self { Some(*imm) } else { None } } } /// M68K memory operand #[derive(Debug, Clone)] pub struct M68kOpMem { pub(crate) op_mem: m68k_op_mem, pub(crate) address_mode: m68k_address_mode, /// Register that is populated depending on address mode pub(crate) extra_info: M68kOpMemExtraInfo, } macro_rules! define_m68k_register_option_getter { ( $( #[$enum_attr:meta] )* => $field:ident ) => { $( #[$enum_attr] )* pub fn $field(&self) -> Option { if self.op_mem.$field == M68kReg::M68K_REG_INVALID { None } else { Some(RegId(self.op_mem.$field as RegIdInt)) } } } } macro_rules! define_m68k_getter { ( $( #[$enum_attr:meta] )* => $field:ident : $ret_type:ty ) => { $( #[$enum_attr] )* pub fn $field(&self) -> $ret_type { self.op_mem.$field } } } /// M68K index size #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub enum M68kIndexSize { W, /// Long L, } impl M68kOpMem { /// Create a `M68kOpMem` from `&cs_m68k_op`, which depends on pub fn new(op: &cs_m68k_op) -> Self { use self::M68kAddressMode::*; let address_mode = op.address_mode; let value: cs_m68k_op__bindgen_ty_1 = op.__bindgen_anon_1; let extra_info = match address_mode { M68K_AM_REG_DIRECT_DATA | M68K_AM_REG_DIRECT_ADDR | M68K_AM_REGI_ADDR | M68K_AM_REGI_ADDR_POST_INC | M68K_AM_REGI_ADDR_PRE_DEC => { M68kOpMemExtraInfo::Reg(RegId(unsafe { value.reg } as RegIdInt)) } // The M68K_AM_IMMEDIATE case cannot be floating point because type will not be op_mem M68K_AM_ABSOLUTE_DATA_LONG | M68K_AM_ABSOLUTE_DATA_SHORT | M68K_AM_IMMEDIATE => { M68kOpMemExtraInfo::Imm(unsafe { value.imm } as u32) } M68K_AM_PCI_INDEX_8_BIT_DISP | M68K_AM_PCI_INDEX_BASE_DISP | M68K_AM_AREGI_INDEX_BASE_DISP | M68K_AM_BRANCH_DISPLACEMENT | M68K_AM_NONE | M68K_AM_REGI_ADDR_DISP | M68K_AM_AREGI_INDEX_8_BIT_DISP | M68K_AM_PC_MEMI_POST_INDEX | M68K_AM_PC_MEMI_PRE_INDEX | M68K_AM_MEMI_PRE_INDEX | M68K_AM_MEMI_POST_INDEX | M68K_AM_PCI_DISP => M68kOpMemExtraInfo::None, }; M68kOpMem { op_mem: op.mem, address_mode, extra_info, } } define_m68k_register_option_getter!( /// Base register => base_reg ); define_m68k_register_option_getter!( /// index register => index_reg ); define_m68k_register_option_getter!( /// indirect base register => in_base_reg ); define_m68k_getter!( /// Indirect displacement => in_disp: u32 ); define_m68k_getter!( /// other displacement => out_disp: u32 ); define_m68k_getter!( /// displacement value => disp: i16 ); define_m68k_getter!( /// scale for index register => scale: u8 ); /// Returns (width, offset) pub fn bitfield(&self) -> Option<(u8, u8)> { if self.op_mem.bitfield == 0 { None } else { Some((self.op_mem.width, self.op_mem.offset)) } } pub fn index_size(&self) -> M68kIndexSize { if self.op_mem.index_size == 0 { M68kIndexSize::W } else { M68kIndexSize::L } } /// M68K addressing mode for this op pub fn address_mode(&self) -> M68kAddressMode { self.address_mode } /// Extra info not included in mem type pub(crate) fn extra_info(&self) -> M68kOpMemExtraInfo { self.extra_info } /// Register value pub fn reg(&self) -> Option { self.extra_info.reg() } /// Immediate value pub fn imm(&self) -> Option { self.extra_info.imm() } } impl_PartialEq_repr_fields!(M68kOpMem; base_reg, index_reg, in_base_reg, in_disp, out_disp, disp, scale, bitfield, index_size, address_mode, extra_info ); impl cmp::Eq for M68kOpMem {} impl<'a> From<&'a cs_m68k_op> for M68kOperand { fn from(insn: &cs_m68k_op) -> M68kOperand { M68kOperand::new(insn) } } def_arch_details_struct!( InsnDetail = M68kInsnDetail; Operand = M68kOperand; OperandIterator = M68kOperandIterator; OperandIteratorLife = M68kOperandIterator<'a>; [ pub struct M68kOperandIterator<'a>(slice::Iter<'a, cs_m68k_op>); ] cs_arch_op = cs_m68k_op; cs_arch = cs_m68k; ); #[cfg(test)] mod test { use super::*; use alloc::vec::Vec; use capstone_sys::m68k_address_mode::*; use capstone_sys::m68k_op_type::*; use capstone_sys::m68k_reg::*; use crate::instruction::*; const MEM_ZERO: m68k_op_mem = m68k_op_mem { base_reg: M68K_REG_INVALID, index_reg: M68K_REG_INVALID, in_base_reg: M68K_REG_INVALID, in_disp: 0, out_disp: 0, disp: 0, scale: 0, bitfield: 0, width: 0, offset: 0, index_size: 0, }; #[test] fn test_m68k_op_from() { let op_zero = cs_m68k_op { __bindgen_anon_1: cs_m68k_op__bindgen_ty_1 { imm: 0 }, mem: MEM_ZERO, br_disp: m68k_op_br_disp { disp: 0, disp_size: 0, }, register_bits: 0, type_: M68K_OP_IMM, address_mode: M68K_AM_NONE, }; // Reg let op_reg = cs_m68k_op { __bindgen_anon_1: cs_m68k_op__bindgen_ty_1 { reg: M68K_REG_D7 }, type_: M68K_OP_REG, ..op_zero }; assert_eq!( M68kOperand::new(&op_reg), M68kOperand::Reg(RegId(M68K_REG_D7 as RegIdInt)) ); // Imm let op_imm = cs_m68k_op { __bindgen_anon_1: cs_m68k_op__bindgen_ty_1 { imm: 42 }, type_: M68K_OP_IMM, ..op_zero }; assert_eq!(M68kOperand::new(&op_imm), M68kOperand::Imm(42)); // Mem let op_mem1 = m68k_op_mem { base_reg: M68K_REG_A0, index_reg: M68K_REG_D0, index_size: 0, // w ..MEM_ZERO }; let op_mem = cs_m68k_op { mem: op_mem1, address_mode: M68K_AM_MEMI_POST_INDEX, type_: M68K_OP_MEM, ..op_zero }; let rust_op_mem = M68kOpMem { op_mem: op_mem1, address_mode: M68K_AM_MEMI_POST_INDEX, extra_info: M68kOpMemExtraInfo::None, }; assert_eq!( M68kOperand::new(&op_mem), M68kOperand::Mem(rust_op_mem.clone()) ); assert_eq!(rust_op_mem.base_reg(), Some(RegId(M68K_REG_A0 as RegIdInt))); assert_eq!( rust_op_mem.index_reg(), Some(RegId(M68K_REG_D0 as RegIdInt)) ); assert_eq!(rust_op_mem.in_base_reg(), None); assert_eq!(rust_op_mem.disp(), 0); assert_eq!(rust_op_mem.scale(), 0); assert_eq!(rust_op_mem.bitfield(), None); assert_eq!(rust_op_mem.index_size(), M68kIndexSize::W); assert_eq!(rust_op_mem.address_mode(), M68K_AM_MEMI_POST_INDEX); } #[test] fn register_bits_mask() { assert_eq!( M68K_REGISTER_BITS_ALLOWED_MASK, 0b11111111_11111111_11111111 ); } #[test] fn register_bits_from_bitfield() { assert!(M68kRegisterBits::from_bitfield(0xff).is_ok()); assert!(M68kRegisterBits::from_bitfield(0xff_00).is_ok()); assert!(M68kRegisterBits::from_bitfield(0xff_00_00).is_ok()); assert!(M68kRegisterBits::from_bitfield(0xf_ff_00_00).is_err()); } #[test] fn register_bits_from_iter() { let empty: &[m68k_reg::Type] = &[]; assert_eq!( M68kRegisterBits::from_register_iter(empty.into_iter().map(|x| *x)), Ok(M68kRegisterBits { bits: 0 }) ); assert_eq!( M68kRegisterBits::from_register_iter([M68K_REG_D1].iter().map(|x| *x)), Ok(M68kRegisterBits { bits: 0b10 }) ); assert_eq!( M68kRegisterBits::from_register_iter( [M68K_REG_D1, M68K_REG_A2, M68K_REG_FP7].iter().map(|x| *x) ), Ok(M68kRegisterBits { bits: 0b1000_0000_0000_0100_0000_0010 }) ); } #[test] fn register_bits_as_bits() { let mask = 0b00110011; assert_eq!( mask, M68kRegisterBits::from_bitfield(mask).unwrap().as_bits() ); } #[test] fn op_eq() { use crate::arch::m68k::M68kOperand::*; use crate::arch::m68k::M68kReg::*; use crate::arch::m68k::*; use capstone_sys::m68k_address_mode::*; assert_ne!( M68kOperand::RegBits( M68kRegisterBits::from_register_iter( [M68K_REG_D0, M68K_REG_D2, M68K_REG_A2, M68K_REG_A3] .iter() .map(|x| *x) ) .unwrap() ), M68kOperand::RegBits( M68kRegisterBits::from_register_iter( [M68K_REG_D0, M68K_REG_A2, M68K_REG_A3].iter().map(|x| *x) ) .unwrap() ) ); assert_ne!( Mem(M68kOpMem { op_mem: MEM_ZERO, address_mode: M68K_AM_REGI_ADDR_PRE_DEC, extra_info: M68kOpMemExtraInfo::Reg(RegId(M68K_REG_A7 as RegIdInt)), }), Mem(M68kOpMem { op_mem: MEM_ZERO, address_mode: M68K_AM_REGI_ADDR_PRE_DEC, extra_info: M68kOpMemExtraInfo::Reg(RegId(M68K_REG_A6 as RegIdInt)), }) ); } #[test] fn extra_info() { use crate::arch::DetailsArchInsn; let cs = Capstone::new() .m68k() .mode(arch::m68k::ArchMode::M68k040) .detail(true) .build() .expect("Failed to create Capstone"); let code_parts: &[&'static [u8]] = &[ // jsr $12.l b"\x4e\xb9\x00\x00\x00\x12", ]; let code: Vec = code_parts .iter() .map(|x| x.iter()) .flatten() .map(|x| *x) .collect(); let insns = cs.disasm_all(&code, 0x1000).expect("Failed to disasm"); let mut insns_iter = insns.iter(); // jsr let insn_jsr: Insn = insns_iter.next().unwrap(); let detail = cs.insn_detail(&insn_jsr).unwrap(); let _arch_detail = detail.arch_detail(); let arch_detail = _arch_detail.m68k().unwrap(); let mut ops = arch_detail.operands(); if let M68kOperand::Mem(mem) = ops.next().unwrap() { assert_eq!(mem.imm(), Some(0x12)); } else { panic!("Not expected type") } } } capstone-0.7.0/src/arch/mips.rs010066400017500001750000000050371364465622700146230ustar0000000000000000//! Contains mips-specific types use core::convert::From; use core::{cmp, fmt, slice}; use capstone_sys::{cs_mips, cs_mips_op, mips_op_mem, mips_op_type}; // XXX todo(tmfink): create rusty versions pub use capstone_sys::mips_insn_group as MipsInsnGroup; pub use capstone_sys::mips_insn as MipsInsn; pub use capstone_sys::mips_reg as MipsReg; pub use crate::arch::arch_builder::mips::*; use crate::arch::DetailsArchInsn; use crate::instruction::{RegId, RegIdInt}; /// Contains MIPS-specific details for an instruction pub struct MipsInsnDetail<'a>(pub(crate) &'a cs_mips); impl_PartialEq_repr_fields!(MipsInsnDetail<'a> [ 'a ]; operands ); /// MIPS operand #[derive(Clone, Debug, Eq, PartialEq)] pub enum MipsOperand { /// Register Reg(RegId), /// Immediate Imm(i64), /// Memory Mem(MipsOpMem), /// Invalid Invalid, } impl Default for MipsOperand { fn default() -> Self { MipsOperand::Invalid } } /// MIPS memory operand #[derive(Debug, Copy, Clone)] pub struct MipsOpMem(pub(crate) mips_op_mem); impl MipsOpMem { /// Base register pub fn base(&self) -> RegId { RegId(self.0.base as RegIdInt) } /// Disp value pub fn disp(&self) -> i64 { self.0.disp } } impl_PartialEq_repr_fields!(MipsOpMem; base, disp ); impl cmp::Eq for MipsOpMem {} impl<'a> From<&'a cs_mips_op> for MipsOperand { fn from(insn: &cs_mips_op) -> MipsOperand { match insn.type_ { mips_op_type::MIPS_OP_REG => { MipsOperand::Reg(RegId(unsafe { insn.__bindgen_anon_1.reg } as RegIdInt)) } mips_op_type::MIPS_OP_IMM => MipsOperand::Imm(unsafe { insn.__bindgen_anon_1.imm }), mips_op_type::MIPS_OP_MEM => { MipsOperand::Mem(MipsOpMem(unsafe { insn.__bindgen_anon_1.mem })) } mips_op_type::MIPS_OP_INVALID => MipsOperand::Invalid, } } } def_arch_details_struct!( InsnDetail = MipsInsnDetail; Operand = MipsOperand; OperandIterator = MipsOperandIterator; OperandIteratorLife = MipsOperandIterator<'a>; [ pub struct MipsOperandIterator<'a>(slice::Iter<'a, cs_mips_op>); ] cs_arch_op = cs_mips_op; cs_arch = cs_mips; ); #[cfg(test)] mod test { use super::*; use capstone_sys::*; #[test] fn test_mips_op_from() { let op = cs_mips_op { type_: mips_op_type::MIPS_OP_INVALID, __bindgen_anon_1: cs_mips_op__bindgen_ty_1 { reg: 0 }, }; assert_eq!(MipsOperand::from(&op), MipsOperand::Invalid); } } capstone-0.7.0/src/arch/mod.rs010066400017500001750000000505631364465622700144360ustar0000000000000000//! Contains architecture-specific types and modules use alloc::vec::Vec; use core::fmt::Debug; use core::marker::PhantomData; use crate::capstone::Capstone; use crate::constants::Endian; use crate::error::CsResult; macro_rules! define_subset_enum { ( [ $subset_enum:ident = $base_enum:ident ] $( $variant:ident, )* ) => { #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] pub enum $subset_enum { $( $variant, )* } impl From<$subset_enum> for $base_enum { fn from(other: $subset_enum) -> $base_enum { match other { $( $subset_enum::$variant => $base_enum::$variant, )* } } } }; } /// Define arch builders macro_rules! define_arch_builder { // ExtraMode rules ( @extra_modes () ) => {}; ( @extra_modes ( $( $extra_mode:ident, )+ ) ) => { impl super::BuildsCapstoneExtraMode for ArchCapstoneBuilder { fn extra_mode>(mut self, extra_mode: T) -> Self { self.extra_mode.clear(); self.extra_mode.extend(extra_mode); self } } }; // Syntax rules ( @syntax () ) => {}; ( @syntax ( $( $syntax:ident, )+ ) ) => { impl super::BuildsCapstoneSyntax for ArchCapstoneBuilder { fn syntax(mut self, syntax: ArchSyntax) -> Self { self.syntax = Some(syntax); self } } }; // Endian rules ( @endian ( false) ) => {}; ( @endian ( true ) ) => { impl super::BuildsCapstoneEndian for ArchCapstoneBuilder { fn endian(mut self, endian: Endian) -> Self { self.endian = Some(endian); self } } }; // Entrance rule ( $( [ ( $arch:ident, $arch_variant:ident ) ( mode: $( $mode:ident, )+ ) ( extra_modes: $( $extra_mode:ident, )* ) ( syntax: $( $syntax:ident, )* ) ( both_endian: $( $endian:ident )* ) ] )+ ) => { // We put builders in `arch::arch_builder::$ARCH` so we can put manual arch-specific code // in `arch::$ARCH`. The contents of each module is imported from `arch::$ARCH`. $( /// Architecture-specific build code pub mod $arch { use alloc::vec::Vec; use crate::capstone::Capstone; use crate::constants::{Arch, Endian, ExtraMode, Mode, Syntax}; use crate::error::{CsResult, Error}; define_arch_builder!( @syntax ( $( $syntax, )* ) ); define_arch_builder!( @endian ( $( $endian )* ) ); define_arch_builder!( @extra_modes ( $( $extra_mode, )* ) ); define_subset_enum!( [ ArchMode = Mode ] $( $mode, )* ); define_subset_enum!( [ ArchExtraMode = ExtraMode ] $( $extra_mode, )* ); define_subset_enum!( [ ArchSyntax = Syntax ] $( $syntax, )* ); #[derive(Clone)] pub struct ArchCapstoneBuilder { pub(crate) mode: Option, pub(crate) is_detail: bool, pub(crate) extra_mode: Vec, pub(crate) syntax: Option, pub(crate) endian: Option, } impl super::BuildsCapstone for ArchCapstoneBuilder { fn mode(mut self, mode: ArchMode) -> Self { self.mode = Some(mode); self } fn detail(mut self, enable_detail: bool) -> Self { self.is_detail = enable_detail; self } fn build(self) -> CsResult { let mode = match self.mode { Some(mode) => mode, None => { let msg: &'static str = concat!( "Must specify mode for ", stringify!($arch), "::ArchCapstoneBuilder with `mode()` method", ); return Err(Error::CustomError(msg)); } }; let extra_mode = self.extra_mode.iter().map(|x| ExtraMode::from(*x)); let mut capstone = Capstone::new_raw(Arch::$arch_variant, mode.into(), extra_mode, self.endian)?; if let Some(syntax) = self.syntax { capstone.set_syntax(Syntax::from(syntax))?; } if self.is_detail { capstone.set_detail(self.is_detail)?; } Ok(capstone) } } impl Default for ArchCapstoneBuilder { fn default() -> Self { ArchCapstoneBuilder { mode: None, is_detail: false, extra_mode: vec![], endian: None, syntax: None, } } } } )+ impl CapstoneBuilder { $( pub fn $arch(self) -> $arch::ArchCapstoneBuilder { Default::default() } )* } } } /// Base X macro with arch info /// /// Notes: /// - Even though [Capstone's documentation](https://www.capstone-engine.org/lang_c.html) /// classifies V9 as an extra mode, we classify it as a Mode since the only other mode is Default /// (which is treated as Big endian) macro_rules! arch_info_base { ($x_macro:ident) => { $x_macro!( [ ( arm, ARM ) ( mode: Arm, Thumb, ) ( extra_modes: MClass, V8, ) ( syntax: NoRegName, ) ( both_endian: true ) ] [ ( arm64, ARM64 ) ( mode: Arm, ) ( extra_modes: ) ( syntax: ) ( both_endian: true ) ] [ ( evm, EVM ) ( mode: Default, ) ( extra_modes: ) ( syntax: ) ( both_endian: false ) ] [ ( m680x, M680X ) ( mode: M680x6301, M680x6309, M680x6800, M680x6801, M680x6805, M680x6808, M680x6809, M680x6811, M680xCpu12, M680xHcs08, ) ( extra_modes: ) ( syntax: ) ( both_endian: false ) ] [ ( m68k, M68K ) ( mode: M68k000, M68k010, M68k020, M68k030, M68k040, ) ( extra_modes: ) ( syntax: ) ( both_endian: false ) ] [ ( mips, MIPS ) ( mode: Mips32, Mips64, Mips2, Mips3, Mips32R6, ) ( extra_modes: Micro, ) ( syntax: ) ( both_endian: true ) ] [ ( ppc, PPC ) ( mode: Mode32, Mode64, Qpx, ) ( extra_modes: ) ( syntax: NoRegName, ) ( both_endian: true ) ] [ ( sparc, SPARC ) ( mode: Default, V9, ) ( extra_modes: ) ( syntax: ) ( both_endian: false ) ] [ ( sysz, SYSZ ) ( mode: Default, ) ( extra_modes: ) ( syntax: ) ( both_endian: false ) ] [ ( tms320c64x, TMS320C64X ) ( mode: Default, ) ( extra_modes: ) ( syntax: ) ( both_endian: false ) ] [ ( x86, X86 ) ( mode: Mode16, Mode32, Mode64, ) ( extra_modes: ) ( syntax: Intel, Att, Masm, ) ( both_endian: false ) ] [ ( xcore, XCORE ) ( mode: Default, ) ( extra_modes: ) ( syntax: ) ( both_endian: false ) ] ); }; } /// Builds a `Capstone` struct pub trait BuildsCapstone { /// Set the disassembly mode fn mode(self, mode: ArchMode) -> Self; /// Enable detailed output fn detail(self, enable_detail: bool) -> Self; /// Get final `Capstone` fn build<'a>(self) -> CsResult; } /// Implies that a `CapstoneBuilder` architecture has extra modes pub trait BuildsCapstoneExtraMode: BuildsCapstone { /// Set architecture endianness fn extra_mode>(self, extra_mode: T) -> Self; } /// Implies that a `CapstoneBuilder` has different syntax options pub trait BuildsCapstoneSyntax: BuildsCapstone { /// Set the disassembly syntax fn syntax(self, syntax: ArchSyntax) -> Self; } /// Implies that a `CapstoneBuilder` architecture has a configurable endianness pub trait BuildsCapstoneEndian: BuildsCapstone { /// Set architecture endianness fn endian(self, endian: Endian) -> Self; } /// Contains builder-pattern implementations pub(crate) mod arch_builder { use super::*; arch_info_base!(define_arch_builder); } /// Builds `Capstone` object #[derive(Debug)] pub struct CapstoneBuilder( /// Hidden field to prevent users from instantiating `CapstoneBuilder` PhantomData<()>, ); impl CapstoneBuilder { /// Create a `CapstoneBuilder` pub(crate) fn new() -> Self { CapstoneBuilder(PhantomData) } } /// Provides architecture-specific details about an instruction pub trait DetailsArchInsn: PartialEq + Debug { type Operand: Into + Default + Clone + Debug + PartialEq; type OperandIterator: Iterator; fn operands(&self) -> Self::OperandIterator; } /// Define PartialEq for a type given representation getter methods macro_rules! impl_PartialEq_repr_fields { // With generic parameters ( $name:ty [ $( $lifetime:tt ),* ]; $( $field:ident),* ) => { impl<$( $lifetime ),*> ::core::cmp::PartialEq for $name { fn eq(&self, other: &Self) -> bool { $( if self.$field() != other.$field() { return false; } )* true } } }; // No generic parameters ( $name:ty; $( $field:ident),* ) => { impl_PartialEq_repr_fields!( $name []; $( $field),* ); }; } /// Base macro for defining arch details macro_rules! detail_arch_base { ($x_macro:ident) => { $x_macro!( [ detail = ArmDetail, insn_detail = ArmInsnDetail<'a>, op = ArmOperand, /// Returns the ARM details, if any => arch_name = arm, ] [ detail = Arm64Detail, insn_detail = Arm64InsnDetail<'a>, op = Arm64Operand, /// Returns the ARM64 details, if any => arch_name = arm64, ] [ detail = EvmDetail, insn_detail = EvmInsnDetail<'a>, op = EvmOperand, /// Returns the EVM details, if any => arch_name = evm, ] [ detail = M680xDetail, insn_detail = M680xInsnDetail<'a>, op = M680xOperand, /// Returns the M680X details, if any => arch_name = m680x, ] [ detail = M68kDetail, insn_detail = M68kInsnDetail<'a>, op = M68kOperand, /// Returns the M68K details, if any => arch_name = m68k, ] [ detail = MipsDetail, insn_detail = MipsInsnDetail<'a>, op = MipsOperand, /// Returns the MIPS details, if any => arch_name = mips, ] [ detail = PpcDetail, insn_detail = PpcInsnDetail<'a>, op = PpcOperand, /// Returns the PPC details, if any => arch_name = ppc, ] [ detail = SparcDetail, insn_detail = SparcInsnDetail<'a>, op = SparcOperand, /// Returns the SPARC details, if any => arch_name = sparc, ] [ detail = Tms320c64xDetail, insn_detail = Tms320c64xInsnDetail<'a>, op = Tms320c64xOperand, /// Returns the Tms320c64x details, if any => arch_name = tms320c64x, ] [ detail = X86Detail, insn_detail = X86InsnDetail<'a>, op = X86Operand, /// Returns the X86 details, if any => arch_name = x86, ] [ detail = XcoreDetail, insn_detail = XcoreInsnDetail<'a>, op = XcoreOperand, /// Returns the XCore details, if any => arch_name = xcore, ] ); }; } /// Define ArchDetail enum, ArchOperand enum, and From<$Operand> for ArchOperand macro_rules! detail_defs { ( $( [ detail = $Detail:tt, insn_detail = $InsnDetail:ty, op = $Operand:tt, $( #[$func_attr:meta] )+ => arch_name = $arch_name:ident, ] )+ ) => { $( use self::$arch_name::*; )+ /// Contains architecture-dependent detail structures. /// /// For convenience, there are methods for each architecture that return an `Option` of that /// architecture's detail structure. This allows you to use an `if let Some(...) = { /* ... */ }` /// instead of a match statement. #[derive(Debug)] pub enum ArchDetail<'a> { $( $Detail($InsnDetail), )+ } /// Architecture-independent enum of operands #[derive(Clone, Debug, PartialEq)] pub enum ArchOperand { $( $Operand($Operand), )+ } impl<'a> ArchDetail<'a> { /// Returns architecture independent set of operands pub fn operands(&'a self) -> Vec { match *self { $( ArchDetail::$Detail(ref detail) => { let ops = detail.operands(); let map = ops.map(ArchOperand::from); let vec: Vec = map.collect(); vec } )+ } } $( $( #[$func_attr] )+ pub fn $arch_name(&'a self) -> Option<& $InsnDetail> { if let ArchDetail::$Detail(ref arch_detail) = *self { Some(arch_detail) } else { None } } )+ } $( impl From<$Operand> for ArchOperand { fn from(op: $Operand) -> ArchOperand { ArchOperand::$Operand(op) } } )+ } } /// Define OperandIterator and DetailsArch impl macro_rules! def_arch_details_struct { ( InsnDetail = $InsnDetail:ident; Operand = $Operand:ident; OperandIterator = $OperandIterator:ident; OperandIteratorLife = $OperandIteratorLife:ty; [ $iter_struct:item ] cs_arch_op = $cs_arch_op:ty; cs_arch = $cs_arch:ty; ) => { /// Iterates over instruction operands #[derive(Clone)] $iter_struct impl<'a> $OperandIteratorLife { fn new(ops: &[$cs_arch_op]) -> $OperandIterator { $OperandIterator(ops.iter()) } } impl<'a> Iterator for $OperandIteratorLife { type Item = $Operand; fn next(&mut self) -> Option { match self.0.next() { None => None, Some(op) => Some($Operand::from(op)), } } } impl<'a> ExactSizeIterator for $OperandIteratorLife { fn len(&self) -> usize { self.0.len() } } impl<'a> PartialEq for $OperandIteratorLife { fn eq(&self, other: & $OperandIteratorLife) -> bool { self.len() == other.len() && { let self_clone: $OperandIterator = self.clone(); let other_clone: $OperandIterator = (*other).clone(); self_clone.zip(other_clone).all(|(a, b)| a == b) } } } impl<'a> ::core::fmt::Debug for $OperandIteratorLife { fn fmt(&self, fmt: &mut fmt::Formatter) -> ::core::fmt::Result { fmt.debug_struct(stringify!($OperandIterator)).finish() } } impl<'a> ::core::fmt::Debug for $InsnDetail<'a> { fn fmt(&self, fmt: &mut fmt::Formatter) -> ::core::fmt::Result { fmt.debug_struct(stringify!($InsnDetail)) .field(stringify!($cs_arch), &(self.0 as *const $cs_arch)) .finish() } } impl<'a> crate::arch::DetailsArchInsn for $InsnDetail<'a> { type OperandIterator = $OperandIteratorLife; type Operand = $Operand; fn operands(&self) -> $OperandIteratorLife { $OperandIterator::new(&self.0.operands[..self.0.op_count as usize]) } } } } detail_arch_base!(detail_defs); /// Define "pub mod" uses macro_rules! define_arch_mods { ( $( [ ( $arch:ident, $arch_variant:ident ) ( mode: $( $mode:ident, )+ ) ( extra_modes: $( $extra_mode:ident, )* ) ( syntax: $( $syntax:ident, )* ) ( both_endian: $( $endian:ident )* ) ] )+ ) => { $( pub mod $arch; )+ } } // Define modules at the end so that they can see macro definitions arch_info_base!(define_arch_mods); capstone-0.7.0/src/arch/ppc.rs010066400017500001750000000116101364465622700144270ustar0000000000000000//! Contains ppc-specific types use core::convert::From; use core::{cmp, fmt, slice}; // XXX todo(tmfink): create rusty versions pub use capstone_sys::ppc_insn_group as PpcInsnGroup; pub use capstone_sys::ppc_insn as PpcInsn; pub use capstone_sys::ppc_reg as PpcReg; pub use capstone_sys::ppc_bc as PpcBc; pub use capstone_sys::ppc_bh as PpcBh; use capstone_sys::{cs_ppc, cs_ppc_op, ppc_op_mem, ppc_op_crx, ppc_op_type}; pub use crate::arch::arch_builder::ppc::*; use crate::arch::DetailsArchInsn; use crate::instruction::{RegId, RegIdInt}; /// Contains PPC-specific details for an instruction pub struct PpcInsnDetail<'a>(pub(crate) &'a cs_ppc); impl<'a> PpcInsnDetail<'a> { /// Branch code for branch instructions pub fn bc(&self) -> PpcBc { self.0.bc } /// Branch hint for branch instructions pub fn bh(&self) -> PpcBh { self.0.bh } /// Whether this 'dot' insn updates CR0 pub fn update_cr0(&self) -> PpcBh { self.0.bh } } impl_PartialEq_repr_fields!(PpcInsnDetail<'a> [ 'a ]; bc, bh, update_cr0, operands ); /// PPC operand #[derive(Clone, Debug, Eq, PartialEq)] pub enum PpcOperand { /// Register Reg(RegId), /// Immediate Imm(i64), /// Memory Mem(PpcOpMem), /// Condition Register field Crx(PpcOpCrx), /// Invalid Invalid, } impl Default for PpcOperand { fn default() -> Self { PpcOperand::Invalid } } /// PPC memory operand #[derive(Debug, Copy, Clone)] pub struct PpcOpMem(pub(crate) ppc_op_mem); impl PpcOpMem { /// Base register pub fn base(&self) -> RegId { RegId(self.0.base as RegIdInt) } /// Disp value pub fn disp(&self) -> i32 { self.0.disp } } impl_PartialEq_repr_fields!(PpcOpMem; base, disp ); impl cmp::Eq for PpcOpMem {} /// PPC condition register field #[derive(Debug, Copy, Clone)] pub struct PpcOpCrx(pub(crate) ppc_op_crx); impl PpcOpCrx { /// Scale pub fn scale(&self) -> u32 { self.0.scale as u32 } /// Register value pub fn reg(&self) -> RegId { RegId(self.0.reg as RegIdInt) } /// Condition value pub fn cond(&self) -> PpcBc { self.0.cond } } impl cmp::PartialEq for PpcOpCrx { fn eq(&self, other: &Self) -> bool { (self.scale(), self.reg(), self.cond()) == (other.scale(), other.reg(), other.cond()) } } impl cmp::Eq for PpcOpCrx {} impl<'a> From<&'a cs_ppc_op> for PpcOperand { fn from(insn: &cs_ppc_op) -> PpcOperand { match insn.type_ { ppc_op_type::PPC_OP_REG => { PpcOperand::Reg(RegId(unsafe { insn.__bindgen_anon_1.reg } as RegIdInt)) } ppc_op_type::PPC_OP_IMM => PpcOperand::Imm(unsafe { insn.__bindgen_anon_1.imm }), ppc_op_type::PPC_OP_MEM => { PpcOperand::Mem(PpcOpMem(unsafe { insn.__bindgen_anon_1.mem })) } ppc_op_type::PPC_OP_CRX => { PpcOperand::Crx(PpcOpCrx(unsafe { insn.__bindgen_anon_1.crx })) } ppc_op_type::PPC_OP_INVALID => PpcOperand::Invalid, } } } def_arch_details_struct!( InsnDetail = PpcInsnDetail; Operand = PpcOperand; OperandIterator = PpcOperandIterator; OperandIteratorLife = PpcOperandIterator<'a>; [ pub struct PpcOperandIterator<'a>(slice::Iter<'a, cs_ppc_op>); ] cs_arch_op = cs_ppc_op; cs_arch = cs_ppc; ); #[cfg(test)] mod test { use super::*; #[test] fn test_ppc_op_type() { use capstone_sys::*; use super::ppc_op_type::*; use super::PpcBc::*; use super::PpcReg::*; use self::PpcOperand::*; fn t( op: (ppc_op_type, cs_ppc_op__bindgen_ty_1), expected_op: PpcOperand, ) { let op = PpcOperand::from(&cs_ppc_op { type_: op.0, __bindgen_anon_1: op.1 }); assert_eq!(expected_op, op); } t( (PPC_OP_INVALID, cs_ppc_op__bindgen_ty_1 { reg: 0 }), Invalid, ); t( (PPC_OP_REG, cs_ppc_op__bindgen_ty_1 { reg: 0 }), Reg(RegId(0)), ); t( (PPC_OP_IMM, cs_ppc_op__bindgen_ty_1 { imm: 42 }), Imm(42), ); let crx = ppc_op_crx { scale: 0, reg: PPC_REG_R0, cond: PPC_BC_LT }; t( (PPC_OP_CRX, cs_ppc_op__bindgen_ty_1 { crx }), Crx(PpcOpCrx(crx)), ); let op_mem = PpcOperand::from(&cs_ppc_op { type_: PPC_OP_MEM, __bindgen_anon_1: cs_ppc_op__bindgen_ty_1 { mem: ppc_op_mem { base: PPC_REG_VS38, disp: -10 }} }); if let Mem(op_mem) = op_mem { assert_eq!( (op_mem.base(), op_mem.disp()), (RegId(PPC_REG_VS38 as RegIdInt), -10) ); } else { panic!("Did not get expected Mem"); } } } capstone-0.7.0/src/arch/sparc.rs010066400017500001750000000052421364465622700147610ustar0000000000000000//! Contains sparc-specific types use core::convert::From; use core::{cmp, fmt, slice}; // XXX todo(tmfink): create rusty versions pub use capstone_sys::sparc_insn_group as SparcInsnGroup; pub use capstone_sys::sparc_insn as SparcInsn; pub use capstone_sys::sparc_reg as SparcReg; pub use capstone_sys::sparc_cc as SparcCC; pub use capstone_sys::sparc_hint as SparcHint; use capstone_sys::{cs_sparc, cs_sparc_op, sparc_op_mem, sparc_op_type}; pub use crate::arch::arch_builder::sparc::*; use crate::arch::DetailsArchInsn; use crate::instruction::{RegId, RegIdInt}; /// Contains SPARC-specific details for an instruction pub struct SparcInsnDetail<'a>(pub(crate) &'a cs_sparc); /// SPARC operand #[derive(Clone, Debug, Eq, PartialEq)] pub enum SparcOperand { /// Register Reg(RegId), /// Immediate Imm(i64), /// Memory Mem(SparcOpMem), /// Invalid Invalid, } impl<'a> SparcInsnDetail<'a> { /// Condition codes pub fn cc(&self) -> SparcCC { self.0.cc } /// Branch hint pub fn hint(&self) -> SparcHint { self.0.hint } } impl_PartialEq_repr_fields!(SparcInsnDetail<'a> [ 'a ]; cc, hint, operands ); impl Default for SparcOperand { fn default() -> Self { SparcOperand::Invalid } } /// SPARC memory operand #[derive(Debug, Copy, Clone)] pub struct SparcOpMem(pub(crate) sparc_op_mem); impl SparcOpMem { /// Base register pub fn base(&self) -> RegId { RegId(RegIdInt::from(self.0.base)) } /// Index register pub fn index(&self) -> RegId { RegId(RegIdInt::from(self.0.index)) } /// Disp value pub fn disp(&self) -> i32 { self.0.disp } } impl_PartialEq_repr_fields!(SparcOpMem; base, index, disp ); impl cmp::Eq for SparcOpMem {} impl<'a> From<&'a cs_sparc_op> for SparcOperand { fn from(insn: &cs_sparc_op) -> SparcOperand { match insn.type_ { sparc_op_type::SPARC_OP_REG => { SparcOperand::Reg(RegId(unsafe { insn.__bindgen_anon_1.reg } as RegIdInt)) } sparc_op_type::SPARC_OP_IMM => SparcOperand::Imm(unsafe { insn.__bindgen_anon_1.imm }), sparc_op_type::SPARC_OP_MEM => { SparcOperand::Mem(SparcOpMem(unsafe { insn.__bindgen_anon_1.mem })) } sparc_op_type::SPARC_OP_INVALID => SparcOperand::Invalid, } } } def_arch_details_struct!( InsnDetail = SparcInsnDetail; Operand = SparcOperand; OperandIterator = SparcOperandIterator; OperandIteratorLife = SparcOperandIterator<'a>; [ pub struct SparcOperandIterator<'a>(slice::Iter<'a, cs_sparc_op>); ] cs_arch_op = cs_sparc_op; cs_arch = cs_sparc; ); capstone-0.7.0/src/arch/sysz.rs010066400017500001750000000001161364465622700146540ustar0000000000000000//! Contains sysz-specific types pub use crate::arch::arch_builder::sysz::*; capstone-0.7.0/src/arch/tms320c64x.rs010066400017500001750000000261771364465622700154200ustar0000000000000000//! Contains tms320c64x-specific types use core::convert::From; use core::{cmp, fmt, slice}; use libc::c_int; use capstone_sys::{ cs_tms320c64x, cs_tms320c64x_op, tms320c64x_funit, tms320c64x_mem_dir, tms320c64x_mem_disp, tms320c64x_mem_mod, tms320c64x_op_mem, tms320c64x_op_type, }; // XXX todo(tmfink): create rusty versions pub use capstone_sys::tms320c64x_insn as Tms320c64xInsn; pub use capstone_sys::tms320c64x_insn_group as Tms320c64xInsnGroup; pub use capstone_sys::tms320c64x_reg as Tms320c64xReg; pub use crate::arch::arch_builder::tms320c64x::*; use crate::instruction::{RegId, RegIdInt}; /// Contains TMS320C64X-specific details for an instruction pub struct Tms320c64xInsnDetail<'a>(pub(crate) &'a cs_tms320c64x); define_cs_enum_wrapper_reverse!( [ /// TMS320C64X Functional Unit => Tms320c64xFuntionalUnit = tms320c64x_funit, ] /// Invalid or unspecified => Invalid = TMS320C64X_FUNIT_INVALID; /// D => D = TMS320C64X_FUNIT_D; /// L => L = TMS320C64X_FUNIT_L; /// M => M = TMS320C64X_FUNIT_M; /// S => S = TMS320C64X_FUNIT_S; /// NO => No = TMS320C64X_FUNIT_NO; ); impl<'a> Tms320c64xInsnDetail<'a> { /// Whether condition is zero pub fn is_condition_zero(&self) -> bool { self.0.condition.zero != 0 } /// Condition register pub fn condition_reg(&self) -> RegId { RegId(self.0.condition.reg as RegIdInt) } /// Functional unit pub fn functional_unit(&self) -> Tms320c64xFuntionalUnit { Tms320c64xFuntionalUnit::from_u32(self.0.funit.unit) .unwrap_or(Tms320c64xFuntionalUnit::Invalid) } /// Functional unit side pub fn functional_unit_side(&self) -> u8 { self.0.funit.side as u8 } /// Functional unit cross path pub fn functional_unit_cross_path(&self) -> i8 { // todo(tmfink): capstone bug where cs_tms320c64x.funit.crosspath is stored as unsigned // instead of signed self.0.funit.crosspath as i8 } /// Instruction parallel pub fn parallel(&self) -> i8 { self.0.parallel as c_int as i8 } } impl_PartialEq_repr_fields!(Tms320c64xInsnDetail<'a> [ 'a ]; is_condition_zero, condition_reg, functional_unit, functional_unit_side, functional_unit_cross_path, parallel ); /// TMS320C64X operand #[derive(Clone, Debug, Eq, PartialEq)] pub enum Tms320c64xOperand { /// Register Reg(RegId), /// Immediate Imm(i32), /// Memory Mem(Tms320c64xOpMem), /// Pair of registers RegPair(RegId, RegId), /// Invalid Invalid, } impl Default for Tms320c64xOperand { fn default() -> Self { Tms320c64xOperand::Invalid } } define_cs_enum_wrapper_reverse!( [ /// TMS320C64X Memory Operand modification => Tms320c64xMemDisplayType = tms320c64x_mem_disp, ] /// Invalid or unspecified => Invalid = TMS320C64X_MEM_DISP_INVALID; /// Constant => Constant = TMS320C64X_MEM_DISP_CONSTANT; /// Regiter => Register = TMS320C64X_MEM_DISP_REGISTER; ); /// TMS320C64X Operand Memory Display #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub enum Tms320c64xMemDisplay { /// Invalid or unspecified Invalid, /// Constant Constant(u32), /// Register Register(RegId), } define_cs_enum_wrapper_reverse!( [ /// TMS320C64X Memory Operand direction => Tms320c64xMemDirection = tms320c64x_mem_dir, ] /// Invalid or unspecified => Invalid = TMS320C64X_MEM_DIR_INVALID; /// Forward => Forward = TMS320C64X_MEM_DIR_FW; /// Backward => Backward = TMS320C64X_MEM_DIR_BW; ); define_cs_enum_wrapper_reverse!( [ /// TMS320C64X Memory Operand modification => Tms320c64xMemModify = tms320c64x_mem_mod, ] /// Invalid or unspecified => Invalid = TMS320C64X_MEM_MOD_INVALID; /// No => No = TMS320C64X_MEM_MOD_NO; /// Pre => Pre = TMS320C64X_MEM_MOD_PRE; /// Post => Post = TMS320C64X_MEM_MOD_POST; ); /// TMS320C64X memory operand #[derive(Debug, Copy, Clone)] pub struct Tms320c64xOpMem(pub(crate) tms320c64x_op_mem); /// todo(tmfink): add all getters impl Tms320c64xOpMem { /// Base register pub fn base(&self) -> RegId { RegId(self.0.base as RegIdInt) } /// Disp value (type depends on display_type) fn disp(&self) -> u32 { self.0.disp as u32 } /// Unit of base and offset register pub fn unit(&self) -> u32 { self.0.unit as u32 } /// Offset scaled pub fn scaled(&self) -> u32 { self.0.scaled as u32 } /// Displacement type fn display_type(&self) -> Tms320c64xMemDisplayType { Tms320c64xMemDisplayType::from_u32(self.0.disptype as u32) .unwrap_or(Tms320c64xMemDisplayType::Invalid) } /// Display pub fn display(&self) -> Tms320c64xMemDisplay { match self.display_type() { Tms320c64xMemDisplayType::Invalid => Tms320c64xMemDisplay::Invalid, Tms320c64xMemDisplayType::Constant => Tms320c64xMemDisplay::Constant(self.disp()), Tms320c64xMemDisplayType::Register => { Tms320c64xMemDisplay::Register(RegId(self.disp() as RegIdInt)) } } } /// Direction pub fn direction(&self) -> Tms320c64xMemDirection { Tms320c64xMemDirection::from_u32(self.0.direction as u32) .unwrap_or(Tms320c64xMemDirection::Invalid) } /// Modification pub fn modify(&self) -> Tms320c64xMemModify { Tms320c64xMemModify::from_u32(self.0.modify as u32).unwrap_or(Tms320c64xMemModify::Invalid) } } impl_PartialEq_repr_fields!(Tms320c64xOpMem; base, disp, unit, scaled, display_type, direction, modify ); impl cmp::Eq for Tms320c64xOpMem {} impl<'a> From<&'a cs_tms320c64x_op> for Tms320c64xOperand { fn from(insn: &cs_tms320c64x_op) -> Tms320c64xOperand { match insn.type_ { tms320c64x_op_type::TMS320C64X_OP_REG => { Tms320c64xOperand::Reg(RegId(unsafe { insn.__bindgen_anon_1.reg } as RegIdInt)) } tms320c64x_op_type::TMS320C64X_OP_IMM => { Tms320c64xOperand::Imm(unsafe { insn.__bindgen_anon_1.imm } as i32) } tms320c64x_op_type::TMS320C64X_OP_MEM => { Tms320c64xOperand::Mem(Tms320c64xOpMem(unsafe { insn.__bindgen_anon_1.mem })) } tms320c64x_op_type::TMS320C64X_OP_REGPAIR => { let reg = unsafe { insn.__bindgen_anon_1.reg }; // todo(tmfink): bug in capstone? Tms320c64xOperand::RegPair(RegId((reg as RegIdInt) + 1), RegId(reg as RegIdInt)) } tms320c64x_op_type::TMS320C64X_OP_INVALID => Tms320c64xOperand::Invalid, } } } def_arch_details_struct!( InsnDetail = Tms320c64xInsnDetail; Operand = Tms320c64xOperand; OperandIterator = Tms320c64xOperandIterator; OperandIteratorLife = Tms320c64xOperandIterator<'a>; [ pub struct Tms320c64xOperandIterator<'a>(slice::Iter<'a, cs_tms320c64x_op>); ] cs_arch_op = cs_tms320c64x_op; cs_arch = cs_tms320c64x; ); #[cfg(test)] mod test { use super::*; use capstone_sys::*; use libc::{c_int, c_uint}; const OP_MEM_ZERO: tms320c64x_op_mem = tms320c64x_op_mem { base: 0, disp: 0, unit: 0, scaled: 0, disptype: 0, direction: 0, modify: 0, }; #[test] fn tms320c64x_insn_detail() { let op = cs_tms320c64x_op { type_: tms320c64x_op_type::TMS320C64X_OP_IMM, __bindgen_anon_1: cs_tms320c64x_op__bindgen_ty_1 { imm: 0 }, }; let cs_insn = cs_tms320c64x { op_count: 0, operands: [op; 8], condition: cs_tms320c64x__bindgen_ty_1 { reg: tms320c64x_reg::TMS320C64X_REG_GPLYA as c_uint, zero: 1, }, funit: cs_tms320c64x__bindgen_ty_2 { unit: tms320c64x_funit::TMS320C64X_FUNIT_L as c_uint, side: 18, crosspath: -1 as c_int as c_uint, }, parallel: 1, }; let d = Tms320c64xInsnDetail(&cs_insn); assert!(d.is_condition_zero()); assert_eq!( d.condition_reg(), RegId(Tms320c64xReg::TMS320C64X_REG_GPLYA as RegIdInt) ); assert_eq!(d.functional_unit(), Tms320c64xFuntionalUnit::L); assert_eq!(d.functional_unit_side(), 18); assert_eq!(d.functional_unit_cross_path(), -1); assert_eq!(d.parallel(), 1); } #[test] fn tms320c64x_op_from() { let op = cs_tms320c64x_op { type_: tms320c64x_op_type::TMS320C64X_OP_INVALID, __bindgen_anon_1: cs_tms320c64x_op__bindgen_ty_1 { reg: 0 }, }; assert_eq!( Tms320c64xOperand::from(&op), Tms320c64xOperand::Invalid ); } #[test] fn op_mem() { // display type assert_eq!( Tms320c64xOpMem(OP_MEM_ZERO).display(), Tms320c64xMemDisplay::Invalid ); assert_eq!( Tms320c64xOpMem(tms320c64x_op_mem { disptype: 999, ..OP_MEM_ZERO }) .display(), Tms320c64xMemDisplay::Invalid ); assert_eq!( Tms320c64xOpMem(tms320c64x_op_mem { disptype: tms320c64x_mem_disp::TMS320C64X_MEM_DISP_CONSTANT as c_uint, disp: 3133789374, ..OP_MEM_ZERO }) .display(), Tms320c64xMemDisplay::Constant(3133789374) ); assert_eq!( Tms320c64xOpMem(tms320c64x_op_mem { disptype: tms320c64x_mem_disp::TMS320C64X_MEM_DISP_REGISTER as c_uint, disp: tms320c64x_reg::TMS320C64X_REG_A13 as c_uint, ..OP_MEM_ZERO }) .display(), Tms320c64xMemDisplay::Register(RegId(Tms320c64xReg::TMS320C64X_REG_A13 as RegIdInt)) ); // Simple getters assert_eq!( Tms320c64xOpMem(tms320c64x_op_mem { base: tms320c64x_reg::TMS320C64X_REG_A13 as c_uint, ..OP_MEM_ZERO }) .base(), RegId(Tms320c64xReg::TMS320C64X_REG_A13 as RegIdInt) ); assert_eq!( Tms320c64xOpMem(tms320c64x_op_mem { unit: 29393 as c_uint, ..OP_MEM_ZERO }) .unit(), 29393 ); assert_eq!( Tms320c64xOpMem(tms320c64x_op_mem { scaled: 29393 as c_uint, ..OP_MEM_ZERO }) .scaled(), 29393 ); assert_eq!( Tms320c64xOpMem(tms320c64x_op_mem { direction: tms320c64x_mem_dir::TMS320C64X_MEM_DIR_FW as c_uint, ..OP_MEM_ZERO }) .direction(), Tms320c64xMemDirection::Forward, ); assert_eq!( Tms320c64xOpMem(tms320c64x_op_mem { modify: tms320c64x_mem_mod::TMS320C64X_MEM_MOD_PRE as c_uint, ..OP_MEM_ZERO }) .modify(), Tms320c64xMemModify::Pre, ); } } capstone-0.7.0/src/arch/x86.rs010066400017500001750000000227201364465622700142760ustar0000000000000000//! Contains x86-specific types use core::convert::From; use core::convert::TryInto; use core::{cmp, fmt, slice}; use capstone_sys::{ cs_ac_type, cs_x86, cs_x86_op, cs_x86_op__bindgen_ty_1, x86_op_mem, x86_op_type, }; pub use capstone_sys::x86_insn_group as X86InsnGroup; pub use capstone_sys::x86_insn as X86Insn; pub use capstone_sys::x86_reg as X86Reg; pub use capstone_sys::x86_prefix as X86Prefix; pub use capstone_sys::x86_avx_bcast as X86AvxBcast; pub use capstone_sys::x86_sse_cc as X86SseCC; pub use capstone_sys::x86_avx_cc as X86AvxCC; pub use capstone_sys::x86_xop_cc as X86XopCC; pub use capstone_sys::x86_avx_rm as X86AvxRm; pub use crate::arch::arch_builder::x86::*; use crate::arch::DetailsArchInsn; use crate::instruction::{RegAccessType, RegId, RegIdInt}; /// Contains X86-specific details for an instruction pub struct X86InsnDetail<'a>(pub(crate) &'a cs_x86); // todo(tmfink): expose new types cs_x86__bindgen_ty_1, cs_x86_encoding, x86_xop_cc, // cs_x86_op::access impl X86OperandType { fn new(op_type: x86_op_type, value: cs_x86_op__bindgen_ty_1) -> X86OperandType { use self::x86_op_type::*; use self::X86OperandType::*; match op_type { X86_OP_REG => Reg(RegId(unsafe { value.reg } as RegIdInt)), X86_OP_IMM => Imm(unsafe { value.imm }), X86_OP_MEM => Mem(X86OpMem(unsafe { value.mem })), X86_OP_INVALID => Invalid, } } } /// X86 operand #[derive(Clone, Debug, PartialEq)] pub struct X86Operand { /// Operand size pub size: u8, /// How is this operand accessed? NOTE: this field is irrelevant if engine /// is compiled in DIET mode. pub access: Option, /// AVX broadcast pub avx_bcast: X86AvxBcast, /// AVX zero opmask pub avx_zero_opmask: bool, /// Operand type pub op_type: X86OperandType, } /// X86 operand #[derive(Clone, Debug, PartialEq)] pub enum X86OperandType { /// Register Reg(RegId), /// Immediate Imm(i64), /// Memory Mem(X86OpMem), /// Invalid Invalid, } /// X86 memory operand #[derive(Debug, Copy, Clone)] pub struct X86OpMem(pub(crate) x86_op_mem); impl<'a> X86InsnDetail<'a> { /// Instruction prefix, which can be up to 4 bytes. /// A prefix byte gets value 0 when irrelevant. /// See `X86Prefix` for details. /// /// prefix[0] indicates REP/REPNE/LOCK prefix (See `X86_PREFIX_REP`/`REPNE`/`LOCK`) /// /// prefix[1] indicates segment override (irrelevant for x86_64): /// See `X86_PREFIX_CS`/`SS`/`DS`/`ES`/`FS`/`GS`. /// /// prefix[2] indicates operand-size override (`X86_PREFIX_OPSIZE`) /// /// prefix[3] indicates address-size override (`X86_PREFIX_ADDRSIZE`) pub fn prefix(&self) -> &[u8; 4] { &self.0.prefix } /// Instruction opcode, which can be from 1 to 4 bytes in size. /// This contains VEX opcode as well. /// A trailing opcode byte gets value 0 when irrelevant. pub fn opcode(&self) -> &[u8; 4] { &self.0.opcode } /// REX prefix: only a non-zero value is relevant for x86_64 pub fn rex(&self) -> u8 { self.0.rex } /// Address size pub fn addr_size(&self) -> u8 { self.0.addr_size } /// ModR/M byte pub fn modrm(&self) -> u8 { self.0.modrm } /// SIB (Scaled Index Byte) value, or 0 when irrelevant pub fn sib(&self) -> u8 { self.0.sib } /// Displacement value, valid if encoding.disp_offset != 0 pub fn disp(&self) -> i64 { self.0.disp } /// Scaled Index Byte (SIB) index, or X86_REG_INVALID when irrelevant pub fn sib_index(&self) -> RegId { RegId(self.0.sib_index as RegIdInt) } /// Scaled Index Byte (SIB) scale, or X86_REG_INVALID when irrelevant pub fn sib_scale(&self) -> i8 { self.0.sib_scale } /// Scaled Index Byte (SIB) base register, or X86_REG_INVALID when irrelevant pub fn sib_base(&self) -> RegId { RegId(self.0.sib_base as RegIdInt) } /// eXtended Operations (XOP) Code Condition pub fn xop_cc(&self) -> X86XopCC { self.0.xop_cc } /// Streaming SIMD Extensions (SSE) condition codes pub fn sse_cc(&self) -> X86SseCC { self.0.sse_cc } /// Advanced Vector Extensions (AVX) condition codes pub fn avx_cc(&self) -> X86AvxCC { self.0.avx_cc } /// Advanced Vector Extensions (AVX) sae pub fn avx_sae(&self) -> bool { self.0.avx_sae } /// Advanced Vector Extensions (AVX) rm pub fn avx_rm(&self) -> X86AvxRm { self.0.avx_rm } } impl_PartialEq_repr_fields!(X86InsnDetail<'a> [ 'a ]; prefix, opcode, rex, addr_size, modrm, sib, disp, sib_index, sib_scale, sib_base, sse_cc, avx_cc, avx_sae, avx_rm, operands ); impl X86OpMem { /// Segment pub fn segment(&self) -> u32 { self.0.segment as u32 } /// Base register pub fn base(&self) -> RegId { RegId(self.0.base as RegIdInt) } /// Index register pub fn index(&self) -> RegId { RegId(self.0.index as RegIdInt) } /// Scale pub fn scale(&self) -> i32 { self.0.scale as i32 } /// Display pub fn disp(&self) -> i64 { self.0.disp } } impl_PartialEq_repr_fields!(X86OpMem; segment, base, index, scale, disp ); impl cmp::Eq for X86OpMem {} impl Default for X86Operand { fn default() -> Self { X86Operand { size: 0, access: None, avx_bcast: X86AvxBcast::X86_AVX_BCAST_INVALID, avx_zero_opmask: false, op_type: X86OperandType::Invalid, } } } impl<'a> From<&'a cs_x86_op> for X86Operand { fn from(op: &cs_x86_op) -> X86Operand { let op_type = X86OperandType::new(op.type_, op.__bindgen_anon_1); X86Operand { size: op.size, access: cs_ac_type(op.access as _).try_into().ok(), avx_bcast: op.avx_bcast, avx_zero_opmask: op.avx_zero_opmask, op_type, } } } def_arch_details_struct!( InsnDetail = X86InsnDetail; Operand = X86Operand; OperandIterator = X86OperandIterator; OperandIteratorLife = X86OperandIterator<'a>; [ pub struct X86OperandIterator<'a>(slice::Iter<'a, cs_x86_op>); ] cs_arch_op = cs_x86_op; cs_arch = cs_x86; ); #[cfg(test)] mod test { use super::*; use capstone_sys::*; #[test] fn test_x86_op_type() { use super::x86_op_type::*; use super::X86OperandType::*; fn t( op_type_value: (x86_op_type, cs_x86_op__bindgen_ty_1), expected_op_type: X86OperandType, ) { let (op_type, op_value) = op_type_value; let op_type = X86OperandType::new(op_type, op_value); assert_eq!(expected_op_type, op_type); } t( (X86_OP_INVALID, cs_x86_op__bindgen_ty_1 { reg: 0 }), Invalid, ); t( (X86_OP_REG, cs_x86_op__bindgen_ty_1 { reg: 0 }), Reg(RegId(0)), ); } #[test] fn test_x86_op_eq() { let a1 = X86Operand { op_type: X86OperandType::Imm(0), ..Default::default() }; let a2 = X86Operand { op_type: X86OperandType::Imm(-100), ..Default::default() }; assert_eq!(a1, a1.clone()); assert_ne!(a1, a2.clone()); } #[test] fn test_x86_insn_eq() { fn t_eq(a: &cs_x86, b: &cs_x86) { assert_eq!(X86InsnDetail(a), X86InsnDetail(b)) } fn t_ne(a: &cs_x86, b: &cs_x86) { assert_ne!(X86InsnDetail(a), X86InsnDetail(b)) } let a1 = cs_x86 { prefix: [0, 0, 0, 0], opcode: [0, 0, 0, 0], rex: 0, addr_size: 0, modrm: 0, sib: 0, disp: 0, sib_index: x86_reg::X86_REG_INVALID, sib_scale: 0, sib_base: x86_reg::X86_REG_INVALID, sse_cc: x86_sse_cc::X86_SSE_CC_INVALID, avx_cc: x86_avx_cc::X86_AVX_CC_INVALID, avx_sae: false, avx_rm: x86_avx_rm::X86_AVX_RM_INVALID, op_count: 0, __bindgen_anon_1: cs_x86__bindgen_ty_1 { eflags: 0, }, encoding: cs_x86_encoding { modrm_offset: 0, disp_offset: 0, disp_size: 0, imm_offset: 0, imm_size: 0, }, xop_cc: x86_xop_cc::X86_XOP_CC_INVALID, operands: [ cs_x86_op { type_: x86_op_type::X86_OP_INVALID, __bindgen_anon_1: cs_x86_op__bindgen_ty_1 { reg: x86_reg::X86_REG_INVALID }, size: 0, avx_bcast: x86_avx_bcast::X86_AVX_BCAST_INVALID, avx_zero_opmask: false, access: 0, } ; 8], }; let mut a2 = a1.clone(); a2.operands[1].type_ = x86_op_type::X86_OP_REG; let a1_clone = cs_x86 { ..a1 }; let a3 = cs_x86 { rex: 1, ..a1 }; let op_count_differ = cs_x86 { op_count: 1, ..a1 }; let mut op1_differ = op_count_differ.clone(); op1_differ.operands[0].avx_bcast = x86_avx_bcast::X86_AVX_BCAST_2; t_eq(&a1, &a1); t_eq(&a1, &a2); t_eq(&a1, &a1_clone); t_ne(&a1, &a3); t_ne(&a1, &op_count_differ); t_ne(&op_count_differ, &op1_differ); } } capstone-0.7.0/src/arch/xcore.rs010066400017500001750000000047201364465622700147710ustar0000000000000000//! Contains xcore-specific types use core::convert::From; use core::{cmp, fmt, slice}; // XXX todo(tmfink): create rusty versions pub use capstone_sys::xcore_insn_group as XcoreInsnGroup; pub use capstone_sys::xcore_insn as XcoreInsn; pub use capstone_sys::xcore_reg as XcoreReg; use capstone_sys::{cs_xcore, cs_xcore_op, xcore_op_mem, xcore_op_type}; pub use crate::arch::arch_builder::xcore::*; use crate::arch::DetailsArchInsn; use crate::instruction::{RegId, RegIdInt}; /// Contains XCORE-specific details for an instruction pub struct XcoreInsnDetail<'a>(pub(crate) &'a cs_xcore); impl_PartialEq_repr_fields!(XcoreInsnDetail<'a> [ 'a ]; operands ); /// XCORE operand #[derive(Clone, Debug, Eq, PartialEq)] pub enum XcoreOperand { /// Register Reg(RegId), /// Immediate Imm(i32), /// Memory Mem(XcoreOpMem), /// Invalid Invalid, } impl Default for XcoreOperand { fn default() -> Self { XcoreOperand::Invalid } } /// XCORE memory operand #[derive(Debug, Copy, Clone)] pub struct XcoreOpMem(pub(crate) xcore_op_mem); impl XcoreOpMem { /// Base register pub fn base(&self) -> RegId { RegId(RegIdInt::from(self.0.base)) } /// Index register pub fn index(&self) -> RegId { RegId(RegIdInt::from(self.0.index)) } /// Disp value pub fn disp(&self) -> i32 { self.0.disp } /// Direct value pub fn direct(&self) -> i32 { self.0.direct } } impl_PartialEq_repr_fields!(XcoreOpMem; base, index, disp, direct ); impl cmp::Eq for XcoreOpMem {} impl<'a> From<&'a cs_xcore_op> for XcoreOperand { fn from(insn: &cs_xcore_op) -> XcoreOperand { match insn.type_ { xcore_op_type::XCORE_OP_REG => { XcoreOperand::Reg(RegId(unsafe { insn.__bindgen_anon_1.reg } as RegIdInt)) } xcore_op_type::XCORE_OP_IMM => XcoreOperand::Imm(unsafe { insn.__bindgen_anon_1.imm }), xcore_op_type::XCORE_OP_MEM => { XcoreOperand::Mem(XcoreOpMem(unsafe { insn.__bindgen_anon_1.mem })) } xcore_op_type::XCORE_OP_INVALID => XcoreOperand::Invalid, } } } def_arch_details_struct!( InsnDetail = XcoreInsnDetail; Operand = XcoreOperand; OperandIterator = XcoreOperandIterator; OperandIteratorLife = XcoreOperandIterator<'a>; [ pub struct XcoreOperandIterator<'a>(slice::Iter<'a, cs_xcore_op>); ] cs_arch_op = cs_xcore_op; cs_arch = cs_xcore; ); capstone-0.7.0/src/capstone.rs010066400017500001750000000321701364465622700145500ustar0000000000000000use alloc::string::{String, ToString}; use core::convert::From; use core::marker::PhantomData; use core::mem; use libc::{c_int, c_uint, c_void}; use crate::arch::CapstoneBuilder; use capstone_sys::cs_opt_value::*; use capstone_sys::*; use crate::constants::{Arch, Endian, ExtraMode, Mode, OptValue, Syntax}; use crate::error::*; use crate::ffi::str_from_cstr_ptr; use crate::instruction::{Insn, InsnDetail, InsnGroupId, InsnId, Instructions, RegId}; /// An instance of the capstone disassembler #[derive(Debug)] pub struct Capstone { /// Opaque handle to cs_engine /// Stored as a pointer to ensure `Capstone` is `!Send`/`!Sync` csh: *mut c_void, /// Internal mode bitfield mode: cs_mode, /// Internal endian bitfield endian: cs_mode, /// Syntax syntax: cs_opt_value::Type, /// Internal extra mode bitfield extra_mode: cs_mode, /// Whether to get extra details when disassembling detail_enabled: bool, /// Whether to skipdata when disassembling skipdata_enabled: bool, /// We *must* set `mode`, `extra_mode`, and `endian` at once because `capstone` /// handles them inside the arch-specific handler. We store the bitwise OR of these flags that /// can be passed directly to `cs_option()`. raw_mode: cs_mode, /// Architecture arch: Arch, } /// Defines a setter on `Capstone` that speculatively changes the arch-specific mode (which /// includes `mode`, `endian`, and `extra_mode`). The setter takes a `capstone-rs` type and changes /// the internal `capstone-sys` type. macro_rules! define_set_mode { ( $( #[$func_attr:meta] )* => $($visibility:ident)*, $fn_name:ident, $opt_type:ident, $param_name:ident : $param_type:ident ; $cs_base_type:ident ) => { $( #[$func_attr] )* $($visibility)* fn $fn_name(&mut self, $param_name: $param_type) -> CsResult<()> { let old_val = self.$param_name; self.$param_name = $cs_base_type::from($param_name); let old_raw_mode = self.raw_mode; let new_raw_mode = self.update_raw_mode(); let result = self._set_cs_option( cs_opt_type::$opt_type, new_raw_mode.0 as usize, ); if result.is_err() { // On error, restore old values self.raw_mode = old_raw_mode; self.$param_name = old_val; } result } } } /// Represents that no extra modes are enabled. Can be passed to `Capstone::new_raw()` as the /// `extra_mode` argument. pub static NO_EXTRA_MODE: EmptyExtraModeIter = EmptyExtraModeIter(PhantomData); /// Represents an empty set of `ExtraMode`. #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] pub struct EmptyExtraModeIter(PhantomData<()>); impl Iterator for EmptyExtraModeIter { type Item = ExtraMode; fn next(&mut self) -> Option { None } } impl Capstone { /// Create a new instance of the decompiler using the builder pattern interface. /// This is the recommended interface to `Capstone`. /// /// ``` /// use capstone::prelude::*; /// let cs = Capstone::new().x86().mode(arch::x86::ArchMode::Mode32).build(); /// ``` pub fn new() -> CapstoneBuilder { CapstoneBuilder::new() } /// Create a new instance of the decompiler using the "raw" interface. /// The user must ensure that only sensible `Arch`/`Mode` combinations are used. /// /// ``` /// use capstone::{Arch, Capstone, NO_EXTRA_MODE, Mode}; /// let cs = Capstone::new_raw(Arch::X86, Mode::Mode64, NO_EXTRA_MODE, None); /// assert!(cs.is_ok()); /// ``` pub fn new_raw>( arch: Arch, mode: Mode, extra_mode: T, endian: Option, ) -> CsResult { let mut handle: csh = 0; let csarch: cs_arch = arch.into(); let csmode: cs_mode = mode.into(); // todo(tmfink): test valid modes at run time (or modify upstream capstone) let endian = match endian { Some(endian) => cs_mode::from(endian), None => cs_mode(0), }; let extra_mode = Self::extra_mode_value(extra_mode); let combined_mode = csmode | endian | extra_mode; let err = unsafe { cs_open(csarch, combined_mode, &mut handle) }; if cs_err::CS_ERR_OK == err { let syntax = CS_OPT_SYNTAX_DEFAULT; let raw_mode = cs_mode(0); let detail_enabled = false; let skipdata_enabled = detail_enabled; let mut cs = Capstone { csh: handle as *mut c_void, syntax, endian, mode: csmode, extra_mode, detail_enabled, skipdata_enabled, raw_mode, arch, }; cs.update_raw_mode(); Ok(cs) } else { Err(err.into()) } } /// Disassemble all instructions in buffer pub fn disasm_all<'a>(&'a self, code: &[u8], addr: u64) -> CsResult> { self.disasm(code, addr, 0) } /// Disassemble `count` instructions in `code` pub fn disasm_count<'a>( &'a self, code: &[u8], addr: u64, count: usize, ) -> CsResult> { if count == 0 { return Err(Error::CustomError("Invalid dissasemble count; must be > 0")); } self.disasm(code, addr, count) } /// Disassembles a `&[u8]` full of instructions. /// /// Pass `count = 0` to disassemble all instructions in the buffer. fn disasm<'a>(&'a self, code: &[u8], addr: u64, count: usize) -> CsResult> { let mut ptr: *mut cs_insn = unsafe { mem::zeroed() }; let insn_count = unsafe { cs_disasm( self.csh(), code.as_ptr(), code.len() as usize, addr, count as usize, &mut ptr, ) }; if insn_count == 0 { match self.error_result() { Ok(_) => Ok(Instructions::new_empty()), Err(err) => Err(err), } } else { Ok(unsafe { Instructions::from_raw_parts(ptr, insn_count) }) } } /// Returns csh handle #[inline] fn csh(&self) -> csh { self.csh as csh } /// Returns the raw mode value, which is useful for debugging #[allow(dead_code)] pub(crate) fn raw_mode(&self) -> cs_mode { self.raw_mode } /// Update `raw_mode` with the bitwise OR of `mode`, `extra_mode`, and `endian`. /// /// Returns the new `raw_mode`. fn update_raw_mode(&mut self) -> cs_mode { self.raw_mode = self.mode | self.extra_mode | self.endian; self.raw_mode } /// Return the integer value used by capstone to represent the set of extra modes fn extra_mode_value>(extra_mode: T) -> cs_mode { // Bitwise OR extra modes extra_mode.fold(cs_mode(0), |acc, x| acc | cs_mode::from(x)) } /// Set extra modes in addition to normal `mode` pub fn set_extra_mode>(&mut self, extra_mode: T) -> CsResult<()> { let old_val = self.extra_mode; self.extra_mode = Self::extra_mode_value(extra_mode); let old_mode = self.raw_mode; let new_mode = self.update_raw_mode(); let result = self._set_cs_option(cs_opt_type::CS_OPT_MODE, new_mode.0 as usize); if result.is_err() { // On error, restore old values self.raw_mode = old_mode; self.extra_mode = old_val; } result } /// Set the assembly syntax (has no effect on some platforms) pub fn set_syntax(&mut self, syntax: Syntax) -> CsResult<()> { // Todo(tmfink) check for valid syntax let syntax_int = cs_opt_value::Type::from(syntax); let result = self._set_cs_option(cs_opt_type::CS_OPT_SYNTAX, syntax_int as usize); if result.is_ok() { self.syntax = syntax_int; } result } define_set_mode!( /// Set the endianness (has no effect on some platforms). => pub, set_endian, CS_OPT_MODE, endian : Endian; cs_mode); define_set_mode!( /// Sets the engine's disassembly mode. /// Be careful, various combinations of modes aren't supported /// See the capstone-sys documentation for more information. => pub, set_mode, CS_OPT_MODE, mode : Mode; cs_mode); /// Returns a `CsResult` based on current `errno`. /// If the `errno` is `CS_ERR_OK`, then `Ok(())` is returned. Otherwise, the error is returned. fn error_result(&self) -> CsResult<()> { let errno = unsafe { cs_errno(self.csh()) }; if errno == cs_err::CS_ERR_OK { Ok(()) } else { Err(errno.into()) } } /// Sets disassembling options at runtime. /// /// Acts as a safe wrapper around capstone's `cs_option`. fn _set_cs_option(&mut self, option_type: cs_opt_type, option_value: usize) -> CsResult<()> { let err = unsafe { cs_option(self.csh(), option_type, option_value) }; if cs_err::CS_ERR_OK == err { Ok(()) } else { Err(err.into()) } } /// Controls whether to capstone will generate extra details about disassembled instructions. /// /// Pass `true` to enable detail or `false` to disable detail. pub fn set_detail(&mut self, enable_detail: bool) -> CsResult<()> { let option_value: usize = OptValue::from(enable_detail).0 as usize; let result = self._set_cs_option(cs_opt_type::CS_OPT_DETAIL, option_value); // Only update internal state on success if result.is_ok() { self.detail_enabled = enable_detail; } result } /// Controls whether capstone will skip over invalid or data instructions. /// /// Pass `true` to enable skipdata or `false` to disable skipdata. pub fn set_skipdata(&mut self, enable_skipdata: bool) -> CsResult<()> { let option_value: usize = OptValue::from(enable_skipdata).0 as usize; let result = self._set_cs_option(cs_opt_type::CS_OPT_SKIPDATA, option_value); // Only update internal state on success if result.is_ok() { self.skipdata_enabled = enable_skipdata; } result } /// Converts a register id `reg_id` to a `String` containing the register name. pub fn reg_name(&self, reg_id: RegId) -> Option { let reg_name = unsafe { let _reg_name = cs_reg_name(self.csh(), c_uint::from(reg_id.0)); str_from_cstr_ptr(_reg_name)?.to_string() }; Some(reg_name) } /// Converts an instruction id `insn_id` to a `String` containing the instruction name. /// /// Note: This function ignores the current syntax and uses the default syntax. pub fn insn_name(&self, insn_id: InsnId) -> Option { let insn_name = unsafe { let _insn_name = cs_insn_name(self.csh(), insn_id.0 as c_uint); str_from_cstr_ptr(_insn_name)?.to_string() }; Some(insn_name) } /// Converts a group id `group_id` to a `String` containing the group name. pub fn group_name(&self, group_id: InsnGroupId) -> Option { let group_name = unsafe { let _group_name = cs_group_name(self.csh(), c_uint::from(group_id.0)); str_from_cstr_ptr(_group_name)?.to_string() }; Some(group_name) } /// Returns `Detail` structure for a given instruction /// /// Requires: /// /// 1. Instruction was created with detail enabled /// 2. Skipdata is disabled /// 3. Capstone was not compiled in diet mode pub fn insn_detail<'s, 'i: 's>(&'s self, insn: &'i Insn) -> CsResult> { if !self.detail_enabled { Err(Error::DetailOff) } else if insn.id().0 == 0 { Err(Error::IrrelevantDataInSkipData) } else if Self::is_diet() { Err(Error::IrrelevantDataInDiet) } else { Ok(unsafe { insn.detail(self.arch) }) } } /// Returns a tuple (major, minor) indicating the version of the capstone C library. pub fn lib_version() -> (u32, u32) { let mut major: c_int = 0; let mut minor: c_int = 0; let major_ptr: *mut c_int = &mut major; let minor_ptr: *mut c_int = &mut minor; // We can ignore the "hexical" version returned by capstone because we already have the // major and minor versions let _ = unsafe { cs_version(major_ptr, minor_ptr) }; (major as u32, minor as u32) } /// Returns whether the capstone library supports a given architecture. pub fn supports_arch(arch: Arch) -> bool { unsafe { cs_support(arch as c_int) } } /// Returns whether the capstone library was compiled in diet mode. pub fn is_diet() -> bool { unsafe { cs_support(CS_SUPPORT_DIET as c_int) } } } impl Drop for Capstone { fn drop(&mut self) { unsafe { cs_close(&mut self.csh()) }; } } capstone-0.7.0/src/constants.rs010066400017500001750000000223351364465622700147520ustar0000000000000000use capstone_sys::cs_arch::*; use capstone_sys::cs_opt_value::*; use capstone_sys::*; use core::convert::From; use core::fmt::{self, Display}; use core::str::FromStr; /// A C-like enum can list its variants pub trait EnumList where Self: Sized, { /// Slice of available variants fn variants() -> &'static [Self]; } /// Define the rust enum macro_rules! define_cs_rust_enum { ( [ $( #[$enum_attr:meta] )* => $rust_enum:ident = $cs_enum:ty ] $( $( #[$attr:meta] )* => $rust_variant:ident = $cs_variant:tt; )* ) => { $( #[$enum_attr] )* #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] pub enum $rust_enum { $( $( #[$attr] )* $rust_variant, )* } } } /// Define an `enum` that corresponds to a capstone enum /// /// The different `From` implementations can be disabled by using the cfg attribute macro_rules! define_cs_enum_wrapper { ( [ $( #[$enum_attr:meta] )* => $rust_enum:ident = $cs_enum:ty ] $( $( #[$attr:meta] )* => $rust_variant:ident = $cs_variant:tt; )* ) => { define_cs_rust_enum!( [ $( #[$enum_attr] )* => $rust_enum = $cs_enum ] $( $( #[$attr] )* => $rust_variant = $cs_variant; )* ); impl ::core::convert::From<$rust_enum> for $cs_enum { fn from(other: $rust_enum) -> Self { match other { $( $rust_enum::$rust_variant => $cs_variant, )* } } } impl EnumList for $rust_enum { fn variants() -> &'static [Self] { &[ $( $rust_enum::$rust_variant, )* ] } } impl FromStr for $rust_enum { type Err = &'static str; fn from_str(s: &str) -> Result { let s = s.to_lowercase(); $( if s == stringify!($rust_variant).to_lowercase() { return Ok($rust_enum::$rust_variant); } )* Err(concat!("Failed to parse ", stringify!($rust_enum))) } } impl Display for $rust_enum { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { $( $rust_enum::$rust_variant => write!(f, "{}", stringify!($rust_variant)), )* } } } } } /// Define Rust enum that is created from C enum #[macro_export] macro_rules! define_cs_enum_wrapper_reverse { ( [ $( #[$enum_attr:meta] )* => $rust_enum:ident = $cs_enum:ident, $( from_u32 = $gen_from_u32:ident,)* ] $( $( #[$attr:meta] )* => $rust_variant:ident = $cs_variant:tt; )* ) => { define_cs_rust_enum!( [ $( #[$enum_attr] )* => $rust_enum = $cs_enum ] $( $( #[$attr] )* => $rust_variant = $cs_variant; )* ); impl ::core::convert::From<$cs_enum> for $rust_enum { fn from(other: $cs_enum) -> Self { match other { $( $cs_enum::$cs_variant => $rust_enum::$rust_variant, )* } } } impl $rust_enum { /// Construct from a `u32` #[allow(dead_code)] pub(crate) fn from_u32(other: u32) -> Option<$rust_enum> { match other { $( x if x == ($cs_enum::$cs_variant as u32) => Some($rust_enum::$rust_variant), )* _ => None, } } } } } /// Defines getters for a bitmask /// /// mask_constants must be unsigned integers with exactly one bit set to 1 #[macro_export] macro_rules! define_impl_bitmask { ( impl $struct:ident < $($impl_lifetime:lifetime),* > ; $mask_getter:ident : $mask_getter_ty:ty = { $get_mask:expr } test_mod = $test_mod:ident; $( $( #[$attr:meta] )* => $getter:ident = $mask_constant:ident; )* ) => { impl < $($impl_lifetime),* > $struct < $($impl_lifetime),* > { /// Raw mask from Capstone pub(crate) fn $mask_getter(&self) -> $mask_getter_ty { $get_mask(self) } $( $( #[$attr] )* pub fn $getter(&self) -> bool { ($get_mask(self) & $mask_constant) != 0 } )* } /// Test that masks have exactly one 1 bit set #[allow(non_snake_case)] #[cfg(test)] mod $test_mod { use super::*; $( #[test] fn $getter() { assert_eq!($mask_constant.count_ones(), 1); } )* } } } define_cs_enum_wrapper!( [ /// Architectures for the disassembler => Arch = cs_arch ] /// ARM (Advanced RISC Machine) => ARM = CS_ARCH_ARM; /// ARM 64-bit (also known as AArch64) => ARM64 = CS_ARCH_ARM64; /// MIPS => MIPS = CS_ARCH_MIPS; /// x86 family (includes 16, 32, and 64 bit modes) => X86 = CS_ARCH_X86; /// PowerPC => PPC = CS_ARCH_PPC; /// SPARC => SPARC = CS_ARCH_SPARC; /// System z => SYSZ = CS_ARCH_SYSZ; /// XCore => XCORE = CS_ARCH_XCORE; /// Motorolla 68K => M68K = CS_ARCH_M68K; /// Texas Instruments TMS320C64x => TMS320C64X = CS_ARCH_TMS320C64X; /// Motorola 68000 => M680X = CS_ARCH_M680X; /// EVM => EVM = CS_ARCH_EVM; ); define_cs_enum_wrapper!( [ /// Disassembler modes => Mode = cs_mode ] /// 32-bit ARM => Arm = CS_MODE_ARM; /// 16-bit mode (X86) => Mode16 = CS_MODE_16; /// 32-bit mode (X86) => Mode32 = CS_MODE_32; /// 64-bit mode (X86, PPC) => Mode64 = CS_MODE_64; /// ARM's Thumb mode, including Thumb-2 => Thumb = CS_MODE_THUMB; /// Mips II ISA => Mips2 = CS_MODE_MIPS2; /// Mips III ISA => Mips3 = CS_MODE_MIPS3; /// Mips32r6 ISA => Mips32R6 = CS_MODE_MIPS32R6; /// Mips32 ISA (Mips) => Mips32 = CS_MODE_MIPS32; /// Mips64 ISA (Mips) => Mips64 = CS_MODE_MIPS64; /// SparcV9 mode (Sparc) => V9 = CS_MODE_V9; /// Quad Processing eXtensions mode (PPC) => Qpx = CS_MODE_QPX; /// M68K 68000 mode => M68k000 = CS_MODE_M68K_000; /// M68K 68010 mode => M68k010 = CS_MODE_M68K_010; /// M68K 68020 mode => M68k020 = CS_MODE_M68K_020; /// M68K 68030 mode => M68k030 = CS_MODE_M68K_030; /// M68K 68040 mode => M68k040 = CS_MODE_M68K_040; /// M680X Hitachi 6301,6303 mode => M680x6301 = CS_MODE_M680X_6301; /// M680X Hitachi 6309 mode => M680x6309 = CS_MODE_M680X_6309; /// M680X Motorola 6800,6802 mode => M680x6800 = CS_MODE_M680X_6800; /// M680X Motorola 6801,6803 mode => M680x6801 = CS_MODE_M680X_6801; /// M680X Motorola/Freescale 6805 mode => M680x6805 = CS_MODE_M680X_6805; /// M680X Motorola/Freescale/NXP 68HC08 mode => M680x6808 = CS_MODE_M680X_6808; /// M680X Motorola 6809 mode => M680x6809 = CS_MODE_M680X_6809; /// M680X Motorola/Freescale/NXP 68HC11 mode => M680x6811 = CS_MODE_M680X_6811; /// M680X Motorola/Freescale/NXP CPU12 => M680xCpu12 = CS_MODE_M680X_CPU12; /// M680X Freescale/NXP HCS08 mode => M680xHcs08 = CS_MODE_M680X_HCS08; /// Default mode for little-endian => Default = CS_MODE_LITTLE_ENDIAN; ); define_cs_enum_wrapper!( [ /// Extra modes or features that can be enabled with some modes => ExtraMode = cs_mode ] /// ARM's Cortex-M series. Works with `Arm` mode. => MClass = CS_MODE_MCLASS; /// ARMv8 A32 encodings for ARM. Works with `Arm` and `Thumb` modes. => V8 = CS_MODE_V8; /// MicroMips mode. Works in `MIPS` mode. => Micro = CS_MODE_MICRO; ); define_cs_enum_wrapper!( [ /// Disassembler endianness => Endian = cs_mode ] /// Little-endian mode => Little = CS_MODE_LITTLE_ENDIAN; /// Big-endian mode => Big = CS_MODE_BIG_ENDIAN; ); define_cs_enum_wrapper!( [ /// Disassembly syntax => Syntax = cs_opt_value::Type ] /// Intel syntax => Intel = CS_OPT_SYNTAX_INTEL; /// AT&T syntax (also known as GNU assembler/GAS syntax) => Att = CS_OPT_SYNTAX_ATT; /// MASM syntax => Masm = CS_OPT_SYNTAX_MASM; /// No register name => NoRegName = CS_OPT_SYNTAX_NOREGNAME; ); pub(crate) struct OptValue(pub cs_opt_value::Type); impl From for OptValue { fn from(value: bool) -> Self { if value { OptValue(cs_opt_value::CS_OPT_ON) } else { OptValue(cs_opt_value::CS_OPT_OFF) } } } #[cfg(test)] mod test { use super::*; #[test] fn parse_arch() { assert_eq!(Arch::from_str("x86"), Ok(Arch::X86)); assert_eq!(Arch::from_str("X86"), Ok(Arch::X86)); } } capstone-0.7.0/src/error.rs010066400017500001750000000111431364465622700140620ustar0000000000000000//! Capstone errors use capstone_sys; use capstone_sys::cs_err::*; use core::fmt; use core::result; /// Create `RustFeatures` struct definition, `new()`, and a getter for each field macro_rules! capstone_error_def { ( $( $( #[$attr:meta] )* => $rust_variant:ident = $cs_variant:ident; )* ) => { #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)] /// An error enum for this library pub enum Error { $( $( #[$attr] )* $rust_variant, )* /// An unknown error not equal to a `CapstoneError` UnknownCapstoneError, /// Invalid M68K bitfield register InvalidM68kBitfieldRegister, /// Error with a custom message CustomError(&'static str), } impl From for Error { fn from(err: capstone_sys::cs_err::Type) -> Self { match err { $( $cs_variant => Error::$rust_variant, )* _ => Error::UnknownCapstoneError, } } } } } capstone_error_def!( /// Out-Of-Memory error: cs_open(), cs_disasm(), cs_disasm_iter() => OutOfMemory = CS_ERR_MEM; /// Unsupported Architecture: cs_open() => UnsupportedArch = CS_ERR_ARCH; /// Invalid Handle: cs_op_count(), cs_op_index() => InvalidHandle = CS_ERR_HANDLE; /// Invalid InvalidCsh argument: cs_close(), cs_errno(), cs_option() => InvalidCsh = CS_ERR_CSH; /// Invalid/unsupported mode: cs_open() => InvalidMode = CS_ERR_MODE; /// Invalid/unsupported option: cs_option() => InvalidOption = CS_ERR_OPTION; /// Information is unavailable because detail option is OFF => DetailOff = CS_ERR_DETAIL; /// Dynamic Memory management uninitialized (see CS_OPT_MEM) => UninitializedMemSetup = CS_ERR_MEMSETUP; /// Unsupported Version (bindings) => UnsupportedVersion = CS_ERR_VERSION; /// Access irrelevant data in "diet" engine => IrrelevantDataInDiet = CS_ERR_DIET; /// Access irrelevant data for "data" instruction in SKIPDATA Mode => IrrelevantDataInSkipData = CS_ERR_SKIPDATA; /// X86 AT&T syntax is unsupported (opt-out at compile time) => UnsupportedX86Att = CS_ERR_X86_ATT; /// X86 Intel syntax is unsupported (opt-out at compile time) => UnsupportedX86Intel = CS_ERR_X86_INTEL; /// X86 MASM syntax is unsupported (opt-out at compile time) => UnsupportedX86Masm = CS_ERR_X86_MASM; ); #[must_use] pub type CsResult = result::Result; impl fmt::Display for Error { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "{}", self.description()) } } impl Error { fn description(&self) -> &str { use self::Error::*; match *self { OutOfMemory => "Out-Of-Memory error", UnsupportedArch => "Unsupported architecture", InvalidHandle => "Invalid handle", InvalidCsh => "Invalid csh argument", InvalidMode => "Invalid/unsupported mode", InvalidOption => "Invalid/unsupported option", DetailOff => "Information is unavailable because detail option is OFF", UninitializedMemSetup => "Dynamic memory management uninitialized (see CS_OPT_MEM)", UnsupportedVersion => "Unsupported version (bindings)", IrrelevantDataInDiet => "Access irrelevant data in \"diet\" engine", IrrelevantDataInSkipData => { "Access irrelevant data for \"data\" instruction in SKIPDATA mode" } UnsupportedX86Att => "X86 AT&T syntax is unsupported (opt-out at compile time)", UnsupportedX86Intel => "X86 Intel syntax is unsupported (opt-out at compile time)", UnsupportedX86Masm => "X86 MASM syntax is unsupported (opt-out at compile time)", UnknownCapstoneError => "Encountered Unknown Capstone Return Error", InvalidM68kBitfieldRegister => { "Invalid M68K Register, must be in d0-d7, a0-a7, fp0-fp7" } CustomError(msg) => msg, } } } #[cfg(test)] mod test { use super::Error; use capstone_sys::cs_err; #[test] fn test_error() { let errors = [ Error::OutOfMemory, Error::UnknownCapstoneError, Error::CustomError("custom error"), Error::from(cs_err::CS_ERR_ARCH), Error::from(500 as cs_err::Type), ]; for error in errors.iter() { println!("{}", error); } } } capstone-0.7.0/src/ffi.rs010066400017500001750000000027361364465622700135050ustar0000000000000000//! Functions useful for FFI use core::{slice, str}; use libc::{self, c_char}; /// Given a valid C-style, NUL terminated, UTF8-encoded string, returns a Rust `&str` /// /// Warnings: /// - No checks are made for: NULL pointers, valid UTF-8 /// - This function "creates" a reference with an arbitrary lifetime, so be careful to limit the /// lifetime appropriately pub(crate) unsafe fn str_from_cstr_ptr<'a>(ptr: *const c_char) -> Option<&'a str> { if ptr.is_null() { return None; } let len = libc::strlen(ptr); /* ASSUMPTION: capstone returns NUL terminated string */ let view: &[u8] = slice::from_raw_parts(ptr as *const u8, len as usize); str::from_utf8(view).ok() } #[cfg(test)] mod test { use super::*; use core; #[test] fn cstr_convert() { unsafe { assert_eq!(str_from_cstr_ptr(core::ptr::null() as *const c_char), None); assert_eq!( str_from_cstr_ptr(b"\xff\x00".as_ptr() as *const c_char), None ); assert_eq!( str_from_cstr_ptr(b"\x00".as_ptr() as *const c_char), Some("") ); assert_ne!( str_from_cstr_ptr(b"\x00".as_ptr() as *const c_char), Some("b") ); assert_eq!( str_from_cstr_ptr(b"this is my TEST string\x00".as_ptr() as *const c_char), Some("this is my TEST string") ); } } } capstone-0.7.0/src/instruction.rs010066400017500001750000000307601364465622700153200ustar0000000000000000use core::convert::TryFrom; use core::fmt::{self, Debug, Display, Error, Formatter}; use core::marker::PhantomData; use core::slice; use core::str; use capstone_sys::*; use crate::arch::ArchDetail; use crate::constants::Arch; use crate::ffi::str_from_cstr_ptr; /// Representation of the array of instructions returned by disasm #[derive(Debug)] pub struct Instructions<'a>(&'a mut [cs_insn]); /// Integer type used in `InsnId` pub type InsnIdInt = u32; /// Represents an instruction id, which may be architecture-specific. #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct InsnId(pub InsnIdInt); /// Integer type used in `InsnGroupId` pub type InsnGroupIdInt = u8; /// Represents the group an instruction belongs to, which may be architecture-specific. #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct InsnGroupId(pub InsnGroupIdInt); pub use capstone_sys::cs_group_type as InsnGroupType; /// Integer type used in `RegId` pub type RegIdInt = u16; /// Represents an register id, which is architecture-specific. #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] pub struct RegId(pub RegIdInt); /// Represents how the register is accessed. #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)] pub enum RegAccessType { /// Operand read from memory or register. ReadOnly, /// Operand write from memory or register. WriteOnly, /// Operand read and write from memory or register. ReadWrite, } impl RegAccessType { /// Returns whether the instruction reads from the operand. /// /// Note that an instruction may read and write to the register /// simultaneously. In this case, the operand is also considered as /// readable. pub fn is_readable(self) -> bool { self == RegAccessType::ReadOnly || self == RegAccessType::ReadWrite } /// Returns whether the instruction writes from the operand. /// /// Note that an instruction may read and write to the register /// simultaneously. In this case, the operand is also considered as /// writable. pub fn is_writable(self) -> bool { self == RegAccessType::WriteOnly || self == RegAccessType::ReadWrite } } impl TryFrom for RegAccessType { type Error = (); fn try_from(access: cs_ac_type) -> Result { // Check for flags other than CS_AC_READ or CS_AC_WRITE. let unknown_flag_mask = !(CS_AC_READ | CS_AC_WRITE).0; if (access.0 & unknown_flag_mask) != 0 { return Err(()); } let is_readable = (access & CS_AC_READ).0 != 0; let is_writable = (access & CS_AC_WRITE).0 != 0; match (is_readable, is_writable) { (true, false) => Ok(RegAccessType::ReadOnly), (false, true) => Ok(RegAccessType::WriteOnly), (true, true) => Ok(RegAccessType::ReadWrite), _ => Err(()), } } } impl<'a> Instructions<'a> { pub(crate) unsafe fn from_raw_parts(ptr: *mut cs_insn, len: usize) -> Instructions<'a> { Instructions(slice::from_raw_parts_mut(ptr, len)) } pub(crate) fn new_empty() -> Instructions<'a> { Instructions(&mut []) } /// Get number of instructions pub fn len(&self) -> usize { self.0.len() } /// Iterator over instructions pub fn iter(&'a self) -> InstructionIterator<'a> { let iter = self.0.iter(); InstructionIterator(iter) } pub fn is_empty(&self) -> bool { self.len() == 0 } } impl<'a> Drop for Instructions<'a> { fn drop(&mut self) { if !self.is_empty() { unsafe { cs_free(self.0.as_mut_ptr(), self.len()); } } } } /// impl Iterator (and variants) for a type that wraps slice::iterator /// /// Implements Iterator, ExactSizeIterator, and DoubleEndedIterator macro_rules! impl_SliceIterator_wrapper { ( impl <$( $lifetime:tt ),*> Iterator for $iterator:ty { type Item = $item:ty; [ $next:expr ] } ) => { impl <$( $lifetime ),*> Iterator for $iterator { type Item = $item; #[inline] fn next(&mut self) -> Option { self.0.next().map($next) } #[inline] fn size_hint(&self) -> (usize, Option) { self.0.size_hint() } #[inline] fn count(self) -> usize { self.0.count() } } impl<'a> ExactSizeIterator for $iterator { #[inline] fn len(&self) -> usize { self.0.len() } } impl<'a> DoubleEndedIterator for $iterator { #[inline] fn next_back(&mut self) -> Option { self.0.next_back().map($next) } } } } /// An iterator over the instructions returned by disasm /// /// This is currently the only supported interface for reading them. pub struct InstructionIterator<'a>(slice::Iter<'a, cs_insn>); impl_SliceIterator_wrapper!( impl<'a> Iterator for InstructionIterator<'a> { type Item = Insn<'a>; [ |x| Insn { insn: *x, _marker: PhantomData } ] } ); /// A wrapper for the raw capstone-sys instruction pub struct Insn<'a> { /// Inner `cs_insn` pub(crate) insn: cs_insn, /// Adds lifetime pub(crate) _marker: PhantomData<&'a InsnDetail<'a>>, } /// Contains architecture-independent details about an instruction, such as register reads. /// /// To get additional architecture-specific information, use the `arch_detail()` method to get an /// `ArchDetail` enum. pub struct InsnDetail<'a>(pub(crate) &'a cs_detail, pub(crate) Arch); impl<'a> Insn<'a> { /// The mnemonic for the instruction pub fn mnemonic(&self) -> Option<&str> { unsafe { str_from_cstr_ptr(self.insn.mnemonic.as_ptr()) } } /// The operand string associated with the instruction pub fn op_str(&self) -> Option<&str> { unsafe { str_from_cstr_ptr(self.insn.op_str.as_ptr()) } } /// Access instruction id pub fn id(&self) -> InsnId { InsnId(self.insn.id) } /// Size of instruction (in bytes) fn len(&self) -> usize { self.insn.size as usize } /// Instruction address pub fn address(&self) -> u64 { self.insn.address as u64 } /// Byte-level representation of the instruction pub fn bytes(&self) -> &[u8] { &self.insn.bytes[..self.len()] } /// Returns the `Detail` object, if there is one. It is up to the caller to determine /// the pre-conditions are satisfied. /// /// Be careful this is still in early stages and largely untested with various `cs_option` and /// architecture matrices pub(crate) unsafe fn detail(&self, arch: Arch) -> InsnDetail { InsnDetail(&*self.insn.detail, arch) } } impl<'a> Debug for Insn<'a> { fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { fmt.debug_struct("Insn") .field("address", &self.address()) .field("len", &self.len()) .field("bytes", &self.bytes()) .field("mnemonic", &self.mnemonic()) .field("op_str", &self.op_str()) .finish() } } impl<'a> Display for Insn<'a> { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { write!(fmt, "{:#x}: ", self.address())?; if let Some(mnemonic) = self.mnemonic() { write!(fmt, "{} ", mnemonic)?; if let Some(op_str) = self.op_str() { write!(fmt, "{}", op_str)?; } } Ok(()) } } /// Iterator over registers ids #[derive(Debug, Clone)] pub struct RegsIter<'a, T: 'a + Into + Copy>(slice::Iter<'a, T>); impl<'a, T: 'a + Into + Copy> Iterator for RegsIter<'a, T> { type Item = RegId; fn next(&mut self) -> Option { self.0.next().map(|x| RegId((*x).into())) } #[inline] fn size_hint(&self) -> (usize, Option) { self.0.size_hint() } #[inline] fn count(self) -> usize { self.0.count() } } impl<'a, T: 'a + Into + Copy> ExactSizeIterator for RegsIter<'a, T> { #[inline] fn len(&self) -> usize { self.0.len() } } impl<'a, T: 'a + Into + Copy> DoubleEndedIterator for RegsIter<'a, T> { #[inline] fn next_back(&mut self) -> Option { self.0.next_back().map(|x| RegId((*x).into())) } } /// Iterator over instruction group ids #[derive(Debug, Clone)] pub struct InsnGroupIter<'a>(slice::Iter<'a, InsnGroupIdInt>); impl_SliceIterator_wrapper!( impl<'a> Iterator for InsnGroupIter<'a> { type Item = InsnGroupId; [ |x| InsnGroupId(*x as InsnGroupIdInt) ] } ); impl<'a> InsnDetail<'a> { /// Returns the implicit read registers pub fn regs_read(&self) -> RegsIter { RegsIter((*self.0).regs_read[..self.regs_read_count() as usize].iter()) } /// Returns the number of implicit read registers pub fn regs_read_count(&self) -> u8 { (*self.0).regs_read_count } /// Returns the implicit write registers pub fn regs_write(&self) -> RegsIter { RegsIter((*self.0).regs_write[..self.regs_write_count() as usize].iter()) } /// Returns the number of implicit write registers pub fn regs_write_count(&self) -> u8 { (*self.0).regs_write_count } /// Returns the groups to which this instruction belongs pub fn groups(&self) -> InsnGroupIter { InsnGroupIter((*self.0).groups[..self.groups_count() as usize].iter()) } /// Returns the number groups to which this instruction belongs pub fn groups_count(&self) -> u8 { (*self.0).groups_count } /// Architecture-specific detail pub fn arch_detail(&self) -> ArchDetail { macro_rules! def_arch_detail_match { ( $( [ $ARCH:ident, $detail:ident, $insn_detail:ident, $arch:ident ] )* ) => { use self::ArchDetail::*; use crate::Arch::*; $( use crate::arch::$arch::$insn_detail; )* return match self.1 { $( $ARCH => { $detail($insn_detail(unsafe { &self.0.__bindgen_anon_1.$arch })) } )* _ => panic!("Unsupported detail arch"), } } } def_arch_detail_match!( [ARM, ArmDetail, ArmInsnDetail, arm] [ARM64, Arm64Detail, Arm64InsnDetail, arm64] [EVM, EvmDetail, EvmInsnDetail, evm] [M680X, M680xDetail, M680xInsnDetail, m680x] [M68K, M68kDetail, M68kInsnDetail, m68k] [MIPS, MipsDetail, MipsInsnDetail, mips] [PPC, PpcDetail, PpcInsnDetail, ppc] [SPARC, SparcDetail, SparcInsnDetail, sparc] [TMS320C64X, Tms320c64xDetail, Tms320c64xInsnDetail, tms320c64x] [X86, X86Detail, X86InsnDetail, x86] [XCORE, XcoreDetail, XcoreInsnDetail, xcore] ); } } impl<'a> Debug for InsnDetail<'a> { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { fmt.debug_struct("Detail") .field("regs_read", &self.regs_read()) .field("regs_read_count", &self.regs_read_count()) .field("regs_write", &self.regs_write()) .field("regs_write_count", &self.regs_write_count()) .field("groups", &self.groups()) .field("groups_count", &self.groups_count()) .finish() } } impl<'a> Display for Instructions<'a> { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { for instruction in self.iter() { write!(fmt, "{:x}:\t", instruction.address())?; for byte in instruction.bytes() { write!(fmt, " {:02x}", byte)?; } let remainder = 16 * 3 - instruction.bytes().len() * 3; for _ in 0..remainder { write!(fmt, " ")?; } if let Some(mnemonic) = instruction.mnemonic() { write!(fmt, " {}", mnemonic)?; if let Some(op_str) = instruction.op_str() { write!(fmt, " {}", op_str)?; } } writeln!(fmt)?; } Ok(()) } } #[cfg(test)] mod test { use super::*; #[test] fn test_invalid_reg_access() { assert_eq!(RegAccessType::try_from(cs_ac_type(1337)), Err(())); } } capstone-0.7.0/src/lib.rs010066400017500001750000000120561364465622700135030ustar0000000000000000//! Bindings to the [capstone library][upstream] disassembly framework. //! //! This crate is a wrapper around the //! [Capstone disassembly library](http://www.capstone-engine.org/), //! a "lightweight multi-platform, multi-architecture disassembly framework." //! //! The [`Capstone`](struct.Capstone.html) struct is the main interface to the library. //! //! ```rust //! extern crate capstone; //! //! use capstone::prelude::*; //! //! const X86_CODE: &'static [u8] = b"\x55\x48\x8b\x05\xb8\x13\x00\x00\xe9\x14\x9e\x08\x00\x45\x31\xe4"; //! //! /// Print register names //! fn reg_names(cs: &Capstone, regs: T) -> String //! where //! T: Iterator, //! I: Into, //! { //! let names: Vec = regs.map(|x| cs.reg_name(x.into()).unwrap()).collect(); //! names.join(", ") //! } //! //! /// Print instruction group names //! fn group_names(cs: &Capstone, regs: T) -> String //! where //! T: Iterator, //! I: Into, //! { //! let names: Vec = regs.map(|x| cs.group_name(x.into()).unwrap()).collect(); //! names.join(", ") //! } //! //! fn main() { //! let mut cs = Capstone::new() //! .x86() //! .mode(arch::x86::ArchMode::Mode64) //! .syntax(arch::x86::ArchSyntax::Att) //! .detail(true) //! .build() //! .expect("Failed to create Capstone object"); //! //! let insns = cs.disasm_all(X86_CODE, 0x1000) //! .expect("Failed to disassemble"); //! println!("Found {} instructions", insns.len()); //! for i in insns.iter() { //! println!(); //! println!("{}", i); //! //! let detail: InsnDetail = cs.insn_detail(&i).expect("Failed to get insn detail"); //! let arch_detail: ArchDetail = detail.arch_detail(); //! let ops = arch_detail.operands(); //! //! let output: &[(&str, String)] = &[ //! ("insn id:", format!("{:?}", i.id().0)), //! ("bytes:", format!("{:?}", i.bytes())), //! ("read regs:", reg_names(&cs, detail.regs_read())), //! ("write regs:", reg_names(&cs, detail.regs_write())), //! ("insn groups:", group_names(&cs, detail.groups())), //! ]; //! //! for &(ref name, ref message) in output.iter() { //! println!("{:4}{:12} {}", "", name, message); //! } //! //! println!("{:4}operands: {}", "", ops.len()); //! for op in ops { //! println!("{:8}{:?}", "", op); //! } //! } //! } //! ``` //! //! Produces: //! //! ```txt //! Found 4 instructions //! //! 0x1000: pushq %rbp //! insn id: 580 //! bytes: [85] //! read regs: rsp //! write regs: rsp //! insn groups: mode64 //! operands: 1 //! X86Operand(X86Operand { size: 8, avx_bcast: X86_AVX_BCAST_INVALID, avx_zero_opmask: false, op_type: Reg(RegId(36)) }) //! //! 0x1001: movq 0x13b8(%rip), %rax //! insn id: 442 //! bytes: [72, 139, 5, 184, 19, 0, 0] //! read regs: //! write regs: //! insn groups: //! operands: 2 //! X86Operand(X86Operand { size: 8, avx_bcast: X86_AVX_BCAST_INVALID, avx_zero_opmask: false, op_type: Mem(X86OpMem(x86_op_mem { segment: 0, base: 41, index: 0, scale: 1, disp: 5048 })) }) //! X86Operand(X86Operand { size: 8, avx_bcast: X86_AVX_BCAST_INVALID, avx_zero_opmask: false, op_type: Reg(RegId(35)) }) //! //! 0x1008: jmp 0x8ae21 //! insn id: 266 //! bytes: [233, 20, 158, 8, 0] //! read regs: //! write regs: //! insn groups: jump //! operands: 1 //! X86Operand(X86Operand { size: 8, avx_bcast: X86_AVX_BCAST_INVALID, avx_zero_opmask: false, op_type: Imm(568865) }) //! //! 0x100d: xorl %r12d, %r12d //! insn id: 327 //! bytes: [69, 49, 228] //! read regs: //! write regs: rflags //! insn groups: //! operands: 2 //! X86Operand(X86Operand { size: 4, avx_bcast: X86_AVX_BCAST_INVALID, avx_zero_opmask: false, op_type: Reg(RegId(222)) }) //! X86Operand(X86Operand { size: 4, avx_bcast: X86_AVX_BCAST_INVALID, avx_zero_opmask: false, op_type: Reg(RegId(222)) }) //! //! ``` //! //! [upstream]: http://capstone-engine.org/ //! #![no_std] #[macro_use] extern crate alloc; #[cfg(test)] #[macro_use] extern crate std; #[cfg(test)] #[global_allocator] static ALLOCATOR: std::alloc::System = std::alloc::System; // Define first so macros are available #[macro_use] mod constants; pub mod arch; mod capstone; mod error; mod ffi; mod instruction; #[cfg(test)] mod test; pub use crate::capstone::*; pub use crate::constants::*; pub use crate::error::*; pub use crate::instruction::*; /// Contains items that you probably want to always import /// /// For example: /// /// ``` /// use capstone::prelude::*; /// ``` pub mod prelude { pub use crate::arch::{ self, ArchDetail, BuildsCapstone, BuildsCapstoneEndian, BuildsCapstoneExtraMode, BuildsCapstoneSyntax, DetailsArchInsn, }; pub use crate::{ Capstone, CsResult, InsnDetail, InsnGroupId, InsnGroupIdInt, InsnId, InsnIdInt, RegId, RegIdInt, }; } capstone-0.7.0/src/test.rs010066400017500001750000002667501364465622700137300ustar0000000000000000use alloc::string::String; use alloc::vec::Vec; use std::collections::HashSet; use capstone_sys::cs_group_type; use libc::c_uint; use super::arch::*; use super::*; const X86_CODE: &'static [u8] = b"\x55\x48\x8b\x05\xb8\x13\x00\x00"; const ARM_CODE: &'static [u8] = b"\x55\x48\x8b\x05\xb8\x13\x00\x00"; // Aliases for group types const JUMP: cs_group_type::Type = cs_group_type::CS_GRP_JUMP; const CALL: cs_group_type::Type = cs_group_type::CS_GRP_CALL; const RET: cs_group_type::Type = cs_group_type::CS_GRP_RET; const INT: cs_group_type::Type = cs_group_type::CS_GRP_INT; const IRET: cs_group_type::Type = cs_group_type::CS_GRP_IRET; /// Used as start address for testing const START_TEST_ADDR: u64 = 0x1000; #[test] fn test_x86_simple() { match Capstone::new().x86().mode(x86::ArchMode::Mode64).build() { Ok(cs) => match cs.disasm_all(X86_CODE, START_TEST_ADDR) { Ok(insns) => { assert_eq!(insns.len(), 2); let is: Vec<_> = insns.iter().collect(); assert_eq!(is[0].mnemonic().unwrap(), "push"); assert_eq!(is[1].mnemonic().unwrap(), "mov"); assert_eq!(is[0].address(), START_TEST_ADDR); assert_eq!(is[1].address(), START_TEST_ADDR + 1); assert_eq!(is[0].bytes(), b"\x55"); assert_eq!(is[1].bytes(), b"\x48\x8b\x05\xb8\x13\x00\x00"); } Err(err) => assert!(false, "Couldn't disasm instructions: {}", err), }, Err(e) => { assert!(false, "Couldn't create a cs engine: {}", e); } } } #[test] fn test_arm_simple() { match Capstone::new().arm().mode(arm::ArchMode::Arm).build() { Ok(cs) => match cs.disasm_all(ARM_CODE, START_TEST_ADDR) { Ok(insns) => { assert_eq!(insns.len(), 2); let is: Vec<_> = insns.iter().collect(); assert_eq!(is[0].mnemonic().unwrap(), "streq"); assert_eq!(is[1].mnemonic().unwrap(), "strheq"); assert_eq!(is[0].address(), START_TEST_ADDR); assert_eq!(is[1].address(), START_TEST_ADDR + 4); } Err(err) => assert!(false, "Couldn't disasm instructions: {}", err), }, Err(e) => { assert!(false, "Couldn't create a cs engine: {}", e); } } } #[test] fn test_arm64_none() { let cs = Capstone::new() .arm64() .mode(arm64::ArchMode::Arm) .build() .unwrap(); assert!(cs.disasm_all(ARM_CODE, START_TEST_ADDR).unwrap().is_empty()); } #[test] fn test_x86_names() { match Capstone::new().x86().mode(x86::ArchMode::Mode32).build() { Ok(cs) => { let reg_id = RegId(1); match cs.reg_name(reg_id) { Some(reg_name) => assert_eq!(reg_name, "ah"), None => assert!(false, "Couldn't get register name"), } let insn_id = InsnId(1); match cs.insn_name(insn_id) { Some(insn_name) => assert_eq!(insn_name, "aaa"), None => assert!(false, "Couldn't get instruction name"), } assert_eq!(cs.group_name(InsnGroupId(1)), Some(String::from("jump"))); let reg_id = RegId(250); match cs.reg_name(reg_id) { Some(_) => assert!(false, "invalid register worked"), None => {} } let insn_id = InsnId(6000); match cs.insn_name(insn_id) { Some(_) => assert!(false, "invalid instruction worked"), None => {} } assert_eq!(cs.group_name(InsnGroupId(250)), None); } Err(e) => { assert!(false, "Couldn't create a cs engine: {}", e); } } } #[test] fn test_detail_false_fail() { let mut cs = Capstone::new() .x86() .mode(x86::ArchMode::Mode64) .build() .unwrap(); cs.set_detail(false).unwrap(); let insns = cs.disasm_all(X86_CODE, START_TEST_ADDR).unwrap(); let insns: Vec<_> = insns.iter().collect(); assert_eq!(cs.insn_detail(&insns[0]).unwrap_err(), Error::DetailOff); assert_eq!(cs.insn_detail(&insns[1]).unwrap_err(), Error::DetailOff); } #[test] fn test_skipdata() { use capstone_sys::x86_insn; let mut cs = Capstone::new() .x86() .mode(x86::ArchMode::Mode64) .build() .unwrap(); cs.set_detail(false).unwrap(); cs.set_skipdata(true).unwrap(); let x86_code_skip: &[u8] = b"\x2f\x6c"; let insns = cs.disasm_all(x86_code_skip, 0x1000).unwrap(); let insns: Vec<_> = insns.iter().collect(); assert_eq!(insns.len(), 2); assert_eq!(insns[0].id().0, x86_insn::X86_INS_INVALID as u32); assert_eq!(insns[1].id().0, x86_insn::X86_INS_INSB as u32); } #[test] fn test_detail_true() { let mut cs1 = Capstone::new() .x86() .mode(x86::ArchMode::Mode64) .build() .unwrap(); cs1.set_detail(true).unwrap(); let cs2 = Capstone::new() .x86() .mode(x86::ArchMode::Mode64) .detail(true) .build() .unwrap(); for cs in [cs1, cs2].iter_mut() { let insns = cs.disasm_all(X86_CODE, START_TEST_ADDR).unwrap(); let insns: Vec<_> = insns.iter().collect(); let insn_group_ids = [ cs_group_type::CS_GRP_JUMP, cs_group_type::CS_GRP_CALL, cs_group_type::CS_GRP_RET, cs_group_type::CS_GRP_INT, cs_group_type::CS_GRP_IRET, ]; for insn_idx in 0..1 + 1 { let detail = cs .insn_detail(&insns[insn_idx]) .expect("Unable to get detail"); let groups: Vec<_> = detail.groups().collect(); for insn_group_id in &insn_group_ids { let insn_group = InsnGroupId(*insn_group_id as InsnGroupIdInt); assert_eq!(groups.contains(&insn_group), false); } } } } fn test_instruction_helper( cs: &Capstone, insn: &Insn, mnemonic_name: &str, bytes: &[u8], has_default_syntax: bool, ) { println!("{:?}", insn); // Check mnemonic if has_default_syntax { // insn_name() does not respect current syntax // does not always match the internal mnemonic cs.insn_name(insn.id()) .expect("Failed to get instruction name"); } assert_eq!( mnemonic_name, insn.mnemonic().expect("Failed to get mnemonic"), "Did not match contained insn.mnemonic" ); // Assert instruction bytes match assert_eq!(bytes, insn.bytes()); } fn test_instruction_detail_helper( cs: &Capstone, insn: &Insn, info: &DetailedInsnInfo, has_default_syntax: bool, ) where T: Into + Clone, { // Check mnemonic if has_default_syntax { // insn_name() does not respect current syntax // does not always match the internal mnemonic cs.insn_name(insn.id()) .expect("Failed to get instruction name"); } assert_eq!( info.mnemonic, insn.mnemonic().expect("Failed to get mnemonic"), "Did not match contained insn.mnemonic" ); // Assert instruction bytes match assert_eq!(info.bytes, insn.bytes()); let detail = cs.insn_detail(insn).expect("Could not get detail"); let arch_detail = detail.arch_detail(); let arch_ops = arch_detail.operands(); let expected_ops: Vec<_> = info .operands .iter() .map(|expected_op| { let expected_op: ArchOperand = (*expected_op).clone().into(); expected_op }) .collect(); assert_eq!(expected_ops, arch_ops, "operands do not match"); } /// Assert instruction belongs or does not belong to groups, testing both insn_belongs_to_group /// and insn_group_ids fn test_instruction_group_helper>( cs: &Capstone, insn: &Insn, mnemonic_name: &str, bytes: &[u8], expected_groups: &[cs_group_type::Type], expected_regs_read: &[R], expected_regs_write: &[R], has_default_syntax: bool, ) where R: Into + Copy, { test_instruction_helper(&cs, insn, mnemonic_name, bytes, has_default_syntax); let detail = cs.insn_detail(insn).expect("Unable to get detail"); // Assert expected instruction groups is a subset of computed groups through ids let instruction_group_ids: HashSet = detail.groups().collect(); let expected_groups_ids: HashSet = expected_groups .iter() .map(|&x| InsnGroupId(x as u8)) .collect(); assert!( expected_groups_ids.is_subset(&instruction_group_ids), "Expected groups {:?} does NOT match computed insn groups {:?} with ", expected_groups_ids, instruction_group_ids ); // Assert expected instruction groups is a subset of computed groups through enum let expected_groups_set: HashSet = expected_groups .iter() .map(|&x| InsnGroupId(x as u8)) .collect(); assert!( expected_groups_set.is_subset(&instruction_group_ids), "Expected groups {:?} does NOT match computed insn groups {:?}", expected_groups_set, instruction_group_ids ); macro_rules! assert_regs_match { ($expected:expr, $actual_regs:expr, $msg:expr) => {{ let mut expected_regs: Vec<_> = $expected .iter() .map(|x| RegId(x.clone().into() as RegIdInt)) .collect(); expected_regs.sort_unstable(); let mut regs: Vec<_> = $actual_regs.collect(); regs.sort_unstable(); assert_eq!(expected_regs, regs, $msg); }}; } assert_regs_match!( expected_regs_read, detail.regs_read(), "read_regs did not match" ); assert_regs_match!( expected_regs_write, detail.regs_write(), "write_regs did not match" ); } fn instructions_match_group( cs: &mut Capstone, expected_insns: &[(&str, &[u8], &[cs_group_type::Type], &[R], &[R])], has_default_syntax: bool, ) where R: Into + Copy, { let insns_buf: Vec = expected_insns .iter() .flat_map(|&(_, bytes, _, _, _)| bytes) .map(|x| *x) .collect(); // Details required to get groups information cs.set_detail(true).unwrap(); let insns = cs .disasm_all(&insns_buf, START_TEST_ADDR) .expect("Failed to disassemble"); let insns: Vec = insns.iter().collect(); // Check number of instructions assert_eq!(insns.len(), expected_insns.len()); for ( insn, &( expected_mnemonic, expected_bytes, expected_groups, expected_regs_read, expected_regs_write, ), ) in insns.iter().zip(expected_insns) { test_instruction_group_helper( &cs, insn, expected_mnemonic, expected_bytes, expected_groups, expected_regs_read, expected_regs_write, has_default_syntax, ) } } fn instructions_match( cs: &mut Capstone, expected_insns: &[(&str, &[u8])], has_default_syntax: bool, ) { let insns_buf: Vec = expected_insns .iter() .flat_map(|&(_, bytes)| bytes) .map(|x| *x) .collect(); // Details required to get groups information cs.set_detail(true).unwrap(); let insns = cs .disasm_all(&insns_buf, START_TEST_ADDR) .expect("Failed to disassemble"); let insns: Vec<_> = insns.iter().collect(); // Check number of instructions assert_eq!( insns.len(), expected_insns.len(), "Wrong number of instructions" ); for (insn, &(expected_mnemonic, expected_bytes)) in insns.iter().zip(expected_insns) { test_instruction_helper( &cs, insn, expected_mnemonic, expected_bytes, has_default_syntax, ) } } fn instructions_match_detail( cs: &mut Capstone, info: &[DetailedInsnInfo], has_default_syntax: bool, ) where T: Into + Clone, { let insns_buf: Vec = info .iter() .flat_map(|ref info| info.bytes) .map(|x| *x) .collect(); // Details required to get groups information cs.set_detail(true).unwrap(); // todo(tmfink) eliminate check if info.len() == 0 { // Input was empty, which will cause disasm_all() to fail return; } let insns = cs .disasm_all(&insns_buf, START_TEST_ADDR) .expect("Failed to disassemble"); let insns: Vec<_> = insns.iter().collect(); // Check number of instructions assert_eq!( insns.len(), info.len(), "Number of instructions {} does not match number of provided instruction info structs {}", insns.len(), info.len(), ); for (insn, info) in insns.iter().zip(info) { test_instruction_detail_helper(&cs, insn, info, has_default_syntax) } } #[test] fn test_instruction_details() { use crate::arch::x86::X86Reg; use crate::arch::x86::X86Reg::*; let expected_insns: &[( &str, &[u8], &[cs_group_type::Type], &[X86Reg::Type], &[X86Reg::Type], )] = &[ ("nop", b"\x90", &[], &[], &[]), ("je", b"\x74\x05", &[JUMP], &[X86_REG_EFLAGS], &[]), ( "call", b"\xe8\x28\x07\x00\x00", &[CALL], &[X86_REG_RIP, X86_REG_RSP], &[X86_REG_RSP], ), ("ret", b"\xc3", &[RET], &[X86_REG_RSP], &[X86_REG_RSP]), ("syscall", b"\x0f\x05", &[INT], &[], &[]), ("iretd", b"\xcf", &[IRET], &[], &[]), ("sub", b"\x48\x83\xec\x08", &[], &[], &[X86_REG_EFLAGS]), ("test", b"\x48\x85\xc0", &[], &[], &[X86_REG_EFLAGS]), ("mov", b"\x48\x8b\x05\x95\x4a\x4d\x00", &[], &[], &[]), ("mov", b"\xb9\x04\x02\x00\x00", &[], &[], &[]), ]; let mut cs = Capstone::new() .x86() .mode(x86::ArchMode::Mode64) .build() .unwrap(); instructions_match_group(&mut cs, expected_insns, true); } fn test_insns_match(cs: &mut Capstone, insns: &[(&str, &[u8])]) { for &(mnemonic, bytes) in insns.iter() { let insns = cs.disasm_all(bytes, START_TEST_ADDR).unwrap(); assert_eq!(insns.len(), 1); let insn = insns.iter().next().unwrap(); assert_eq!(insn.mnemonic(), Some(mnemonic)); } } fn test_extra_mode_helper( arch: Arch, mode: Mode, extra_mode: &[ExtraMode], valid_both_insns: &[(&str, &[u8])], valid_extra_mode: &[(&str, &[u8])], ) { let extra_mode = extra_mode.iter().map(|x| *x); let mut cs = Capstone::new_raw(arch, mode, extra_mode, None).unwrap(); test_insns_match(&mut cs, valid_both_insns); for &(_, _) in valid_extra_mode.iter() { // Capstone will disassemble instructions not allowed by the current mode // assert!( // cs.disasm_all(bytes, START_TEST_ADDR).is_err(), // "Disassembly succeeded when on instruction when it should not have for {:?}", // bytes); } test_insns_match(&mut cs, valid_both_insns); test_insns_match(&mut cs, valid_extra_mode); } #[test] fn test_extra_mode() { test_extra_mode_helper( Arch::ARM, Mode::Arm, &[ExtraMode::V8], &[("str", b"\x04\xe0\x2d\xe5")], &[("vcvtt.f64.f16", b"\xe0\x3b\xb2\xee")], ); } fn test_arch_mode_endian_insns( cs: &mut Capstone, arch: Arch, mode: Mode, endian: Option, extra_mode: &[ExtraMode], insns: &[(&str, &[u8])], ) { let expected_insns: Vec<(&str, &[u8])> = insns .iter() .map(|&(mnemonic, bytes)| (mnemonic, bytes)) .collect(); let mut cs_raw = Capstone::new_raw(arch, mode, extra_mode.iter().map(|x| *x), endian).unwrap(); let mut cs_raw_endian_set = Capstone::new_raw(arch, mode, extra_mode.iter().map(|x| *x), None).unwrap(); if let Some(some_endian) = endian { cs_raw_endian_set .set_endian(some_endian) .expect("Failed to set endianness"); } instructions_match(cs, expected_insns.as_slice(), true); instructions_match(&mut cs_raw, expected_insns.as_slice(), true); instructions_match(&mut cs_raw_endian_set, expected_insns.as_slice(), true); } #[derive(Copy, Clone, Debug)] struct DetailedInsnInfo<'a, T: 'a + Into> { pub mnemonic: &'a str, pub bytes: &'a [u8], pub operands: &'a [T], } type DII<'a, T> = DetailedInsnInfo<'a, T>; impl<'a, T> DetailedInsnInfo<'a, T> where T: Into, { fn new(mnemonic: &'a str, bytes: &'a [u8], operands: &'a [T]) -> DetailedInsnInfo<'a, T> where T: Into, { DetailedInsnInfo { mnemonic, bytes, operands, } } } fn test_arch_mode_endian_insns_detail( cs: &mut Capstone, arch: Arch, mode: Mode, endian: Option, extra_mode: &[ExtraMode], insns: &[DetailedInsnInfo], ) where T: Into + Clone, { let extra_mode = extra_mode.iter().map(|x| *x); let mut cs_raw = Capstone::new_raw(arch, mode, extra_mode, endian).unwrap(); instructions_match_detail(&mut cs_raw, insns, true); instructions_match_detail(cs, insns, true); } #[test] fn test_syntax() { use crate::arch::x86::X86Reg; use crate::arch::x86::X86Reg::*; let expected_insns: &[( &str, &str, &[u8], &[cs_group_type::Type], &[X86Reg::Type], &[X86Reg::Type], )] = &[ ("nop", "nop", b"\x90", &[], &[], &[]), ("je", "je", b"\x74\x05", &[JUMP], &[X86_REG_EFLAGS], &[]), ( "call", "callq", b"\xe8\x28\x07\x00\x00", &[CALL], &[X86_REG_RIP, X86_REG_RSP], &[X86_REG_RSP], ), ( "ret", "retq", b"\xc3", &[RET], &[X86_REG_RSP], &[X86_REG_RSP], ), ("syscall", "syscall", b"\x0f\x05", &[INT], &[], &[]), ("iretd", "iretl", b"\xcf", &[IRET], &[], &[]), ( "sub", "subq", b"\x48\x83\xec\x08", &[], &[], &[X86_REG_EFLAGS], ), ( "test", "testq", b"\x48\x85\xc0", &[], &[], &[X86_REG_EFLAGS], ), ( "mov", "movq", b"\x48\x8b\x05\x95\x4a\x4d\x00", &[], &[], &[], ), ("mov", "movl", b"\xb9\x04\x02\x00\x00", &[], &[], &[]), ]; let expected_insns_intel: Vec<( &str, &[u8], &[cs_group_type::Type], &[X86Reg::Type], &[X86Reg::Type], )> = expected_insns .iter() .map(|&(mnemonic, _, bytes, groups, reads, writes)| { (mnemonic, bytes, groups, reads, writes) }) .collect(); let expected_insns_att: Vec<( &str, &[u8], &[cs_group_type::Type], &[X86Reg::Type], &[X86Reg::Type], )> = expected_insns .iter() .map(|&(_, mnemonic, bytes, groups, reads, writes)| { (mnemonic, bytes, groups, reads, writes) }) .collect(); let mut cs = Capstone::new() .x86() .mode(x86::ArchMode::Mode64) .syntax(x86::ArchSyntax::Intel) .build() .unwrap(); instructions_match_group(&mut cs, &expected_insns_intel, true); cs.set_syntax(Syntax::Intel).unwrap(); instructions_match_group(&mut cs, &expected_insns_intel, true); cs.set_syntax(Syntax::Att).unwrap(); instructions_match_group(&mut cs, &expected_insns_att, false); // In this case, MASM and Intel syntaxes match cs.set_syntax(Syntax::Masm).unwrap(); instructions_match_group(&mut cs, &expected_insns_intel, false); } // @todo(tmfink) test invalid syntax once we check for invalid options #[test] fn test_invalid_syntax() { // These do no support any syntax change let rules = [(Arch::ARM, Mode::Thumb)]; let syntaxes = [ // Syntax::Intel, // Syntax::Att, // Syntax::Masm, // Syntax::NoRegName, ]; for &(arch, mode) in rules.iter() { let mut cs = Capstone::new_raw(arch, mode, NO_EXTRA_MODE, None).unwrap(); for &syntax in syntaxes.iter() { let result = cs.set_syntax(syntax); assert!(result.is_err(), "Expected Err, got {:?}", result); } } } // todo(tmfink): enable test once we test for valid modes #[test] #[ignore] fn test_invalid_mode() { if let Err(err) = Capstone::new_raw(Arch::PPC, Mode::Thumb, NO_EXTRA_MODE, None) { assert_eq!(err, Error::InvalidMode); } else { panic!("Should fail to create given modes"); } } #[test] fn test_capstone_version() { let (major, minor) = Capstone::lib_version(); println!("Capstone lib version: ({}, {})", major, minor); assert!(major > 0 && major < 100, "Invalid major version {}", major); assert!(minor < 500, "Invalid minor version {}", minor); } #[test] fn test_capstone_supports_arch() { let architectures = vec![ Arch::ARM, Arch::ARM64, Arch::MIPS, Arch::X86, Arch::PPC, Arch::SPARC, Arch::SYSZ, Arch::XCORE, // Arch::M68K, ]; println!("Supported architectures"); for arch in architectures { let supports_arch = Capstone::supports_arch(arch); println!(" {:?}: {}", arch, if supports_arch { "yes" } else { "no" }); } } #[test] fn test_capstone_is_diet() { println!("Capstone is diet: {}", Capstone::is_diet()); } #[test] fn test_arch_arm() { test_arch_mode_endian_insns( &mut Capstone::new() .arm() .mode(arm::ArchMode::Arm) .build() .unwrap(), Arch::ARM, Mode::Arm, None, &[], &[ ("bl", b"\xed\xff\xff\xeb"), ("str", b"\x04\xe0\x2d\xe5"), ("andeq", b"\x00\x00\x00\x00"), ("str", b"\xe0\x83\x22\xe5"), ("mcreq", b"\xf1\x02\x03\x0e"), ("mov", b"\x00\x00\xa0\xe3"), ("strb", b"\x02\x30\xc1\xe7"), ("cmp", b"\x00\x00\x53\xe3"), ("setend", b"\x00\x02\x01\xf1"), ("ldm", b"\x05\x40\xd0\xe8"), ("strdeq", b"\xf4\x80\x00\x00"), ], ); test_arch_mode_endian_insns( &mut Capstone::new() .arm() .mode(arm::ArchMode::Thumb) .build() .unwrap(), Arch::ARM, Mode::Thumb, None, &[], &[ ("bx", b"\x70\x47"), ("blx", b"\x00\xf0\x10\xe8"), ("mov", b"\xeb\x46"), ("sub", b"\x83\xb0"), ("ldr", b"\xc9\x68"), ("cbz", b"\x1f\xb1"), ("wfi", b"\x30\xbf"), ("cpsie.w", b"\xaf\xf3\x20\x84"), ("tbb", b"\xd1\xe8\x00\xf0"), ("movs", b"\xf0\x24"), ("lsls", b"\x04\x07"), ("subs", b"\x1f\x3c"), ("stm", b"\xf2\xc0"), ("movs", b"\x00\x00"), ("mov.w", b"\x4f\xf0\x00\x01"), ("ldr", b"\x46\x6c"), ], ); test_arch_mode_endian_insns( &mut Capstone::new() .arm() .mode(arm::ArchMode::Thumb) .build() .unwrap(), Arch::ARM, Mode::Thumb, None, &[], &[ ("mov.w", b"\x4f\xf0\x00\x01"), ("pop.w", b"\xbd\xe8\x00\x88"), ("tbb", b"\xd1\xe8\x00\xf0"), ("it", b"\x18\xbf"), ("iteet", b"\xad\xbf"), ("vdupne.8", b"\xf3\xff\x0b\x0c"), ("msr", b"\x86\xf3\x00\x89"), ("msr", b"\x80\xf3\x00\x8c"), ("sxtb.w", b"\x4f\xfa\x99\xf6"), ("vaddw.u16", b"\xd0\xff\xa2\x01"), ], ); test_arch_mode_endian_insns( &mut Capstone::new() .arm() .mode(arm::ArchMode::Thumb) .extra_mode([arm::ArchExtraMode::MClass].iter().map(|x| *x)) .build() .unwrap(), Arch::ARM, Mode::Thumb, None, &[ExtraMode::MClass], &[("mrs", b"\xef\xf3\x02\x80")], ); test_arch_mode_endian_insns( &mut Capstone::new() .arm() .mode(arm::ArchMode::Arm) .extra_mode([arm::ArchExtraMode::V8].iter().map(|x| *x)) .build() .unwrap(), Arch::ARM, Mode::Arm, None, &[ExtraMode::V8], &[ ("vcvtt.f64.f16", b"\xe0\x3b\xb2\xee"), ("crc32b", b"\x42\x00\x01\xe1"), ("dmb", b"\x51\xf0\x7f\xf5"), ], ); } #[test] fn test_arch_arm_detail() { use crate::arch::arm::ArmOperandType::*; use crate::arch::arm::*; use capstone_sys::arm_op_mem; let r0_op = ArmOperand { op_type: Reg(RegId(ArmReg::ARM_REG_R0 as RegIdInt)), ..Default::default() }; test_arch_mode_endian_insns_detail( &mut Capstone::new() .arm() .mode(arm::ArchMode::Arm) .build() .unwrap(), Arch::ARM, Mode::Arm, Some(Endian::Little), &[], &[ // bl #0xfbc DII::new( "bl", b"\xed\xff\xff\xeb", &[ArmOperand { op_type: Imm(0xfbc), ..Default::default() }], ), // str lr, [sp, #-4]! DII::new( "str", b"\x04\xe0\x2d\xe5", &[ ArmOperand { op_type: Reg(RegId(ArmReg::ARM_REG_LR as RegIdInt)), ..Default::default() }, ArmOperand { op_type: Mem(ArmOpMem(arm_op_mem { base: ArmReg::ARM_REG_SP, index: 0, scale: 1, disp: -4, lshift: 0, })), ..Default::default() }, ], ), // andeq r0, r0, r0 DII::new( "andeq", b"\x00\x00\x00\x00", &[r0_op.clone(), r0_op.clone(), r0_op.clone()], ), // str r8, [r2, #-0x3e0]! DII::new( "str", b"\xe0\x83\x22\xe5", &[ ArmOperand { op_type: Reg(RegId(ArmReg::ARM_REG_R8 as RegIdInt)), ..Default::default() }, ArmOperand { op_type: Mem(ArmOpMem(arm_op_mem { base: ArmReg::ARM_REG_R2, index: 0, scale: 1, disp: -992, lshift: 0, })), ..Default::default() }, ], ), // mcreq p2, #0, r0, c3, c1, #7 DII::new( "mcreq", b"\xf1\x02\x03\x0e", &[ ArmOperand { op_type: Pimm(2), ..Default::default() }, ArmOperand { op_type: Imm(0), ..Default::default() }, r0_op.clone(), ArmOperand { op_type: Cimm(3), ..Default::default() }, ArmOperand { op_type: Cimm(1), ..Default::default() }, ArmOperand { op_type: Imm(7), ..Default::default() }, ], ), // mov r0, #0 DII::new( "mov", b"\x00\x00\xa0\xe3", &[ r0_op.clone(), ArmOperand { op_type: Imm(0), ..Default::default() }, ], ), ], ); test_arch_mode_endian_insns_detail( &mut Capstone::new() .arm() .mode(arm::ArchMode::Thumb) .build() .unwrap(), Arch::ARM, Mode::Thumb, None, &[], &[DII::new( "bx", b"\x70\x47", &[ArmOperand { op_type: Reg(RegId(ArmReg::ARM_REG_LR as RegIdInt)), ..Default::default() }], )], ); } #[test] fn test_arch_arm64() { test_arch_mode_endian_insns( &mut Capstone::new() .arm64() .mode(arm64::ArchMode::Arm) .build() .unwrap(), Arch::ARM64, Mode::Arm, None, &[], &[ ("mrs", b"\x09\x00\x38\xd5"), ("msr", b"\xbf\x40\x00\xd5"), ("msr", b"\x0c\x05\x13\xd5"), ("tbx", b"\x20\x50\x02\x0e"), ("scvtf", b"\x20\xe4\x3d\x0f"), ("fmla", b"\x00\x18\xa0\x5f"), ("fmov", b"\xa2\x00\xae\x9e"), ("dsb", b"\x9f\x37\x03\xd5"), ("dmb", b"\xbf\x33\x03\xd5"), ("isb", b"\xdf\x3f\x03\xd5"), ("mul", b"\x21\x7c\x02\x9b"), ("lsr", b"\x21\x7c\x00\x53"), ("sub", b"\x00\x40\x21\x4b"), ("ldr", b"\xe1\x0b\x40\xb9"), ("cneg", b"\x20\x04\x81\xda"), ("add", b"\x20\x08\x02\x8b"), ("ldr", b"\x10\x5b\xe8\x3c"), ], ); } #[test] fn test_arch_arm64_detail() { use crate::arch::arm64::Arm64OperandType::*; use crate::arch::arm64::Arm64Pstate::*; use crate::arch::arm64::Arm64Reg::*; use crate::arch::arm64::Arm64Sysreg::*; use crate::arch::arm64::Arm64Vas::*; use crate::arch::arm64::Arm64Vess::*; use crate::arch::arm64::*; use capstone_sys::arm64_op_mem; let s0 = Arm64Operand { op_type: Reg(RegId(ARM64_REG_S0 as RegIdInt)), ..Default::default() }; let x0 = Arm64Operand { op_type: Reg(RegId(ARM64_REG_X0 as RegIdInt)), ..Default::default() }; let x1 = Arm64Operand { op_type: Reg(RegId(ARM64_REG_X1 as RegIdInt)), ..Default::default() }; let x2 = Arm64Operand { op_type: Reg(RegId(ARM64_REG_X2 as RegIdInt)), ..Default::default() }; test_arch_mode_endian_insns_detail( &mut Capstone::new() .arm64() .mode(arm64::ArchMode::Arm) .build() .unwrap(), Arch::ARM64, Mode::Arm, None, &[], &[ // mrs x9, midr_el1 DII::new( "mrs", b"\x09\x00\x38\xd5", &[ Arm64Operand { op_type: Reg(RegId(ARM64_REG_X9 as RegIdInt)), ..Default::default() }, Arm64Operand { op_type: RegMrs(ARM64_SYSREG_MIDR_EL1), ..Default::default() }, ], ), // msr spsel, #0 DII::new( "msr", b"\xbf\x40\x00\xd5", &[ Arm64Operand { op_type: Pstate(ARM64_PSTATE_SPSEL), ..Default::default() }, Arm64Operand { op_type: Imm(0), ..Default::default() }, ], ), // tbx v0.8b, {v1.16b, v2.16b, v3.16b}, v2.8b DII::new( "tbx", b"\x20\x50\x02\x0e", &[ Arm64Operand { vas: ARM64_VAS_8B, op_type: Reg(RegId(ARM64_REG_V0 as RegIdInt)), ..Default::default() }, Arm64Operand { vas: ARM64_VAS_16B, op_type: Reg(RegId(ARM64_REG_V1 as RegIdInt)), ..Default::default() }, Arm64Operand { vas: ARM64_VAS_16B, op_type: Reg(RegId(ARM64_REG_V2 as RegIdInt)), ..Default::default() }, Arm64Operand { vas: ARM64_VAS_16B, op_type: Reg(RegId(ARM64_REG_V3 as RegIdInt)), ..Default::default() }, Arm64Operand { vas: ARM64_VAS_8B, op_type: Reg(RegId(ARM64_REG_V2 as RegIdInt)), ..Default::default() }, ], ), // scvtf v0.2s, v1.2s, #3 DII::new( "scvtf", b"\x20\xe4\x3d\x0f", &[ Arm64Operand { vas: ARM64_VAS_2S, op_type: Reg(RegId(ARM64_REG_V0 as RegIdInt)), ..Default::default() }, Arm64Operand { vas: ARM64_VAS_2S, op_type: Reg(RegId(ARM64_REG_V1 as RegIdInt)), ..Default::default() }, Arm64Operand { op_type: Imm(3), ..Default::default() }, ], ), // fmla s0, s0, v0.s[3] DII::new( "fmla", b"\x00\x18\xa0\x5f", &[ s0.clone(), s0.clone(), Arm64Operand { vector_index: Some(3), vess: ARM64_VESS_S, op_type: Reg(RegId(ARM64_REG_V0 as RegIdInt)), ..Default::default() }, ], ), // fmov x2, v5.d[1] DII::new( "fmov", b"\xa2\x00\xae\x9e", &[ Arm64Operand { op_type: Reg(RegId(ARM64_REG_X2 as RegIdInt)), ..Default::default() }, Arm64Operand { vector_index: Some(1), vess: ARM64_VESS_D, op_type: Reg(RegId(ARM64_REG_V5 as RegIdInt)), ..Default::default() }, ], ), // dsb nsh DII::new( "dsb", b"\x9f\x37\x03\xd5", &[Arm64Operand { op_type: Barrier(Arm64BarrierOp::ARM64_BARRIER_NSH), ..Default::default() }], ), // dmb osh DII::new( "dmb", b"\xbf\x33\x03\xd5", &[Arm64Operand { op_type: Barrier(Arm64BarrierOp::ARM64_BARRIER_OSH), ..Default::default() }], ), // isb DII::new("isb", b"\xdf\x3f\x03\xd5", &[]), // mul x1, x1, x2 DII::new( "mul", b"\x21\x7c\x02\x9b", &[x1.clone(), x1.clone(), x2.clone()], ), // lsr w1, w1, #0 DII::new( "lsr", b"\x21\x7c\x00\x53", &[ Arm64Operand { op_type: Reg(RegId(ARM64_REG_W1 as RegIdInt)), ..Default::default() }, Arm64Operand { op_type: Reg(RegId(ARM64_REG_W1 as RegIdInt)), ..Default::default() }, Arm64Operand { op_type: Imm(0), ..Default::default() }, ], ), // sub w0, w0, w1, uxtw DII::new( "sub", b"\x00\x40\x21\x4b", &[ Arm64Operand { op_type: Reg(RegId(ARM64_REG_W0 as RegIdInt)), ..Default::default() }, Arm64Operand { op_type: Reg(RegId(ARM64_REG_W0 as RegIdInt)), ..Default::default() }, Arm64Operand { ext: Arm64Extender::ARM64_EXT_UXTW, op_type: Reg(RegId(ARM64_REG_W1 as RegIdInt)), ..Default::default() }, ], ), // ldr w1, [sp, #8] DII::new( "ldr", b"\xe1\x0b\x40\xb9", &[ Arm64Operand { op_type: Reg(RegId(ARM64_REG_W1 as RegIdInt)), ..Default::default() }, Arm64Operand { op_type: Mem(Arm64OpMem(arm64_op_mem { base: ARM64_REG_SP, index: 0, disp: 8, })), ..Default::default() }, ], ), // cneg x0, x1, ne DII::new("cneg", b"\x20\x04\x81\xda", &[x0.clone(), x1.clone()]), // add x0, x1, x2, lsl #2 DII::new( "add", b"\x20\x08\x02\x8b", &[ x0.clone(), x1.clone(), Arm64Operand { shift: Arm64Shift::Lsl(2), ..x2 }, ], ), // ldr q16, [x24, w8, uxtw #4] DII::new( "ldr", b"\x10\x5b\xe8\x3c", &[ Arm64Operand { op_type: Reg(RegId(ARM64_REG_Q16 as RegIdInt)), ..Default::default() }, Arm64Operand { shift: Arm64Shift::Lsl(4), ext: Arm64Extender::ARM64_EXT_UXTW, op_type: Mem(Arm64OpMem(arm64_op_mem { base: ARM64_REG_X24, index: ARM64_REG_W8, disp: 0, })), ..Default::default() }, ], ), ], ); } #[test] fn test_arch_evm() { test_arch_mode_endian_insns( &mut Capstone::new() .evm() .mode(evm::ArchMode::Default) .build() .unwrap(), Arch::EVM, Mode::Default, None, &[], &[("push1", b"\x60\x61"), ("pop", b"\x50")], ); } #[test] fn test_arch_evm_detail() { let ops: &[arch::m68k::M68kOperand] = &[]; test_arch_mode_endian_insns_detail( &mut Capstone::new() .evm() .mode(evm::ArchMode::Default) .build() .unwrap(), Arch::EVM, Mode::Default, None, &[], &[DII::new("push1", b"\x60\x61", ops)], ); } #[test] fn test_arch_m680x_detail() { use crate::arch::m680x::M680xOperandType::*; use crate::arch::m680x::M680xReg::*; use crate::arch::m680x::*; use capstone_sys::m680x_op_idx; let op_idx_zero = m680x_op_idx { base_reg: M680X_REG_INVALID, offset_reg: M680X_REG_INVALID, offset: 0, offset_addr: 0, offset_bits: 0, inc_dec: 0, flags: 0, }; test_arch_mode_endian_insns_detail( &mut Capstone::new() .m680x() .mode(m680x::ArchMode::M680x6301) .build() .unwrap(), Arch::M680X, Mode::M680x6301, None, &[], &[ // tim #16;0,x DII::new( "tim", b"\x6b\x10\x00", &[ M680xOperand { op_type: Imm(16), size: 1, }, M680xOperand { op_type: Indexed(M680xOpIdx(m680x_op_idx { base_reg: M680X_REG_X, offset_bits: 8, ..op_idx_zero })), size: 1, }, ], ), // aim #16,$00 DII::new( "aim", b"\x71\x10\x00", &[ M680xOperand { op_type: Imm(16), size: 1, }, M680xOperand { op_type: Direct { direct_addr: 0 }, size: 1, }, ], ), // oim #16,$10 DII::new( "oim", b"\x72\x10\x10", &[ M680xOperand { op_type: Imm(16), size: 1, }, M680xOperand { op_type: Direct { direct_addr: 0x10 }, size: 1, }, ], ), // rts DII::new("rts", b"\x39", &[]), ], ); test_arch_mode_endian_insns_detail( &mut Capstone::new() .m680x() .mode(m680x::ArchMode::M680x6309) .build() .unwrap(), Arch::M680X, Mode::M680x6309, None, &[], &[ // oim #16,$10 DII::new( "oim", b"\x01\x10\x10", &[ M680xOperand { op_type: Imm(16), size: 1, }, M680xOperand { op_type: Direct { direct_addr: 0x10 }, size: 1, }, ], ), // aim #16;-16,x DII::new( "aim", b"\x62\x10\x10", &[ M680xOperand { op_type: Imm(16), size: 1, }, M680xOperand { op_type: Indexed(M680xOpIdx(m680x_op_idx { base_reg: M680X_REG_X, offset: -16, offset_bits: 5, offset_addr: START_TEST_ADDR as u16 - 10, ..op_idx_zero })), size: 1, }, ], ), // tim #16,$1000 DII::new( "tim", b"\x7b\x10\x10\x00", &[ M680xOperand { op_type: Imm(16), size: 1, }, M680xOperand { op_type: Extended { address: 0x1000, indirect: false, }, size: 1, }, ], ), // ldq #1234567890 DII::new( "ldq", b"\xcd\x49\x96\x02\xd2", &[ M680xOperand { op_type: Reg(RegId(M680X_REG_Q as RegIdInt)), size: 4, }, M680xOperand { op_type: Imm(1234567890), size: 4, }, ], ), // addr y,u DII::new( "addr", b"\x10\x30\x23", &[ M680xOperand { op_type: Reg(RegId(M680X_REG_Y as RegIdInt)), size: 2, }, M680xOperand { op_type: Reg(RegId(M680X_REG_U as RegIdInt)), size: 2, }, ], ), // pshsw DII::new( "pshsw", b"\x10\x38", &[ M680xOperand { op_type: Reg(RegId(M680X_REG_S as RegIdInt)), size: 2, }, M680xOperand { op_type: Reg(RegId(M680X_REG_W as RegIdInt)), size: 2, }, ], ), // puluw DII::new( "puluw", b"\x10\x3b", &[ M680xOperand { op_type: Reg(RegId(M680X_REG_U as RegIdInt)), size: 2, }, M680xOperand { op_type: Reg(RegId(M680X_REG_W as RegIdInt)), size: 2, }, ], ), // comw DII::new( "comw", b"\x10\x53", &[M680xOperand { op_type: Reg(RegId(M680X_REG_W as RegIdInt)), size: 2, }], ), // tstw DII::new( "tstw", b"\x10\x5d", &[M680xOperand { op_type: Reg(RegId(M680X_REG_W as RegIdInt)), size: 2, }], ), // band a,0,3,$10 DII::new( "band", b"\x11\x30\x43\x10", &[ M680xOperand { op_type: Reg(RegId(M680X_REG_A as RegIdInt)), size: 1, }, M680xOperand { op_type: Constant(0), size: 0, }, M680xOperand { op_type: Constant(3), size: 0, }, M680xOperand { op_type: Direct { direct_addr: 0x10 }, size: 1, }, ], ), // stbt cc,4,5,$10 DII::new( "stbt", b"\x11\x37\x25\x10", &[ M680xOperand { op_type: Reg(RegId(M680X_REG_CC as RegIdInt)), size: 1, }, M680xOperand { op_type: Constant(4), size: 0, }, M680xOperand { op_type: Constant(5), size: 0, }, M680xOperand { op_type: Direct { direct_addr: 0x10 }, size: 1, }, ], ), // tfm x+,y+ DII::new( "tfm", b"\x11\x38\x12", &[ M680xOperand { op_type: Indexed(M680xOpIdx(m680x_op_idx { base_reg: M680X_REG_X, inc_dec: 1, flags: 6, ..op_idx_zero })), size: 1, }, M680xOperand { op_type: Indexed(M680xOpIdx(m680x_op_idx { base_reg: M680X_REG_Y, inc_dec: 1, flags: 6, ..op_idx_zero })), size: 1, }, ], ), ], ); let empty_ops: &[M680xOperand] = &[]; test_arch_mode_endian_insns_detail( &mut Capstone::new() .m680x() .mode(m680x::ArchMode::M680x6800) .build() .unwrap(), Arch::M680X, Mode::M680x6800, None, &[], &[ // nop DII::new("nop", b"\x01", empty_ops), // dex DII::new( "dex", b"\x09", &[M680xOperand { op_type: Reg(RegId(M680X_REG_X as RegIdInt)), size: 2, }], ), // psha DII::new( "psha", b"\x36", &[M680xOperand { op_type: Reg(RegId(M680X_REG_A as RegIdInt)), size: 1, }], ), // lsr 127,x DII::new( "lsr", b"\x64\x7f", &[M680xOperand { op_type: Indexed(M680xOpIdx(m680x_op_idx { base_reg: M680X_REG_X, offset: 127, offset_bits: 8, ..op_idx_zero })), size: 1, }], ), // lsr $1000 DII::new( "lsr", b"\x74\x10\x00", &[M680xOperand { op_type: Extended { address: 0x1000, indirect: false, }, size: 1, }], ), ], ); test_arch_mode_endian_insns_detail( &mut Capstone::new() .m680x() .mode(m680x::ArchMode::M680x6801) .build() .unwrap(), Arch::M680X, Mode::M680x6801, None, &[], &[ // lsrd DII::new( "lsrd", b"\x04", &[M680xOperand { op_type: Reg(RegId(M680X_REG_D as RegIdInt)), size: 2, }], ), // asld DII::new( "asld", b"\x05", &[M680xOperand { op_type: Reg(RegId(M680X_REG_D as RegIdInt)), size: 2, }], ), ], ); } #[test] fn test_arch_m68k_detail() { use crate::arch::m68k::M68kOperand::*; use crate::arch::m68k::M68kReg::*; use crate::arch::m68k::*; use capstone_sys::m68k_address_mode::*; use capstone_sys::m68k_op_mem; let mem_zero = m68k_op_mem { base_reg: M68K_REG_INVALID, index_reg: M68K_REG_INVALID, in_base_reg: M68K_REG_INVALID, in_disp: 0, out_disp: 0, disp: 0, scale: 0, bitfield: 0, width: 0, offset: 0, index_size: 0, }; test_arch_mode_endian_insns_detail( &mut Capstone::new() .m68k() .mode(m68k::ArchMode::M68k040) .build() .unwrap(), Arch::M68K, Mode::M68k040, Some(Endian::Big), &[], &[ // mulu.l d0, d4:d5 DII::new( "mulu.l", b"\x4c\x00\x54\x04", &[ Reg(RegId(M68K_REG_D0 as RegIdInt)), RegPair( RegId(M68K_REG_D4 as RegIdInt), RegId(M68K_REG_D5 as RegIdInt), ), ], ), // movem.l d0-d2/a2-a3, -(a7) DII::new( "movem.l", b"\x48\xe7\xe0\x30", &[ RegBits( M68kRegisterBits::from_register_iter( [ M68K_REG_D0, M68K_REG_D1, M68K_REG_D2, M68K_REG_A2, M68K_REG_A3, ] .iter() .map(|x| *x), ) .unwrap(), ), Mem(M68kOpMem { op_mem: mem_zero, address_mode: M68K_AM_REGI_ADDR_PRE_DEC, extra_info: M68kOpMemExtraInfo::Reg(RegId(M68K_REG_A7 as RegIdInt)), }), ], ), // movem.l (a7)+, d0-d2/a2-a3 DII::new( "movem.l", b"\x4c\xdf\x0c\x07", &[ Mem(M68kOpMem { op_mem: mem_zero, address_mode: M68K_AM_REGI_ADDR_POST_INC, extra_info: M68kOpMemExtraInfo::Reg(RegId(M68K_REG_A7 as RegIdInt)), }), RegBits( M68kRegisterBits::from_register_iter( [ M68K_REG_D0, M68K_REG_D1, M68K_REG_D2, M68K_REG_A2, M68K_REG_A3, ] .iter() .map(|x| *x), ) .unwrap(), ), ], ), // add.w d0, d2 DII::new( "add.w", b"\xd4\x40", &[ Reg(RegId(M68K_REG_D0 as RegIdInt)), Reg(RegId(M68K_REG_D2 as RegIdInt)), ], ), // or.w d3, (a2)+ DII::new( "or.w", b"\x87\x5a", &[ Reg(RegId(M68K_REG_D3 as RegIdInt)), Mem(M68kOpMem { op_mem: mem_zero, address_mode: M68K_AM_REGI_ADDR_POST_INC, extra_info: M68kOpMemExtraInfo::Reg(RegId(M68K_REG_A2 as RegIdInt)), }), ], ), // nop DII::new("nop", b"\x4e\x71", &[]), // andi.l #$c0dec0de, (a4, d5.l * 4) DII::new( "andi.l", b"\x02\xb4\xc0\xde\xc0\xde\x5c\x00", &[ Imm(0xc0dec0de), Mem(M68kOpMem { op_mem: m68k_op_mem { base_reg: M68K_REG_A4, index_reg: M68K_REG_D5, index_size: 1, // l scale: 4, ..mem_zero }, address_mode: M68K_AM_AREGI_INDEX_BASE_DISP, extra_info: M68kOpMemExtraInfo::None, }), ], ), // move.b d0, ([a6, d7.w], $123) DII::new( "move.b", b"\x1d\x80\x71\x12\x01\x23", &[ Reg(RegId(M68K_REG_D0 as RegIdInt)), Mem(M68kOpMem { op_mem: m68k_op_mem { base_reg: M68K_REG_A6, index_reg: M68K_REG_D7, out_disp: 0x123, // $123 treated as hex index_size: 0, // w ..mem_zero }, address_mode: M68K_AM_MEMI_PRE_INDEX, extra_info: M68kOpMemExtraInfo::None, }), ], ), // fadd.s #3.141500, fp0 DII::new( "fadd.s", b"\xf2\x3c\x44\x22\x40\x49\x0e\x56", &[FpSingle(3.1415), Reg(RegId(M68K_REG_FP0 as RegIdInt))], ), // scc.b d5 DII::new("scc.b", b"\x54\xc5", &[Reg(RegId(M68K_REG_D5 as RegIdInt))]), // fmove.s #1000.000000, fp0 DII::new( "fmove.s", b"\xf2\x3c\x44\x00\x44\x7a\x00\x00", &[FpSingle(1000.000000), Reg(RegId(M68K_REG_FP0 as RegIdInt))], ), // fsub fp2, fp4 DII::new( "fsub", b"\xf2\x00\x0a\x28", &[ Reg(RegId(M68K_REG_FP2 as RegIdInt)), Reg(RegId(M68K_REG_FP4 as RegIdInt)), ], ), // jsr $12.l DII::new( "jsr", b"\x4e\xb9\x00\x00\x00\x12", &[Mem(M68kOpMem { op_mem: m68k_op_mem { ..mem_zero }, address_mode: M68K_AM_ABSOLUTE_DATA_LONG, extra_info: M68kOpMemExtraInfo::Imm(0x12), })], ), // rts DII::new("rts", b"\x4e\x75", &[]), ], ); } #[test] fn test_arch_mips() { test_arch_mode_endian_insns( &mut Capstone::new() .mips() .mode(mips::ArchMode::Mips32R6) .build() .unwrap(), Arch::MIPS, Mode::Mips32R6, Some(Endian::Little), &[], &[("ori", b"\x56\x34\x21\x34"), ("srl", b"\xc2\x17\x01\x00")], ); test_arch_mode_endian_insns( &mut Capstone::new() .mips() .mode(mips::ArchMode::Mips32R6) .endian(Endian::Big) .build() .unwrap(), Arch::MIPS, Mode::Mips32R6, Some(Endian::Big), &[], &[ ("ori", b"\x34\x21\x34\x56"), ("jal", b"\x0C\x10\x00\x97"), ("nop", b"\x00\x00\x00\x00"), ("addiu", b"\x24\x02\x00\x0c"), ("lw", b"\x8f\xa2\x00\x00"), ("ori", b"\x34\x21\x34\x56"), ], ); test_arch_mode_endian_insns( &mut Capstone::new() .mips() .mode(mips::ArchMode::Mips32R6) .extra_mode([mips::ArchExtraMode::Micro].iter().map(|x| *x)) .endian(Endian::Big) .build() .unwrap(), Arch::MIPS, Mode::Mips32R6, Some(Endian::Big), &[ExtraMode::Micro], &[ ("break", b"\x00\x07\x00\x07"), ("wait", b"\x00\x11\x93\x7c"), ("syscall", b"\x01\x8c\x8b\x7c"), ("rotrv", b"\x00\xc7\x48\xd0"), ], ); } #[test] fn test_arch_mips_detail() { use crate::arch::mips::MipsOperand::*; use crate::arch::mips::*; use capstone_sys::mips_op_mem; test_arch_mode_endian_insns_detail( &mut Capstone::new() .mips() .mode(mips::ArchMode::Mips32R6) .build() .unwrap(), Arch::MIPS, Mode::Mips32R6, Some(Endian::Little), &[], &[ DII::new( "ori", b"\x56\x34\x21\x34", &[Reg(RegId(3)), Reg(RegId(3)), Imm(13398)], ), DII::new( "srl", b"\xc2\x17\x01\x00", &[Reg(RegId(4)), Reg(RegId(3)), Imm(31)], ), DII::new("syscall", b"\x0c\x00\x00\x00", &[]), ], ); test_arch_mode_endian_insns_detail( &mut Capstone::new() .mips() .mode(mips::ArchMode::Mips32R6) .endian(Endian::Big) .build() .unwrap(), Arch::MIPS, Mode::Mips32R6, Some(Endian::Big), &[], &[DII::new( "lw", b"\x8f\xa2\x00\x00", &[ Reg(RegId(MipsReg::MIPS_REG_V0 as RegIdInt)), Mem(MipsOpMem(mips_op_mem { base: MipsReg::MIPS_REG_SP, disp: 0, })), ], )], ); } #[test] fn test_arch_ppc() { test_arch_mode_endian_insns( &mut Capstone::new() .ppc() .mode(ppc::ArchMode::Mode32) .endian(Endian::Big) .build() .unwrap(), Arch::PPC, // Mode::Mode32, Mode::Default, Some(Endian::Big), &[], &[ ("bdnzla+", b"\x43\x20\x0c\x07"), ("bdztla", b"\x41\x56\xff\x17"), ("lwz", b"\x80\x20\x00\x00"), ("lwz", b"\x80\x3f\x00\x00"), ("vpkpx", b"\x10\x43\x23\x0e"), ("stfs", b"\xd0\x44\x00\x80"), ("crand", b"\x4c\x43\x22\x02"), ("cmpwi", b"\x2d\x03\x00\x80"), ("addc", b"\x7c\x43\x20\x14"), ("mulhd.", b"\x7c\x43\x20\x93"), ("bdnzlrl+", b"\x4f\x20\x00\x21"), ("bgelrl-", b"\x4c\xc8\x00\x21"), ("bne", b"\x40\x82\x00\x14"), ], ); } #[test] fn test_arch_ppc_detail() { use crate::arch::ppc::PpcOperand::*; use crate::arch::ppc::PpcReg::*; use crate::arch::ppc::*; use capstone_sys::ppc_op_mem; test_arch_mode_endian_insns_detail( &mut Capstone::new() .ppc() .mode(ppc::ArchMode::Mode64) .endian(Endian::Big) .build() .unwrap(), Arch::PPC, Mode::Mode64, Some(Endian::Big), &[], &[ // lwz r1, 0(0) DII::new( "lwz", b"\x80\x20\x00\x00", &[ Reg(RegId(PPC_REG_R1 as RegIdInt)), Mem(PpcOpMem(ppc_op_mem { base: 44, disp: 0 })), ], ), // lwz r1, 0(r31) DII::new( "lwz", b"\x80\x3f\x00\x00", &[ Reg(RegId(PPC_REG_R1 as RegIdInt)), Mem(PpcOpMem(ppc_op_mem { base: PPC_REG_R31, disp: 0, })), ], ), // vpkpx v2, v3, v4 DII::new( "vpkpx", b"\x10\x43\x23\x0e", &[ Reg(RegId(PPC_REG_V2 as RegIdInt)), Reg(RegId(PPC_REG_V3 as RegIdInt)), Reg(RegId(PPC_REG_V4 as RegIdInt)), ], ), // stfs f2, 0x80(r4) DII::new( "stfs", b"\xd0\x44\x00\x80", &[ Reg(RegId(PPC_REG_F2 as RegIdInt)), Mem(PpcOpMem(ppc_op_mem { base: PPC_REG_R4, disp: 0x80, })), ], ), // crand 2, 3, 4 DII::new( "crand", b"\x4c\x43\x22\x02", &[ Reg(RegId(PPC_REG_R2 as RegIdInt)), Reg(RegId(PPC_REG_R3 as RegIdInt)), Reg(RegId(PPC_REG_R4 as RegIdInt)), ], ), // cmpwi cr2, r3, 0x80 DII::new( "cmpwi", b"\x2d\x03\x00\x80", &[ Reg(RegId(PPC_REG_CR2 as RegIdInt)), Reg(RegId(PPC_REG_R3 as RegIdInt)), Imm(0x80), ], ), // addc r2, r3, r4 DII::new( "addc", b"\x7c\x43\x20\x14", &[ Reg(RegId(PPC_REG_R2 as RegIdInt)), Reg(RegId(PPC_REG_R3 as RegIdInt)), Reg(RegId(PPC_REG_R4 as RegIdInt)), ], ), // mulhd. r2, r3, r4 DII::new( "mulhd.", b"\x7c\x43\x20\x93", &[ Reg(RegId(PPC_REG_R2 as RegIdInt)), Reg(RegId(PPC_REG_R3 as RegIdInt)), Reg(RegId(PPC_REG_R4 as RegIdInt)), ], ), // bdnzlrl+ DII::new("bdnzlrl+", b"\x4f\x20\x00\x21", &[]), // bgelrl- cr2 DII::new( "bgelrl-", b"\x4c\xc8\x00\x21", &[Reg(RegId(PPC_REG_CR2 as RegIdInt))], ), ], ); } #[test] fn test_arch_sparc() { test_arch_mode_endian_insns( &mut Capstone::new() .sparc() .mode(sparc::ArchMode::Default) .build() .unwrap(), Arch::SPARC, Mode::Default, None, &[], &[ ("cmp", b"\x80\xa0\x40\x02"), ("jmpl", b"\x85\xc2\x60\x08"), ("restore", b"\x85\xe8\x20\x01"), ("restore", b"\x81\xe8\x00\x00"), ("mov", b"\x90\x10\x20\x01"), ("casx", b"\xd5\xf6\x10\x16"), ("sethi", b"\x21\x00\x00\x0a"), ("add", b"\x86\x00\x40\x02"), ("nop", b"\x01\x00\x00\x00"), ("bne", b"\x12\xbf\xff\xff"), ("ba", b"\x10\xbf\xff\xff"), ("add", b"\xa0\x02\x00\x09"), ("fbg", b"\x0d\xbf\xff\xff"), ("st", b"\xd4\x20\x60\x00"), ("ldsb", b"\xd4\x4e\x00\x16"), ("brnz,a,pn", b"\x2a\xc2\x80\x03"), ], ); test_arch_mode_endian_insns( &mut Capstone::new() .sparc() .mode(sparc::ArchMode::V9) .build() .unwrap(), Arch::SPARC, Mode::V9, Some(Endian::Big), &[], &[ ("fcmps", b"\x81\xa8\x0a\x24"), ("fstox", b"\x89\xa0\x10\x20"), ("fqtoi", b"\x89\xa0\x1a\x60"), ("fnegq", b"\x89\xa0\x00\xe0"), ], ); } #[test] fn test_arch_sparc_detail() { use crate::arch::sparc::SparcOperand::*; use crate::arch::sparc::SparcReg::*; use crate::arch::sparc::*; use capstone_sys::sparc_op_mem; test_arch_mode_endian_insns_detail( &mut Capstone::new() .sparc() .mode(sparc::ArchMode::Default) .build() .unwrap(), Arch::SPARC, Mode::Default, None, &[], &[ // cmp %g1, %g2 DII::new( "cmp", b"\x80\xa0\x40\x02", &[ Reg(RegId(SPARC_REG_G1 as RegIdInt)), Reg(RegId(SPARC_REG_G2 as RegIdInt)), ], ), // jmpl %o1+8, %g2 DII::new( "jmpl", b"\x85\xc2\x60\x08", &[ Mem(SparcOpMem(sparc_op_mem { base: SPARC_REG_O1 as u8, index: 0, disp: 8, })), Reg(RegId(SPARC_REG_G2 as RegIdInt)), ], ), // restore %g0, 1, %g2 DII::new( "restore", b"\x85\xe8\x20\x01", &[ Reg(RegId(SPARC_REG_G0 as RegIdInt)), Imm(1), Reg(RegId(SPARC_REG_G2 as RegIdInt)), ], ), // mov 1, %o0 DII::new( "mov", b"\x90\x10\x20\x01", &[Imm(1), Reg(RegId(SPARC_REG_O0 as RegIdInt))], ), // casx [%i0], %l6, %o2 DII::new( "casx", b"\xd5\xf6\x10\x16", &[ Mem(SparcOpMem(sparc_op_mem { base: SPARC_REG_I0 as u8, index: 0, disp: 0, })), Reg(RegId(SPARC_REG_L6 as RegIdInt)), Reg(RegId(SPARC_REG_O2 as RegIdInt)), ], ), // sethi 0xa, %l0 DII::new( "sethi", b"\x21\x00\x00\x0a", &[Imm(0xa), Reg(RegId(SPARC_REG_L0 as RegIdInt))], ), // add %g1, %g2, %g3 DII::new( "add", b"\x86\x00\x40\x02", &[ Reg(RegId(SPARC_REG_G1 as RegIdInt)), Reg(RegId(SPARC_REG_G2 as RegIdInt)), Reg(RegId(SPARC_REG_G3 as RegIdInt)), ], ), // nop DII::new("nop", b"\x01\x00\x00\x00", &[]), // bne 0x1020 DII::new("bne", b"\x12\xbf\xff\xff", &[Imm(0x101c)]), // ba 0x1024 DII::new("ba", b"\x10\xbf\xff\xff", &[Imm(0x1020)]), // add %o0, %o1, %l0 DII::new( "add", b"\xa0\x02\x00\x09", &[ Reg(RegId(SPARC_REG_O0 as RegIdInt)), Reg(RegId(SPARC_REG_O1 as RegIdInt)), Reg(RegId(SPARC_REG_L0 as RegIdInt)), ], ), // fbg 0x102c DII::new("fbg", b"\x0d\xbf\xff\xff", &[Imm(0x1028)]), // st %o2, [%g1] DII::new( "st", b"\xd4\x20\x60\x00", &[ Reg(RegId(SPARC_REG_O2 as RegIdInt)), Mem(SparcOpMem(sparc_op_mem { base: SPARC_REG_G1 as u8, index: 0, disp: 0, })), ], ), // ldsb [%i0+%l6], %o2 DII::new( "ldsb", b"\xd4\x4e\x00\x16", &[ Mem(SparcOpMem(sparc_op_mem { base: SPARC_REG_I0 as u8, index: SPARC_REG_L6 as u8, disp: 0, })), Reg(RegId(SPARC_REG_O2 as RegIdInt)), ], ), // brnz,a,pn %o2, 0x1048 DII::new( "brnz,a,pn", b"\x2a\xc2\x80\x03", &[Reg(RegId(SPARC_REG_O2 as RegIdInt)), Imm(0x1044)], ), ], ); let f0_f4 = [ Reg(RegId(SPARC_REG_F0 as RegIdInt)), Reg(RegId(SPARC_REG_F4 as RegIdInt)), ]; test_arch_mode_endian_insns_detail( &mut Capstone::new() .sparc() .mode(sparc::ArchMode::V9) .build() .unwrap(), Arch::SPARC, Mode::V9, None, &[], &[ // fcmps %f0, %f4 DII::new("fcmps", b"\x81\xa8\x0a\x24", &f0_f4), // fstox %f0, %f4 DII::new("fstox", b"\x89\xa0\x10\x20", &f0_f4), // fqtoi %f0, %f4 DII::new("fqtoi", b"\x89\xa0\x1a\x60", &f0_f4), // fnegq %f0, %f4 DII::new("fnegq", b"\x89\xa0\x00\xe0", &f0_f4), ], ); } #[test] fn test_arch_systemz() { test_arch_mode_endian_insns( &mut Capstone::new() .sysz() .mode(sysz::ArchMode::Default) .build() .unwrap(), Arch::SYSZ, Mode::Default, None, &[], &[ ("adb", b"\xed\x00\x00\x00\x00\x1a"), ("a", b"\x5a\x0f\x1f\xff"), ("afi", b"\xc2\x09\x80\x00\x00\x00"), ("br", b"\x07\xf7"), ("xiy", b"\xeb\x2a\xff\xff\x7f\x57"), ("xy", b"\xe3\x01\xff\xff\x7f\x57"), ("stmg", b"\xeb\x00\xf0\x00\x00\x24"), ("ear", b"\xb2\x4f\x00\x78"), ("clije", b"\xec\x18\x00\x00\xc1\x7f"), ], ); } #[test] fn test_arch_tms320c64x_detail() { use crate::arch::tms320c64x::Tms320c64xOperand::*; use crate::arch::tms320c64x::Tms320c64xReg::*; use crate::arch::tms320c64x::*; use capstone_sys::tms320c64x_funit::*; use capstone_sys::tms320c64x_mem_dir::*; use capstone_sys::tms320c64x_mem_disp::*; use capstone_sys::tms320c64x_mem_mod::*; use capstone_sys::tms320c64x_op_mem; test_arch_mode_endian_insns_detail( &mut Capstone::new() .tms320c64x() .mode(tms320c64x::ArchMode::Default) .build() .unwrap(), Arch::TMS320C64X, Mode::Default, None, &[], &[ // add.D1 a11, a4, a3 DII::new( "add.D1", b"\x01\xac\x88\x40", &[ Reg(RegId(TMS320C64X_REG_A11 as RegIdInt)), Reg(RegId(TMS320C64X_REG_A4 as RegIdInt)), Reg(RegId(TMS320C64X_REG_A3 as RegIdInt)), ], ), // [ a1] add.D2 b11, b4, b3 || DII::new( "[ a1] add.D2", b"\x81\xac\x88\x43", &[ Reg(RegId(TMS320C64X_REG_B11 as RegIdInt)), Reg(RegId(TMS320C64X_REG_B4 as RegIdInt)), Reg(RegId(TMS320C64X_REG_B3 as RegIdInt)), ], ), // ldbu.D2T2 *+b15[0x46], b5 DII::new( "ldbu.D2T2", b"\x02\x80\x46\x9e", &[ Mem(Tms320c64xOpMem(tms320c64x_op_mem { base: TMS320C64X_REG_B15, disp: 0x46, unit: TMS320C64X_FUNIT_L as c_uint, scaled: false as c_uint, disptype: TMS320C64X_MEM_DISP_CONSTANT as c_uint, direction: TMS320C64X_MEM_DIR_FW as c_uint, modify: TMS320C64X_MEM_MOD_NO as c_uint, })), Reg(RegId(TMS320C64X_REG_B5 as RegIdInt)), ], ), // NOP DII::new("NOP", b"\x00\x00\x00\x00", &[]), // ldbu.D1T2 *++a4[1], b5 DII::new( "ldbu.D1T2", b"\x02\x90\x32\x96", &[ Mem(Tms320c64xOpMem(tms320c64x_op_mem { base: TMS320C64X_REG_A4, disp: 0x1, unit: TMS320C64X_FUNIT_L as c_uint, scaled: true as c_uint, disptype: TMS320C64X_MEM_DISP_CONSTANT as c_uint, direction: TMS320C64X_MEM_DIR_FW as c_uint, modify: TMS320C64X_MEM_MOD_PRE as c_uint, })), Reg(RegId(TMS320C64X_REG_B5 as RegIdInt)), ], ), // ldbu.D2T2 *+b15[0x46], b5 DII::new( "ldbu.D2T2", b"\x02\x80\x46\x9e", &[ Mem(Tms320c64xOpMem(tms320c64x_op_mem { base: TMS320C64X_REG_B15, disp: 0x46, unit: TMS320C64X_FUNIT_L as c_uint, scaled: false as c_uint, disptype: TMS320C64X_MEM_DISP_CONSTANT as c_uint, direction: TMS320C64X_MEM_DIR_FW as c_uint, modify: TMS320C64X_MEM_MOD_NO as c_uint, })), Reg(RegId(TMS320C64X_REG_B5 as RegIdInt)), ], ), // lddw.D1T2 *+a15[4], b11:b10 DII::new( "lddw.D1T2", b"\x05\x3c\x83\xe6", &[ Mem(Tms320c64xOpMem(tms320c64x_op_mem { base: TMS320C64X_REG_A15, disp: 0x4, unit: TMS320C64X_FUNIT_L as c_uint, scaled: true as c_uint, disptype: TMS320C64X_MEM_DISP_CONSTANT as c_uint, direction: TMS320C64X_MEM_DIR_FW as c_uint, modify: TMS320C64X_MEM_MOD_NO as c_uint, })), RegPair( RegId(TMS320C64X_REG_B11 as RegIdInt), RegId(TMS320C64X_REG_B10 as RegIdInt), ), ], ), // ldndw.D1T1 *+a3(a4), a23:a22 DII::new( "ldndw.D1T1", b"\x0b\x0c\x8b\x24", &[ Mem(Tms320c64xOpMem(tms320c64x_op_mem { base: TMS320C64X_REG_A3, disp: TMS320C64X_REG_A4 as c_uint, unit: TMS320C64X_FUNIT_D as c_uint, scaled: false as c_uint, disptype: TMS320C64X_MEM_DISP_REGISTER as c_uint, direction: TMS320C64X_MEM_DIR_FW as c_uint, modify: TMS320C64X_MEM_MOD_NO as c_uint, })), RegPair( RegId(TMS320C64X_REG_A23 as RegIdInt), RegId(TMS320C64X_REG_A22 as RegIdInt), ), ], ), ], ); } #[test] fn test_arch_x86() { test_arch_mode_endian_insns( &mut Capstone::new() .x86() .mode(x86::ArchMode::Mode16) .build() .unwrap(), Arch::X86, Mode::Mode16, None, &[], &[ ("lea", b"\x8d\x4c\x32"), ("or", b"\x08\x01"), ("fadd", b"\xd8\x81\xc6\x34"), ("adc", b"\x12\x00"), ("add", b"\x00\x05"), ("and", b"\x23\x01"), ("add", b"\x00\x00"), ("mov", b"\x36\x8b\x84\x91\x23"), ("add", b"\x01\x00"), ("add", b"\x00\x41\x8d"), ("test", b"\x84\x39"), ("mov", b"\x89\x67\x00"), ("add", b"\x00\x8d\x87\x89"), ("add", b"\x67\x00\x00"), ("mov", b"\xb4\xc6"), ], ); test_arch_mode_endian_insns( &mut Capstone::new() .x86() .mode(x86::ArchMode::Mode32) .build() .unwrap(), Arch::X86, Mode::Mode32, None, &[], &[ ("lea", b"\x8d\x4c\x32\x08"), ("add", b"\x01\xd8"), ("add", b"\x81\xc6\x34\x12\x00\x00"), ("add", b"\x05\x23\x01\x00\x00"), ("mov", b"\x36\x8b\x84\x91\x23\x01\x00\x00"), ("inc", b"\x41"), ("lea", b"\x8d\x84\x39\x89\x67\x00\x00"), ("lea", b"\x8d\x87\x89\x67\x00\x00"), ("mov", b"\xb4\xc6"), ], ); test_arch_mode_endian_insns( &mut Capstone::new() .x86() .mode(x86::ArchMode::Mode64) .build() .unwrap(), Arch::X86, Mode::Mode64, None, &[], &[("push", b"\x55"), ("mov", b"\x48\x8b\x05\xb8\x13\x00\x00")], ); } #[test] fn test_arch_x86_detail() { use crate::arch::x86::X86OperandType::*; use crate::arch::x86::X86Reg::*; use crate::arch::x86::*; use capstone_sys::*; // X86 16bit (Intel syntax) test_arch_mode_endian_insns_detail( &mut Capstone::new() .x86() .mode(x86::ArchMode::Mode16) .build() .unwrap(), Arch::X86, Mode::Mode16, None, &[], &[ // lea cx, word ptr [si + 0x32] DII::new( "lea", b"\x8d\x4c\x32", &[ X86Operand { size: 2, access: Some(RegAccessType::WriteOnly), op_type: Reg(RegId(X86_REG_CX as RegIdInt)), ..Default::default() }, X86Operand { size: 2, access: Some(RegAccessType::ReadOnly), op_type: Mem(X86OpMem(x86_op_mem { segment: 0, base: X86_REG_SI, index: 0, scale: 1, disp: 0x32, })), ..Default::default() }, ], ), // or byte ptr [bx + di], al DII::new( "or", b"\x08\x01", &[ X86Operand { size: 1, access: Some(RegAccessType::ReadWrite), op_type: Mem(X86OpMem(x86_op_mem { segment: 0, base: X86_REG_BX, index: X86_REG_DI, scale: 1, disp: 0, })), ..Default::default() }, X86Operand { size: 1, access: Some(RegAccessType::ReadOnly), op_type: Reg(RegId(X86_REG_AL as RegIdInt)), ..Default::default() }, ], ), // fadd dword ptr [bx + di + 0x34c6] DII::new( "fadd", b"\xd8\x81\xc6\x34", &[X86Operand { size: 4, access: Some(RegAccessType::ReadOnly), op_type: Mem(X86OpMem(x86_op_mem { segment: 0, base: X86_REG_BX, index: X86_REG_DI, scale: 1, disp: 0x34c6, })), ..Default::default() }], ), // adc al, byte ptr [bx + si] DII::new( "adc", b"\x12\x00", &[ X86Operand { size: 1, access: Some(RegAccessType::ReadWrite), op_type: Reg(RegId(X86_REG_AL as RegIdInt)), ..Default::default() }, X86Operand { size: 1, access: Some(RegAccessType::ReadOnly), op_type: Mem(X86OpMem(x86_op_mem { segment: 0, base: X86_REG_BX, index: X86_REG_SI, scale: 1, disp: 0, })), ..Default::default() }, ], ), ], ); // X86 32bit test_arch_mode_endian_insns_detail( &mut Capstone::new() .x86() .mode(x86::ArchMode::Mode32) .build() .unwrap(), Arch::X86, Mode::Mode32, None, &[], &[ // leal 8(%edx, %esi), %ecx DII::new( "lea", b"\x8d\x4c\x32\x08", &[ X86Operand { size: 4, access: Some(RegAccessType::WriteOnly), op_type: Reg(RegId(X86_REG_ECX as RegIdInt)), ..Default::default() }, X86Operand { size: 4, access: Some(RegAccessType::ReadOnly), op_type: Mem(X86OpMem(x86_op_mem { segment: 0, base: X86_REG_EDX, index: X86_REG_ESI, scale: 1, disp: 8, })), ..Default::default() }, ], ), // addl %ebx, %eax DII::new( "add", b"\x01\xd8", &[ X86Operand { size: 4, access: Some(RegAccessType::ReadWrite), op_type: Reg(RegId(X86_REG_EAX as RegIdInt)), ..Default::default() }, X86Operand { size: 4, access: Some(RegAccessType::ReadOnly), op_type: Reg(RegId(X86_REG_EBX as RegIdInt)), ..Default::default() }, ], ), // addl $0x1234, %esi DII::new( "add", b"\x81\xc6\x34\x12\x00\x00", &[ X86Operand { size: 4, access: Some(RegAccessType::ReadWrite), op_type: Reg(RegId(X86_REG_ESI as RegIdInt)), ..Default::default() }, X86Operand { size: 4, access: None, op_type: Imm(0x1234), ..Default::default() }, ], ), ], ); // X86 64 test_arch_mode_endian_insns_detail( &mut Capstone::new() .x86() .mode(x86::ArchMode::Mode64) .build() .unwrap(), Arch::X86, Mode::Mode64, None, &[], &[ // push rbp DII::new( "push", b"\x55", &[X86Operand { size: 8, access: Some(RegAccessType::ReadOnly), op_type: Reg(RegId(X86_REG_RBP as RegIdInt)), ..Default::default() }], ), // mov rax, qword ptr [rip + 0x13b8] DII::new( "mov", b"\x48\x8b\x05\xb8\x13\x00\x00", &[ X86Operand { size: 8, access: Some(RegAccessType::WriteOnly), op_type: Reg(RegId(X86_REG_RAX as RegIdInt)), ..Default::default() }, X86Operand { size: 8, access: Some(RegAccessType::ReadOnly), op_type: Mem(X86OpMem(x86_op_mem { segment: 0, base: X86_REG_RIP, index: 0, scale: 1, disp: 0x13b8, })), ..Default::default() }, ], ), ], ); } #[test] fn test_arch_xcore() { test_arch_mode_endian_insns( &mut Capstone::new() .xcore() .mode(xcore::ArchMode::Default) .build() .unwrap(), Arch::XCORE, Mode::Default, None, &[], &[ ("get", b"\xfe\x0f"), ("ldw", b"\xfe\x17"), ("setd", b"\x13\x17"), ("init", b"\xc6\xfe\xec\x17"), ("divu", b"\x97\xf8\xec\x4f"), ("lda16", b"\x1f\xfd\xec\x37"), ("ldw", b"\x07\xf2\x45\x5b"), ("lmul", b"\xf9\xfa\x02\x06"), ("add", b"\x1b\x10"), ("ldaw", b"\x09\xfd\xec\xa7"), ], ); } // XXX todo(tmfink) investigate upstream xcore operand bugs #[test] fn test_arch_xcore_detail() { use crate::arch::xcore::XcoreOperand::*; use crate::arch::xcore::XcoreReg::*; use crate::arch::xcore::*; use capstone_sys::xcore_op_mem; test_arch_mode_endian_insns_detail( &mut Capstone::new() .xcore() .mode(xcore::ArchMode::Default) .build() .unwrap(), Arch::XCORE, Mode::Default, None, &[], &[ // get r11, ed DII::new( "get", b"\xfe\x0f", &[ Reg(RegId(XCORE_REG_R11 as RegIdInt)), Reg(RegId(XCORE_REG_ED as RegIdInt)), ], ), // ldw et, sp[4] DII::new( "ldw", b"\xfe\x17", &[ Reg(RegId(XCORE_REG_ET as RegIdInt)), Mem(XcoreOpMem(xcore_op_mem { base: XCORE_REG_SP as u8, index: XCORE_REG_INVALID as u8, disp: 4, direct: 1, })), ], ), // setd res[r3], r4 DII::new("setd", b"\x13\x17", &[Reg(RegId(XCORE_REG_R4 as RegIdInt))]), // init t[r2]:lr, r1 DII::new( "init", b"\xc6\xfe\xec\x17", &[ Mem(XcoreOpMem(xcore_op_mem { base: XCORE_REG_R2 as u8, index: XCORE_REG_LR as u8, disp: 0, direct: 1, })), Reg(RegId(XCORE_REG_R1 as RegIdInt)), ], ), // divu r9, r1, r3 DII::new( "divu", b"\x97\xf8\xec\x4f", &[ Reg(RegId(XCORE_REG_R9 as RegIdInt)), Reg(RegId(XCORE_REG_R1 as RegIdInt)), Reg(RegId(XCORE_REG_R3 as RegIdInt)), ], ), // lda16 r9, r3[-r11] DII::new( "lda16", b"\x1f\xfd\xec\x37", &[Reg(RegId(XCORE_REG_R9 as RegIdInt))], ), // ldw dp, dp[0x81c5] DII::new( "ldw", b"\x07\xf2\x45\x5b", &[Reg(RegId(XCORE_REG_DP as RegIdInt))], ), // lmul r11, r0, r2, r5, r8, r10 DII::new( "lmul", b"\xf9\xfa\x02\x06", &[ Reg(RegId(XCORE_REG_R11 as RegIdInt)), Reg(RegId(XCORE_REG_R0 as RegIdInt)), Reg(RegId(XCORE_REG_R2 as RegIdInt)), Reg(RegId(XCORE_REG_R5 as RegIdInt)), Reg(RegId(XCORE_REG_R8 as RegIdInt)), Reg(RegId(XCORE_REG_R10 as RegIdInt)), ], ), // add r1, r2, r3 DII::new( "add", b"\x1b\x10", &[ Reg(RegId(XCORE_REG_R1 as RegIdInt)), Reg(RegId(XCORE_REG_R2 as RegIdInt)), Reg(RegId(XCORE_REG_R3 as RegIdInt)), ], ), // add r0, r8, 9 DII::new( "add", b"\x01\x96", &[ Reg(RegId(XCORE_REG_R0 as RegIdInt)), Reg(RegId(XCORE_REG_R8 as RegIdInt)), Imm(9), ], ), ], ); } capstone-0.7.0/test-inputs/x86_64.bin_ls.bin010066400017500001750000002111311364465622700167670ustar0000000000000000AWAVAUATUSHHH>dH%(H$x1ϰAeAeAaeA @!H!P!H!׺!H!H!F!mt/t  r! !1X!fA@!2!*!!!!!!!!!!H!H!!HIt/A@AHsH14AfAH!P!hA1HHRe1!bL1IĺpAqLH1110JH=ý!HF!1HH1HHw!袸HH8!Ӹh!v5LfAfAt H=!I貸Au1C:HHǼ!芸= !=! !!wEu !=Ƽ!D%!= =!=!tE1AJ@J@J@HH$!6A@#@'@11a}tgA3H;Hܻ!^!=S!=s!=-!=!!}=!p=!c!WHDqLd1IĺA@LH111?!P! E =! x6!Hں!UfA@aIǾHaHMJ!!H!H!E-!Hպ!fк!H=\! eA!1!%!=i!t2A@#@'@11afA@#@'@11`aMAݿKH!dE)H!H!E8 IcHlH}1AAHHD9H=P! H#!AHD$bY A1/|$LH!LwH\$H;zH{qHi!H!HD$H|$lHL$H=ֹ!HAHH!?L1MHD$HHXL$HLIGHIgH={!HUHT$@ƿLHL$HHT$@HL$H$4HL$H$HH=!HHHP肄H H9tSH\L1͸Hþ(A1HH111L!H!H+۵!Hl$HHT$@HwaH$0H$H!HHH !HPH(=!u =K!=&!u(H=e!HG(H;G0HPHW( HT!=m!!HH!H=!ID1HH!=3!t1Hr!H+c!HHR!H!HH@!H ɫ!gAH!D$H$D$A$LHHHXr!te}.tGu[L=L!MuMDMMt?I?Hu(ft1}.|t@L=!Mu*f.MMtI?Hu롐E1< w 4@IA1LHDH$=!k=Ե!^=õ!Q=!D;50A$t+1gA|$LHA<$KLE8=!M!t =6!=!(1gAH5ǩ!HH$HH=!H!HG(H;G0HPHW( L!!H$H<$Hw!:H5[!HHH0H=A!HJ!HG(H;G0)HPHW( H)!H=ɴ!:LLk1L1gA|$LH$H !eAH!H !eAlH|!=!/Hб!H+!H!H!HI!HH!H2!HH+ !Hj HPHt$0H!HPH@HT$0HD$8H! HH\$H;H{HHD$L0=Ȳ!=!t"H=`!a$a$H=g!IAAJADHI9t+@aat1ڋP!t>u=;!t*=+!tEagAb`agASH=!WXAH@A1H}!HtHqH HgxH$xdH3 %(! HĈ[]A\A]A^A_þ`ac1gA|$LH`a `v QLij1IĺALH111_!wEt =!MAIAlILLtA<$+TA TAL^HHHHu/l H=Т!eAH IH"!A`aADLt$0qHL$0E1E1LH֐H=Hۥ!H9D$0HCD$0AIšAH!uI9wH SgAQHHD$(t 8]gA4Ht8u{bgA HIQ8HA`TAI1LLHHILH-`TAH=tggALEuI}1Lu=ɮ! N t =!3 t =!AIA@aME>HT$@1DH|$@t D@aIJAL9uH|$H@a DŽ$AJ@Eu@aDt!APY@Ht$@IED1HD$@IIJAu =!H=,!3H=!H=T!HG(H;G0|HPHW( H̭!HHAHIM`O,=ut)HŸ[L'H.]M+A\A]A^fDt܀:tHAHDHxw'$0fH11AAHdH%(HD$1t>t!1HT$dH3%(u9Hf.HŢ!fDH$H!AUATIUSH AHH1MtLaHC1HtHNHH$!DkH!HCH[]A\A]f.SHH?H{HHjat [UD[@f.S1H=բ!t H!HH+H #@?MIH}!3A}aaAeADFo!EbLeAH1>LFIfDH{cH >q! eA"IL-1ҾHH#{!4|!1ҿ{,5@|!=,p!!|!eAs0t%1={!uHt$$ZHt$$H={!{,L5{!h={!H{!1fH{PLe{!Ht$`b{!IIQ^{!T{!Ht$`D-D{!DA)HC8HH @ [IHC8Ht$@D5{!HH H% x[EAAWEIDz!AVIeAHH1BHcz!Y^LlqHT=H9H|$ Ht$@OZIH|$L $HD$PHL $t;H y!H5l!H|$`E1HL $5HDl!L $u+ECvl!E11DD)ʉ3H|$`1HL $7_L $A>l!胭AWAVAUATUSH8=y!w-y!$h@AH=Mn!HG(H;G0_HPHW( H8[]A\A]A^A_DH=x! 1E1H@IHx!LtHy!HH+HD$IF1H߻L H=xy!HL$wf`@H=m!HG(H;G0HPE1HW( H6y!LL$HLLH;y!HIFL$1HIHHtMI<LM9,H8[]A\A]A^A_1H=x!u0HPHW( Hv!HH9x!Hpx!H<H=l!HG(H;G0r f1H=Fx!u(SHPHW( HH9"x!0H x!1HL!1@sAHH芌HL!PtA1ҊHHgHK!1uA诊HHDHK!1wA茊HH!HK!1yAiHHHK!1zAFHHۋHlK!1|A#HH踋HIK!1P}AHH蕋H&K!1XA݉HHrHK!1pA躉HHOHJ!1A藉HH,HJ!1xAtHH HJ!1AQHHHwJ!1A.HHÊHTJ!1PA HH蠊H1J!1AHH}HJ!1AňHHZPI!w]eA^eAHDH$gAHD$beAgAHD$eAHD$reAHHD$ eAHD$(eAHD$0eAHD$8eAHD$@eAHD$HeAHD$PeAHD$XeAHD$`HD$hHH3Ht H̉uH[1eAHڇAHƺeA1A1%HteAH^1A腇HHƺA1AAH918ANLHHڿ1蹊eA%AHƺeA1茊1pHtAeAH詅t+H1ЇAцHHƿ1?$A1詆HHƺA1HA|eA%@AWAVAUATIUSH^I?IHHHGHHHt;HHL蒅HIxRL9weH}L9w(HH轈HHu1H[]A\A]A^A_@L9wKHs8"tH&1fBD%HH[]A\A]A^A_; H1[]A\A]A^A_@fDAWAVIAUATIUSHH(H<$HT$[M7IHMD$HD$1OHD$HH|$LHIHkD$ED$HLM4Mt@H4$LLyuL̈́I9tXH|$uH\$HLM4Mu|$HHDD$H([]A\A]A^A_D$fH(H[]A\A]A^A_@f.HATUHSHtK0A1IH޿RHHþ1NILH[]A\111饇DA1ăIDf.AWAVIAUAT1USHIվMAHHD!E1~HH1M'Mu?@LHIaRH=D!HbA1LÇM$MtJHtLHL藄uLHLRH=mD!HjA1yM$MuH=ID!HG(H;G0sHPHW( H[]A\A]A^A_H []A\A]A^A_-f.AWAVIAUATIUSHHLHHHLMMHxH[]A\A]A^A_HLL LHHAHAVAUATUSL6Mt1IIHH^L3LHMtLHLcu[L]A\A]A^H=N!@=N!USHH=B!t=N!t!<8 uH=B!ˋuJH[]1oA_H=pN!HHt. NH0IH4fA11=A!0HٺlA11ڄUS1HH?/@HH)H9v+x/HQtf|/HBuHH9uHH[]H@f.USHHHHH|0 NH4$A 6QЀ 8KD$ t&HVHt@HRHHuH9HBHH9rHLG11L9s6H9t'HAHHHtfH@HHuHL9r1H9wtH9W AUATUSH1HH7HO LgLoHH9s>fH>t&HFHt@H@HHuH9HBHH9r˺AH1v1LʋAHvMx^fMI*YxqfI*^LHA~vHHH[]A\A]A1]vLLfHH MH*XY6yLfHAL H*XyDATUISHH0Ht&H@H3H9tHAT$8uH[Hu1[H]A\fH3[]HA\DHH t+HHOH9r$HH9sHHtH1H~o@f.USHHHHHHRHtH;uHBHu#HU HHu HH9w1H[]HH[]ff.HHH;WsV1LMt4H9v9LLBHMt@H9t+MHLLM@MuHH9WwH1Ðf.AWAVAUATUSHL7L;wsOIII1I>Ht LH;LAԄtH[HHuIM9wwHH[]A\A]A^A_1fD1҄t$HHHH)1HHuHDf.G?GL?G ?GAWAV@AUATIUSIIPHHHMLDHɸ @LDqHHH0AHDH{(Hk(L}ML=HHHpeHHkt\H'pHHtGHHCHC HLk0Lc8HCLs@HHCHH[]A\A]A^A_fDHlH1[]A\A]A^A_MxSfI*^.ws.rr$\hHL,I1 DL,fDLfHAL H*X@ATUHSL'L;gssI<$tbI\$HU@Hu6fHHt H;HU@HCHMHHHHKH]HuHtI<$I$ID$IL9ewHEHE []A\ATUHSH@t@H t9L'L;gsmfI<$HtLfH;U@H[HuHEIL9wHELeL9v0DI|$HtfDH_jHHuIL9ewH}HHt@H_jHHuH}j[H]A\jfATUHSH`Lg(dH%(HD$X1A|$uQHfH*A^D$.qC.h>\ZHH,H1@HPH1HHmHHH9]H!mHH$H\$H1HHHHD$HE0HD$HD$ Ld$(HD$0HE8HD$8HE@HD$@HEHHD$Hu}HD$HHHHEH1HHH<$6i @HL$XdH3 %(uwH`[]A\1@H,fDH}hH$HEHD$HEHD$HEHD$HEHD$HHEH떐HfHH H*X;mjh@f.ATUSHdH%(HD$1H1IHHHlHt/MI$1HL$dH3 %($H[]A\HCH[fH*HCHS(H!fH*YB.w^L$$I<$?HCHHHPHSHIT$H(HPID$HC bf.1QfH{(HCHS(HR&fH*HCHfH*(Y.TzYB uY.4 .+H,H\1HHHHgfDHfHH H*XfHfHH H*XfI,$HC HC@\HH,H1UHfHH H*XHfHH H*XcjHPgf.SHHHdH%(HD$1Pt#HHD$HL$dH3 %(u H[1ogDf.ATUSHHHdH%(HD$1HH$Hk HH:tHL$dH3 %(H[]A\HCHHHCfH*HCHS(H!fH*Y .wHf.H{(HCHS(H6fH*HCHfH*Y.vzYB.srH,HHH{HHtLgOdMLuHCHHHfHH H*Xf\$HH,H1tYB[fDHfHH H*Xf1lfHfHH H*XfHfHH H*X=ef.AWAVAUATUSIHHD$Ht$ T$HLD$dH %(H$1ɃD$0D$4cL0ILdIHHwgAImLCIELCHIrdHAH|$IBHD$8HD$HL9H$31LHHH_Ld$ Ml$  HD$HD$ Hl$ H\$ Hl$ D$HD$41ru@ڃ|$-|$ND$N fD$Ll$L|$@l$NHD$@t$1҅u"HD$ Hl$ 1ҹHEHHD$ Hl$ J?DMHD$1IHIH1IHH1HHIHAI91H@NjL$HL$$DD$4L9{1E#@@@M9H1҉IIÍ1ABAVALA9wA9׃f.|$t-HhAHH\$1<$HfH߻a_AXIE1@ @fDL<$Ht$LLL$M)LLeLL$M)O$7D$HD$H|$Hډ% tf@g:DT$0H<$EHWu kt HcȌAH<$tDL$0EtHWGiHBBH$H$H$dH3%(LHĘ[]A\A]A^A_H1HHHH91E1fDH|$8MIM)`H|$P)LLHD$gbLL|$8MLl$DL)LLH_bHEt<~DMGHM9HD$PMGM)L)LJ4 H%bMHuIpDH|$HƇ H$}@@|$ HnAH1Lt$L<$dL_IMOXZL$01IDl$ L9[-_G|$ND$N fD$Ll$L|$@l$NHD$@L$1҅u-HD$Hl$1ҹHE HHD$Hl$y GHhAL|$H1L<$cL^A]IE1A_fD|$ND$N fD$Ll$L|$@l$NHD$@DL$1Eu.HD$Hl$1ҹHE HHD$Hl$kfDHnA<$Lt$H1L|$0bL]^_DD$0IMO1EIDl$ L9D$HHD$B|00|$XI |$Dd$EAD!„DPA A0HD$LDDM)AMAtAA1E1D|$tID\$Eu_Dׅ~XHtPD$4H9uGtBD$HM|$AD$1@HHcHAADMHHIHHHHH)ƃ0HA7HuD#DrDbDHL$HeT$4HH9Bu5fD|$ND$NH fD$Ll$L|$@l$NHD$@H1|$ND$NH fD$Ll$L|$@l$NHD$@H1׃e@L$$1ED|$ND$NH fD$Ll$L|$@l$NHD$@H1fDL$$pZxD$Hu/A0ji1ƒ,L$$E1IIxMHIDITITLH)I)DG1AЃO 9N r+IsH L$$LAD$0HIADs6A0AA$ ATfATIIL$HI$DITITLH)I)D1҉փI<69H<1rAAATATaATfATPEYH$ H$AA$ATATa1U@f.AUATIUSHHHdH%(HD$1HE1;'AAHx}HHED ,AE,$1Hu%~AVHH%HHEHL$dH3 %(%H[]A\A]HAi1AAHHHR0< v/H$H9uf.H9H0< wHU:fAUHHtAUHH~AUHHE1HHUf.A$HUzBtÀA lDÀziV(WATUSH $!Ht;;uef.9;tWH[HuAAvVHtL LVHxGHx(LHgUH#!H#!HC{HC[]A\HDDf.ATUISHj#!Ht=/H[Ht'@8kuH{LWuH[]A\fH#!Ht4A,$DH[Ht@8kuH{LWu[1]A\fLWLHUHxFHxLHuTHtEH"!H"!HCcH"!H"!HC1Mf.ATUSH]"!Ht;;uef.9;tWH[HuAAUHtL LUHxEHx(LHSH!!H!!HC{HC[]A\HDDf.ATUISH!!Ht=/H[Ht'@8kuH{L5VuH[]A\fHi!!Ht4A,$DH[Ht@8kuH{LUu[1]A\fLHVLHTHxEHxLHRHtEH !H !HCcH !H !HC1Mf.HHNFHgfffffffxAfHHHHH?HH)HHH)ǃ0H@9HuH@HIgfffffff0fDHHIHH?HH)HA)HH׈uHNF-HfHNFHfDHHHHHHH)ǃ0H@9HuHf.@AWAVIAUATIUSDIH8HT$DD$RIHD$vH$LHD$MEL9LE1ɋD$Im*E1t MAIM1LEȃLEHD$HI\IAI9s,Mt'fIAD$ L9HHuHA$LL)LLL$L9LD$IGKUH9IULD$LL$v&Mt!HH@ @H9H@uMMH<$OH|$OH8L[]A\A]A^A_@HD$L9ED$MImI)MME1f.PH{11L5PHu/H$HD$IPfHH,HD$(HGSHH$HT$(H<$LOHH$D(8HD$ fTu ED$ H}uHt$(H<$RO|$ HcH4$11'THHD$ H|$ RHHD$H$Mu8I1DHHI9r.IA?HŅt KRu޸AHI9sfLt$HT$ H4$ALSI Ht$(H<$NHcf.MEI9HD$HHD$ 2ILH$HD$#L<$1YME1@f.AWAVIAUATE1USHHL.T$L$ LHXLHQHItADL$ DD$HLmHHLHt,H9MvHL[]A\A]A^A_fLLfDLE1LAWAVAAUATL,7USHH(dH%(HD$1NHL9EE1AA<%} <w(HAI9UEx-u8x8u2xu,;`8A4AHEH[]Gu P߀Bt ACAHEH[]x1uۀx8uՀx0uπx3uɀx0uÀxu;`@A{HpHg|$L9sA?HCI9vAD"HCI9vAD"HCI9vAD?AHH111A|$L9sA'HCI9vAD\HCI9vAD'HE1f1Ҁ|$|DAD8v2I9vA'HSI9vAD$HSI9vAD'HAL9sA\HHH|$`dDT$0DL$EHADL$DT$0QfAD8$H1D߉H4$DD$|T$L\$AA8s2L9sA'HCI9vAD$HCI9vAD'HAL9sA\HCI9vD0ADHCI9vD0ADAHHA0H9VL9sE$E$+H@8FEtL9sA\HHH9A8PL9sA'LCM9vAD'HE1E1D$$1HD$ HD$HVtT1EAL$_?H=EAHD$XL$1AL$5?H=AHD$PL$1ۅu'HT$XtfDI9vAHuHl$PLT$D$H>Hl$HHD$ $LT$D$$1HD$ HD$HCAAzD$$1HD$ HD$HX$-$MtA'D$HD$ HD$HAA$…MtA"D$$HD$ HD$HCAHD$D$|HHD$|$DRDD$|fDIHDŽ$1H\$pDd$DL$~HALt$@L|$hLl$0DT$E1߾H?H9PHH9CH1뭾H?H9sH놾H?A1H9HE1D AuLH?A1HE1D AH9vHA߾H?H9ZH 1H?A1HE1D AH9vHA߾H?A1HE1D AkH9vHA߾HH9H 14H?A1HE1D AH9vHA߾H?A1HE1D AH9vHA1AxB@ƍt6B@5k@$AHcjHc1jHcHc1HHHc|Hc1HHHcHc1HHHcHc1HHHc,Hc1HHHc$Hc1HHHc?Hc1HHHcOHc1HHHcrHcHAHaA`AAAUATHcUSMHdH%(H\$1ۃ-W wOsCAu4xHH١AL,1yMHMH112 AAt١A$D$H)IDAWAVAUATUSH(dH%(HD$1$THD$IHH$IMLDI] $H0H @HDN u-u2H|$dH3<%(H([]A\A]A^A_f.1LHsMHL9A$u1Mt A(L$@uI딐"u@LHL$HHL$L$0LHL$L$kHL$HL$A@E1߾H?H9PHH9CH1뭾H?H9sH놾H?A1H9HE1D AuLH?A1HE1D AH9vHA߾H?H9ZH 1H?A1HE1D AH9vHA߾H?A1HE1D AkH9vHA߾HH9H 14H?A1HE1D AH9vHA߾H?A1HE1D AH9vHA1AxB@ƍt6B@5k@$AHcjHc1jHcHc1HHHc|Hc1HHHcHc1HHHcHc1HHHc,Hc1HHHc$Hc1HHHc?Hc1HHHcOHc1HHHcrHcHAHahA`AAStK t[fHHSeA HuHH=[_[@f.SHgHމ[|ff.SHHމ[\ff.SHHމ[