capstone-0.13.0/.cargo_vcs_info.json0000644000000001510000000000100127260ustar { "git": { "sha1": "d8c4e2c1c4a477d8f82a82ca5ad493c2ca602ecb" }, "path_in_vcs": "capstone-rs" }capstone-0.13.0/.gitignore000064400000000000000000000000271046102023000135100ustar 00000000000000demo target Cargo.lock capstone-0.13.0/CHANGELOG.md000064400000000000000000000147141046102023000133410ustar 00000000000000# 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.13.0] - 2025-02-04 ### Added - BPF arch support - Detail support for SystemZ - Make RegAccessType available for ARM ### Changed - Bump minimum Rust version to 1.70.0 ### Fixed - Build on Windows platforms when using `bindgen` feature - Segfault when running on s390x (AKA SystemZ) ### Removed - `From for RegId` impl ## [0.12.0] - 2024-02-25 ### Added - `full` feature (enabled by default) which disables [diet mode] for the Capstone C library ### Changed - Bump minimum Rust version to 1.60.0 - `Arm64OperandType::Sys` contains `Arm64SysOp` instead of `u32` ## [0.11.0] - 2022-05-01 ### Added - Owned insn type `OwnedInsn` ([#123](https://github.com/capstone-rust/capstone-rs/pull/123)) ### Removed - `Insn` `Clone` impl to fix soundness issue ([#122](https://github.com/capstone-rust/capstone-rs/pull/122)) ### Fixed - Soundness issue by remove `Insn` `Clone` impl (see "Removed" note above) ## [0.10.0] - 2021-08-09 ### Added - Links to related types/functions in API documentation ### Changed - Access `Insn` elements of `Instructions` via `Deref`/`AsRef` traits instead of an iterator - Access `Insn` groups and read/write registers via a slice - Update README.md code example ## [0.9.0] - 2021-07-13 ### Added - Support for RISC-V architecture ### Changed - Updated minimum supported Rust version to 1.40.0 ## [0.8.0] - 2021-04-09 ### Added - `Insn::from_raw()` to convert from a `cs_insn` pointer - `Deref` impl for `Instructions` ### Changed - X86: return `X86OpMem::segment()` as `RegId` instead of `u32` ### Fixed - Fixed data race in multi-threaded environment ([#87](https://github.com/capstone-rust/capstone-rs/issues/87)) ## [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.13.0]: https://github.com/capstone-rust/capstone-rs/compare/capstone-v0.12.0...master [0.12.0]: https://github.com/capstone-rust/capstone-rs/compare/capstone-v0.11.0...capstone-v0.12.0 [0.11.0]: https://github.com/capstone-rust/capstone-rs/compare/capstone-v0.10.0...capstone-v0.11.0 [0.10.0]: https://github.com/capstone-rust/capstone-rs/compare/capstone-v0.9.0...capstone-v0.10.0 [0.9.0]: https://github.com/capstone-rust/capstone-rs/compare/capstone-v0.8.0...capstone-v0.9.0 [0.8.0]: https://github.com/capstone-rust/capstone-rs/compare/capstone-v0.7.0...capstone-v0.8.0 [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.13.0/Cargo.lock0000644000000513760000000000100107200ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "aho-corasick" version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] [[package]] name = "anes" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" [[package]] name = "anstyle" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" [[package]] name = "autocfg" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" [[package]] name = "bindgen" version = "0.69.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" dependencies = [ "bitflags", "cexpr", "clang-sys", "itertools 0.12.1", "lazy_static", "lazycell", "log", "prettyplease", "proc-macro2", "quote", "regex", "rustc-hash", "shlex", "syn", "which", ] [[package]] name = "bitflags" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" [[package]] name = "bumpalo" version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] name = "capstone" version = "0.13.0" dependencies = [ "capstone-sys", "criterion", "libc", ] [[package]] name = "capstone-sys" version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2267cb8d16a1e4197863ec4284ffd1aec26fe7e57c58af46b02590a0235809a0" dependencies = [ "bindgen", "cc", "libc", "regex", ] [[package]] name = "cast" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" version = "1.0.94" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7" [[package]] name = "cexpr" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ "nom", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "ciborium" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" dependencies = [ "ciborium-io", "ciborium-ll", "serde", ] [[package]] name = "ciborium-io" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" [[package]] name = "ciborium-ll" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" dependencies = [ "ciborium-io", "half", ] [[package]] name = "clang-sys" version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" dependencies = [ "glob", "libc", "libloading", ] [[package]] name = "clap" version = "4.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" dependencies = [ "clap_builder", ] [[package]] name = "clap_builder" version = "4.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" dependencies = [ "anstyle", "clap_lex", ] [[package]] name = "clap_lex" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" [[package]] name = "criterion" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" dependencies = [ "anes", "cast", "ciborium", "clap", "criterion-plot", "is-terminal", "itertools 0.10.5", "num-traits", "once_cell", "oorandom", "plotters", "rayon", "regex", "serde", "serde_derive", "serde_json", "tinytemplate", "walkdir", ] [[package]] name = "criterion-plot" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" dependencies = [ "cast", "itertools 0.10.5", ] [[package]] name = "crossbeam-deque" version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" dependencies = [ "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" [[package]] name = "crunchy" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" [[package]] name = "either" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" [[package]] name = "errno" version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" dependencies = [ "libc", "windows-sys", ] [[package]] name = "glob" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "half" version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" dependencies = [ "cfg-if", "crunchy", ] [[package]] name = "hermit-abi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" [[package]] name = "home" version = "0.5.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3d1354bf6b7235cb4a0576c2619fd4ed18183f689b12b006a0ee7329eeff9a5" dependencies = [ "windows-sys", ] [[package]] name = "is-terminal" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" dependencies = [ "hermit-abi", "libc", "windows-sys", ] [[package]] name = "itertools" version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" dependencies = [ "either", ] [[package]] name = "itertools" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" dependencies = [ "either", ] [[package]] name = "itoa" version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" [[package]] name = "js-sys" version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" dependencies = [ "wasm-bindgen", ] [[package]] name = "lazy_static" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "lazycell" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "libc" version = "0.2.153" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" [[package]] name = "libloading" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", "windows-targets", ] [[package]] name = "linux-raw-sys" version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" [[package]] name = "log" version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "memchr" version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "minimal-lexical" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "nom" version = "7.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" dependencies = [ "memchr", "minimal-lexical", ] [[package]] name = "num-traits" version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" dependencies = [ "autocfg", ] [[package]] name = "once_cell" version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "oorandom" version = "11.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" [[package]] name = "plotters" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" dependencies = [ "num-traits", "plotters-backend", "plotters-svg", "wasm-bindgen", "web-sys", ] [[package]] name = "plotters-backend" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" [[package]] name = "plotters-svg" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" dependencies = [ "plotters-backend", ] [[package]] name = "prettyplease" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ac2cf0f2e4f42b49f5ffd07dae8d746508ef7526c13940e5f524012ae6c6550" dependencies = [ "proc-macro2", "syn", ] [[package]] name = "proc-macro2" version = "1.0.80" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a56dea16b0a29e94408b9aa5e2940a4eedbd128a1ba20e8f7ae60fd3d465af0e" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" dependencies = [ "proc-macro2", ] [[package]] name = "rayon" version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" dependencies = [ "either", "rayon-core", ] [[package]] name = "rayon-core" version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" dependencies = [ "crossbeam-deque", "crossbeam-utils", ] [[package]] name = "regex" version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rustc-hash" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustix" version = "0.38.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" dependencies = [ "bitflags", "errno", "libc", "linux-raw-sys", "windows-sys", ] [[package]] name = "ryu" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" [[package]] name = "same-file" version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" dependencies = [ "winapi-util", ] [[package]] name = "serde" version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fb1c873e1b9b056a4dc4c0c198b24c3ffa059243875552b2bd0933b1aee4ce2" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.197" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "serde_json" version = "1.0.116" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" dependencies = [ "itoa", "ryu", "serde", ] [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "syn" version = "2.0.59" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a6531ffc7b071655e4ce2e04bd464c4830bb585a61cabb96cf808f05172615a" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "tinytemplate" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" dependencies = [ "serde", "serde_json", ] [[package]] name = "unicode-ident" version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "walkdir" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ "same-file", "winapi-util", ] [[package]] name = "wasm-bindgen" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" dependencies = [ "cfg-if", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" dependencies = [ "bumpalo", "log", "once_cell", "proc-macro2", "quote", "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", "syn", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.92" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" [[package]] name = "web-sys" version = "0.3.69" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" dependencies = [ "js-sys", "wasm-bindgen", ] [[package]] name = "which" version = "4.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87ba24419a2078cd2b0f2ede2691b6c66d8e47836da3b6db8265ebad47afbfc7" dependencies = [ "either", "home", "once_cell", "rustix", ] [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-util" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" dependencies = [ "winapi", ] [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows-sys" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ "windows-targets", ] [[package]] name = "windows-targets" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", "windows_i686_gnu", "windows_i686_gnullvm", "windows_i686_msvc", "windows_x86_64_gnu", "windows_x86_64_gnullvm", "windows_x86_64_msvc", ] [[package]] name = "windows_aarch64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" [[package]] name = "windows_i686_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" capstone-0.13.0/Cargo.toml0000644000000030440000000000100107300ustar # 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 are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" rust-version = "1.70" name = "capstone" version = "0.13.0" authors = [ "m4b ", "Richo Healey ", "Travis Finkenauer ", ] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false 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" [lib] name = "capstone" path = "src/lib.rs" [[example]] name = "demo" path = "examples/demo.rs" [[bench]] name = "my_benchmark" path = "benches/my_benchmark.rs" harness = false [dependencies.capstone-sys] version = "0.17.0" default-features = false [dependencies.libc] version = "0.2" default-features = false [dev-dependencies.criterion] version = "0.5" [features] default = [ "full", "std", ] full = ["capstone-sys/full"] std = [] use_bindgen = ["capstone-sys/use_bindgen"] [badges.travis-ci] repository = "capstone-rust/capstone-rs" capstone-0.13.0/Cargo.toml.orig000064400000000000000000000021521046102023000144100ustar 00000000000000[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.13.0" rust-version.workspace = true [badges] travis-ci = { repository = "capstone-rust/capstone-rs" } [dependencies] capstone-sys = { path = "../capstone-sys", version = "0.17.0", default-features = false } libc = { version = "0.2", default-features = false } [dev-dependencies] criterion = "0.5" [[bench]] name = "my_benchmark" harness = false [features] default = ["full", "std"] std = [] # The 'full' feature, enabled by default, compiles Capstone normally. When disabled, # Capstone will be built in Diet mode (https://www.capstone-engine.org/diet.html). # This disables some features to reduce the size of the library full = ["capstone-sys/full"] use_bindgen = ["capstone-sys/use_bindgen"] capstone-0.13.0/LICENSE000064400000000000000000000021101046102023000125200ustar 00000000000000The 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.13.0/README.md000064400000000000000000000103341046102023000130010ustar 00000000000000# capstone-rs [![Crates.io Badge](https://img.shields.io/crates/v/capstone.svg)](https://crates.io/crates/capstone) Linux/macOS/Windows [![Github Workflow CI Badge](https://github.com/capstone-rust/capstone-rs/actions/workflows/main.yml/badge.svg)](https://github.com/capstone-rust/capstone-rs/actions) | 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. The `Capstone` struct is the main interface to the library. # 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.70.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: &[RegId]) -> String { let names: Vec = regs.iter().map(|&x| cs.reg_name(x).unwrap()).collect(); names.join(", ") } /// Print instruction group names fn group_names(cs: &Capstone, regs: &[InsnGroupId]) -> String { let names: Vec = regs.iter().map(|&x| cs.group_name(x).unwrap()).collect(); names.join(", ") } fn main() { let 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.as_ref() { 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: ```plain 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/`](capstone-rs/examples) directory. More complex demos welcome! # Features - `full`: do not compile Capstone C library in [diet mode](https://www.capstone-engine.org/diet.html) - `std`: enable `std`-only features, such as the [`Error` trait](https://doc.rust-lang.org/std/error/trait.Error.html) - `use_bindgen`: run `bindgen` to generate Rust bindings to Capstone C library instead of using pre-generated bindings (not recommended). : enabled by default # 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](capstone-rs/LICENSE) [upstream]: https://www.capstone-engine.org/ capstone-0.13.0/THIRD_PARTY.txt000064400000000000000000000033301046102023000141120ustar 00000000000000capstone-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.13.0/benches/my_benchmark.rs000064400000000000000000000024561046102023000161440ustar 00000000000000extern 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.13.0/ci/test.sh000075500000000000000000000207741046102023000134440ustar 00000000000000#!/usr/bin/env bash # # Environment variables: # # features: # - FEATURES: (none by default) # - NO_DEFAULT_FEATURES: enables --no-default-features # - ALL_FEATURES: enables --all-features # # other: # - 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) # - SKIP_CARGO_UPDATE: set to disable "cargo update" part of tests # - VALGRIND_TESTS: run tests under Valgrind # Useful debug info echo "BASH=${BASH}" echo "BASH_VERSION=${BASH_VERSION}" echo "BASH_OPTIONS=$-" echo "BASH_COMPAT=${BASH_COMPAT}" set -euo pipefail set -x if [ "$(basename "$0")" = "test.sh" ]; then cd "$(dirname "$0")/.." else echo "Script is sourced" fi pwd Error() { echo "Error:" "$@" >&2 exit 1 } export RUST_BACKTRACE=1 SHOULD_FAIL=${SHOULD_FAIL:-} # Default to false VALGRIND_TESTS=${VALGRIND_TESTS:-} CARGO="${CARGO:-cargo}" UNAME="$(uname)" # Feature vars if [ -n "${ALL_FEATURES:-}" ] && [ -n "${NO_DEFAULT_FEATURES:-}" ]; then Error "ALL_FEATURES and NO_DEFAULT_FEATURES are mutually exclusive" fi if [ -n "${ALL_FEATURES:-}" ] && [ -n "${FEATURES:-}" ]; then Error "ALL_FEATURES and FEATURES are mutually exclusive" fi CARGO_FEATURE_ARGS=( ${NO_DEFAULT_FEATURES:+ --no-default-features} ${ALL_FEATURES:+ --all-features} ${FEATURES:+ --features "$FEATURES"} ) TARGET="../target" TARGET_COV="${TARGET}/cov" SIMPLE_RUN_EXAMPLES="${SIMPLE_RUN_EXAMPLES:-demo}" 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" if ! [ "${OS_NAME:-}" ]; then case "${UNAME}" in CYGWIN*|MINGW*|MSYS_NT*) OS_NAME=windows ;; Linux) OS_NAME=linux ;; Darwin) OS_NAME=osx ;; FreeBSD) OS_NAME=freebsd ;; esac fi true_path() { case "${OS_NAME}" in windows) cygpath -m "$@" ;; *) echo "$@" ;; esac } # 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 json_format_args=(--quiet --message-format=json) # Test binaries cargo_test_args="${CARGO} test --no-run" ${cargo_test_args} -v TEST_BINS="$(${cargo_test_args} "${json_format_args[@]}" \ | jq -r "select(.profile.test == true) | .filenames[]")" # Exaple binaries EXAMPLE_BINS= for example in $SIMPLE_RUN_EXAMPLES; do cargo_build_example_args="${CARGO} build --example $example" ${cargo_build_example_args} -v example_bin="$(${cargo_build_example_args} "${json_format_args[@]}" \ | jq -r '.executable | strings')" EXAMPLE_BINS="${EXAMPLE_BINS} ${example_bin}" done mkdir -p "${TARGET_COV}" ( set -x pwd ls -l "${TARGET}" ls -l "${TARGET}/${PROFILE}" # Run test and example binaries under kcov for file in ${TEST_BINS} ${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" cargo_update install_kcov cleanup_cov KCOV=./kcov-install/usr/local/bin/kcov run_kcov if [[ -n "${CI:-}" ]]; then codecov_script="$(mktemp)" curl --silent --show-error "https://codecov.io/bash" \ > "${codecov_script}" \ || Error "Failed to download codecov script" bash "${codecov_script}" || Error "Codecov script execution failed" echo "Uploaded code coverage" else echo "Not uploading coverage since we are not in a CI job" fi } bench() { echo "Running bench" cargo_update ${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, which it reads from stdin test_rust_file() { ( tmp_dir="$(mktemp -d /tmp/rust.testdir.XXXXXXXXXX)" [ -d "$tmp_dir" ] || Error "Could not make temp dir" capstone_dir="$(true_path "$(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() { cargo_update 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=( --verbose $(profile_args) "${CARGO_FEATURE_ARGS[@]}" ) 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 ) # Test the example code in README cat ../README.md | \ sed -n '/^```rust/,/^```/p' | grep -vE '^```' | \ test_rust_file if [ ! "${VALGRIND_TESTS}" ]; then continue fi which valgrind valgrind --version 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" } cargo_update() { if [ -z "${SKIP_CARGO_UPDATE:-}" ]; then if [[ -n "${CI:-}" ]]; then echo "Updating dependencies in Cargo.lock" ${CARGO} update else echo "Skipping 'cargo update' since we are not in CI" fi else echo "Skipping 'cargo update' since SKIP_CARGO_UPDATE is set" fi } 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.13.0/examples/demo.rs000064400000000000000000000052421046102023000146340ustar 00000000000000extern crate capstone; use capstone::prelude::*; use capstone::InsnDetail; const MIPS_CODE: &[u8] = b"\x56\x34\x21\x34\xc2\x17\x01\x00"; const X86_CODE: &[u8] = b"\x55\x48\x8b\x05\xb8\x13\x00\x00\xe9\x14\x9e\x08\x00\x45\x31\xe4"; #[cfg(feature = "full")] /// Print register names fn reg_names(cs: &Capstone, regs: &[RegId]) -> String { let names: Vec = regs.iter().map(|&x| cs.reg_name(x).unwrap()).collect(); names.join(", ") } #[cfg(feature = "full")] /// Print instruction group names fn group_names(cs: &Capstone, regs: &[InsnGroupId]) -> String { let names: Vec = regs.iter().map(|&x| cs.group_name(x).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(); #[cfg(feature = "full")] 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())), ]; #[cfg(not(feature = "full"))] let output: &[(&str, String)] = &[ ("insn id:", format!("{:?}", i.id().0)), ("bytes:", format!("{:?}", i.bytes())), ]; for (name, 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.13.0/examples/test.c000064400000000000000000000001751046102023000144650ustar 00000000000000#include int main(void) { int c = 42; c = c * c + 42; printf("Having done stuff, c is now %d\n", c); } capstone-0.13.0/scripts/gen_arch_enums.sh000075500000000000000000000014211046102023000165220ustar 00000000000000#!/bin/sh usage() { cat <&1)"; then echo "$output" echo echo "Failed after $iter tests" exit 1 fi iter=$(expr $iter + 1) done capstone-0.13.0/scripts/tag_release.sh000075500000000000000000000017321046102023000160250ustar 00000000000000#!/bin/sh # # Tag the HEAD based on version in Cargo.toml set -eu cd "$(dirname $0)/.." Error() { echo "Error:" "$@" >&2 exit 1 } extract_toml_value() { grep "^$1" Cargo.toml | sed 's/^[^ =].*=.*"\([^"]\+\)"$/\1/' | head -n1 } git diff --exit-code HEAD || Error "Uncommitted changes" 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.13.0/src/arch/arm.rs000064400000000000000000000237241046102023000143620ustar 00000000000000//! Contains arm-specific types use core::convert::{From, TryInto}; use core::{cmp, fmt, slice}; use capstone_sys::{ arm_op_mem, arm_op_type, arm_shifter, cs_ac_type, cs_arm, cs_arm_op, 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}; use crate::RegAccessType; 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 left (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 left (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, /// How is this operand accessed? /// /// NOTE: this field is always `None` if the "full" feataure is not enabled. pub access: Option } /// 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 ArmInsnDetail<'_> { /// 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, access: None } } } impl From<&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, access: cs_ac_type(op.access as _).try_into().ok(), } } } 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; 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.13.0/src/arch/arm64.rs000064400000000000000000000226551046102023000145360ustar 00000000000000//! Contains arm64-specific types use libc::c_uint; pub use crate::arch::arch_builder::arm64::*; use crate::arch::DetailsArchInsn; use crate::instruction::{RegId, RegIdInt}; use capstone_sys::{arm64_op_mem, arm64_op_type, cs_arm64, cs_arm64_op}; use core::convert::From; use core::{cmp, fmt, mem, slice}; // Re-exports pub use capstone_sys::arm64_barrier_op as ArmBarrierOp; pub use capstone_sys::arm64_barrier_op as Arm64BarrierOp; pub use capstone_sys::arm64_cc as Arm64CC; pub use capstone_sys::arm64_extender as Arm64Extender; pub use capstone_sys::arm64_insn as Arm64Insn; pub use capstone_sys::arm64_insn_group as Arm64InsnGroup; pub use capstone_sys::arm64_prefetch_op as ArmPrefetchOp; pub use capstone_sys::arm64_pstate as Arm64Pstate; pub use capstone_sys::arm64_reg as Arm64Reg; pub use capstone_sys::arm64_sys_op as Arm64SysOp; pub use capstone_sys::arm64_sysreg as Arm64Sysreg; pub use capstone_sys::arm64_vas as Arm64Vas; use capstone_sys::arm64_shifter; use capstone_sys::cs_arm64_op__bindgen_ty_2; /// 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 left Lsl(u32), /// Masking shift left 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 as Arm64Reg::Type) }), ARM64_OP_REG_MSR => RegMsr(unsafe { mem::transmute::(value.reg as Arm64Reg::Type) }), 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, /// 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 register MRS (move the contents of a PSR to a general-purpose register) RegMrs(Arm64Sysreg), /// System register MSR (move to system coprocessor register from ARM register) RegMsr(Arm64Sysreg), /// System PState Field (MSR instruction) Pstate(Arm64Pstate), /// System operation (IC/DC/AT/TLBI) Sys(Arm64SysOp), /// 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 Arm64InsnDetail<'_> { /// 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, 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 From<&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, 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::arm64_prefetch_op::*; use capstone_sys::arm64_pstate::*; use capstone_sys::*; 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 arm64_reg::Type, }, ), 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_sysreg::ARM64_SYSREG_ICC_EOIR1_EL1 as arm64_reg::Type, }, ), RegMsr(arm64_sysreg::ARM64_SYSREG_ICC_EOIR1_EL1), ); t( ( ARM64_OP_SYS, cs_arm64_op__bindgen_ty_2 { sys: arm64_sys_op::ARM64_AT_S1E0R, }, ), Sys(arm64_sys_op::ARM64_AT_S1E0R), ); t( ( ARM64_OP_PREFETCH, cs_arm64_op__bindgen_ty_2 { prefetch: ARM64_PRFM_PLDL2KEEP, }, ), Prefetch(ARM64_PRFM_PLDL2KEEP), ); } } capstone-0.13.0/src/arch/bpf.rs000064400000000000000000000050161046102023000143440ustar 00000000000000//! Contains bpf specific types use core::convert::From; use core::{cmp, fmt, slice}; pub use capstone_sys::bpf_insn_group as BpfInsnGroup; pub use capstone_sys::bpf_insn as BpfInsn; pub use capstone_sys::bpf_reg as BpfReg; use capstone_sys::{cs_bpf, cs_bpf_op, bpf_op_mem, bpf_op_type}; pub use crate::arch::arch_builder::bpf::*; use crate::arch::DetailsArchInsn; use crate::instruction::{RegId, RegIdInt}; /// Contains BPF-specific details for an instruction pub struct BpfInsnDetail<'a>(pub(crate) &'a cs_bpf); impl_PartialEq_repr_fields!(BpfInsnDetail<'a> [ 'a ]; operands ); /// BPF operand #[derive(Clone, Debug, Eq, PartialEq)] pub enum BpfOperand { /// Register Reg(RegId), /// Immediate Imm(u64), /// Memory Mem(BpfOpMem), /// Offset Off(u32), /// Mmem Mmem(u32), /// Msh Msh(u32), /// Ext Ext(u32), /// Invalid Invalid, } impl Default for BpfOperand { fn default() -> Self { BpfOperand::Invalid } } /// Bpf memory operand #[derive(Debug, Copy, Clone)] pub struct BpfOpMem(pub(crate) bpf_op_mem); impl BpfOpMem { /// Base register pub fn base(&self) -> RegId { RegId(self.0.base as RegIdInt) } /// Disp value pub fn disp(&self) -> u32 { self.0.disp } } impl_PartialEq_repr_fields!(BpfOpMem; base, disp ); impl cmp::Eq for BpfOpMem {} impl From<&cs_bpf_op> for BpfOperand { fn from(insn: &cs_bpf_op) -> BpfOperand { match insn.type_ { bpf_op_type::BPF_OP_EXT => BpfOperand::Ext(unsafe { insn.__bindgen_anon_1.ext }), bpf_op_type::BPF_OP_INVALID => BpfOperand::Invalid, bpf_op_type::BPF_OP_REG => BpfOperand::Reg(RegId(unsafe {insn.__bindgen_anon_1.reg} as RegIdInt)), bpf_op_type::BPF_OP_IMM => BpfOperand::Imm(unsafe { insn.__bindgen_anon_1.imm }), bpf_op_type::BPF_OP_MEM => BpfOperand::Mem(BpfOpMem(unsafe { insn.__bindgen_anon_1.mem})), bpf_op_type::BPF_OP_OFF => BpfOperand::Off(unsafe { insn.__bindgen_anon_1.off }), bpf_op_type::BPF_OP_MMEM => BpfOperand::Mmem(unsafe { insn.__bindgen_anon_1.mmem }), bpf_op_type::BPF_OP_MSH => BpfOperand::Msh(unsafe { insn.__bindgen_anon_1.msh }), } } } def_arch_details_struct!( InsnDetail = BpfInsnDetail; Operand = BpfOperand; OperandIterator = BpfOperandIterator; OperandIteratorLife = BpfOperandIterator<'a>; [ pub struct BpfOperandIterator<'a>(slice::Iter<'a, cs_bpf_op>); ] cs_arch_op = cs_bpf_op; cs_arch = cs_bpf; ); capstone-0.13.0/src/arch/evm.rs000064400000000000000000000050141046102023000143620ustar 00000000000000//! 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 EvmInsnDetail<'_> { /// 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, Default)] pub struct 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 fmt::Debug for EvmInsnDetail<'_> { 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 DetailsArchInsn for EvmInsnDetail<'_> { 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.13.0/src/arch/m680x.rs000064400000000000000000000246101046102023000144600ustar 00000000000000//! 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 From<&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.13.0/src/arch/m68k.rs000064400000000000000000000446461046102023000143760ustar 00000000000000//! 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 M68kInsnDetail<'_> { /// 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 (M68K_REG_D0..=M68K_REG_FP7).contains(®) { 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 From<&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 capstone_sys::m68k_address_mode::*; use capstone_sys::m68k_op_type::*; use capstone_sys::m68k_reg::*; 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, 0b1111_1111_1111_1111_1111_1111 ); } #[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.iter().copied()), Ok(M68kRegisterBits { bits: 0 }) ); assert_eq!( M68kRegisterBits::from_register_iter([M68K_REG_D1].iter().copied()), Ok(M68kRegisterBits { bits: 0b10 }) ); assert_eq!( M68kRegisterBits::from_register_iter( [M68K_REG_D1, M68K_REG_A2, M68K_REG_FP7].iter().copied() ), 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().copied() ) .unwrap() ), M68kOperand::RegBits( M68kRegisterBits::from_register_iter( [M68K_REG_D0, M68K_REG_A2, M68K_REG_A3].iter().copied() ) .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)), }) ); } #[cfg(feature = "full")] #[test] fn extra_info() { use alloc::vec::Vec; use crate::instruction::*; 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() .flat_map(|x| x.iter()).copied() .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.13.0/src/arch/mips.rs000064400000000000000000000050301046102023000145410ustar 00000000000000//! 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 From<&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.13.0/src/arch/mod.rs000064400000000000000000000534231046102023000143610ustar 00000000000000//! Contains architecture-specific types and modules // We use explicit casts from c_int (and such) so the code compiles on platforms with different // integer widths #![allow(clippy::unnecessary_cast)] 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 ) ] [ ( riscv, RISCV ) ( mode: RiscV32, RiscV64, ) ( extra_modes: RiscVC, ) ( syntax: ) ( 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 ) ] [ ( bpf, BPF ) ( mode: Cbpf, Ebpf, ) ( extra_modes: ) ( syntax: ) ( both_endian: true ) ] ); }; } /// 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(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 = RiscVDetail, insn_detail = RiscVInsnDetail<'a>, op = RiscVOperand, /// Returns the RISCV details, if any => arch_name = riscv, ] [ 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, ] [ detail = BpfDetail, insn_detail = BpfInsnDetail<'a>, op = BpfOperand, /// Returns the BPF details, if any => arch_name = bpf, ] [ detail = SysZDetail, insn_detail = SysZInsnDetail<'a>, op = SysZOperand, /// Returns the SysZ details, if any => arch_name = sysz, ] ); }; } /// 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<&'a $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 { self.0.next().map($Operand::from) } fn size_hint(&self) -> (usize, Option) { self.0.size_hint() } } 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:expr )* ) ] )+ ) => { $( pub mod $arch; )+ } } // Define modules at the end so that they can see macro definitions arch_info_base!(define_arch_mods); capstone-0.13.0/src/arch/ppc.rs000064400000000000000000000115751046102023000143660ustar 00000000000000//! 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 PpcInsnDetail<'_> { /// 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 From<&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.13.0/src/arch/riscv.rs000064400000000000000000000043651046102023000147310ustar 00000000000000//! Contains riscv-specific types use core::convert::From; use core::{cmp, fmt, slice}; // XXX todo(tmfink): create rusty versions pub use capstone_sys::riscv_insn_group as RiscVInsnGroup; pub use capstone_sys::riscv_insn as RiscVInsn; pub use capstone_sys::riscv_reg as RiscVReg; use capstone_sys::{cs_riscv, cs_riscv_op, riscv_op_mem, riscv_op_type}; pub use crate::arch::arch_builder::riscv::*; use crate::arch::DetailsArchInsn; use crate::instruction::{RegId, RegIdInt}; /// Contains RISCV-specific details for an instruction pub struct RiscVInsnDetail<'a>(pub(crate) &'a cs_riscv); impl_PartialEq_repr_fields!(RiscVInsnDetail<'a> [ 'a ]; operands ); /// RISCV operand #[derive(Clone, Debug, Eq, PartialEq)] pub enum RiscVOperand { /// Register Reg(RegId), /// Immediate Imm(i64), /// Memory Mem(RiscVOpMem), /// Invalid Invalid, } impl Default for RiscVOperand { fn default() -> Self { RiscVOperand::Invalid } } /// RISCV memory operand #[derive(Debug, Copy, Clone)] pub struct RiscVOpMem(pub(crate) riscv_op_mem); impl RiscVOpMem { /// 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!(RiscVOpMem; base, disp ); impl cmp::Eq for RiscVOpMem {} impl From<&cs_riscv_op> for RiscVOperand { fn from(insn: &cs_riscv_op) -> RiscVOperand { match insn.type_ { riscv_op_type::RISCV_OP_REG => { RiscVOperand::Reg(RegId(unsafe { insn.__bindgen_anon_1.reg } as RegIdInt)) } riscv_op_type::RISCV_OP_IMM => RiscVOperand::Imm(unsafe { insn.__bindgen_anon_1.imm }), riscv_op_type::RISCV_OP_MEM => { RiscVOperand::Mem(RiscVOpMem(unsafe { insn.__bindgen_anon_1.mem })) } riscv_op_type::RISCV_OP_INVALID => RiscVOperand::Invalid, } } } def_arch_details_struct!( InsnDetail = RiscVInsnDetail; Operand = RiscVOperand; OperandIterator = RiscVOperandIterator; OperandIteratorLife = RiscVOperandIterator<'a>; [ pub struct RiscVOperandIterator<'a>(slice::Iter<'a, cs_riscv_op>); ] cs_arch_op = cs_riscv_op; cs_arch = cs_riscv; ); capstone-0.13.0/src/arch/sparc.rs000064400000000000000000000052271046102023000147110ustar 00000000000000//! 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 SparcInsnDetail<'_> { /// 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 From<&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.13.0/src/arch/sysz.rs000064400000000000000000000050511046102023000146040ustar 00000000000000//! Contains sysz-specific types use core::convert::From; use core::{cmp, fmt, slice}; // XXX todo(garnt): create rusty versions pub use capstone_sys::sysz_insn_group as SysZInsnGroup; pub use capstone_sys::sysz_insn as SysZInsn; pub use capstone_sys::sysz_reg as SysZReg; use capstone_sys::{cs_sysz, cs_sysz_op, sysz_op_mem, sysz_op_type}; pub use crate::arch::arch_builder::sysz::*; use crate::arch::DetailsArchInsn; use crate::instruction::{RegId, RegIdInt}; /// Contains sysz-specific details for an instruction pub struct SysZInsnDetail<'a>(pub(crate) &'a cs_sysz); impl_PartialEq_repr_fields!(SysZInsnDetail<'a> [ 'a ]; operands ); /// SysZ operand #[derive(Clone, Debug, Eq, PartialEq)] pub enum SysZOperand { /// Register Reg(RegId), /// Immediate Imm(i64), /// Memory Mem(SysZOpMem), /// Access Register AcReg(RegId), /// Invalid Invalid, } impl Default for SysZOperand { fn default() -> Self { SysZOperand::Invalid } } /// SysZ memory operand #[derive(Debug, Copy, Clone)] pub struct SysZOpMem(pub(crate) sysz_op_mem); impl SysZOpMem { /// Base register pub fn base(&self) -> u8 { self.0.base } /// Index register pub fn index(&self) -> u8 { self.0.index } /// BDLAddr operand pub fn length(&self) -> u64 { self.0.length } /// Disp value pub fn disp(&self) -> i64 { self.0.disp } } impl_PartialEq_repr_fields!(SysZOpMem; base, index, length, disp ); impl cmp::Eq for SysZOpMem {} impl From<&cs_sysz_op> for SysZOperand { fn from(insn: &cs_sysz_op) -> SysZOperand { match insn.type_ { sysz_op_type::SYSZ_OP_REG => { SysZOperand::Reg(RegId(unsafe { insn.__bindgen_anon_1.reg } as RegIdInt)) }, sysz_op_type::SYSZ_OP_IMM => SysZOperand::Imm(unsafe { insn.__bindgen_anon_1.imm }), sysz_op_type::SYSZ_OP_MEM => { SysZOperand::Mem(SysZOpMem(unsafe { insn.__bindgen_anon_1.mem })) }, sysz_op_type::SYSZ_OP_ACREG => { SysZOperand::AcReg(RegId(unsafe { insn.__bindgen_anon_1.reg } as RegIdInt)) }, sysz_op_type::SYSZ_OP_INVALID => SysZOperand::Invalid, } } } def_arch_details_struct!( InsnDetail = SysZInsnDetail; Operand = SysZOperand; OperandIterator = SysZOperandIterator; OperandIteratorLife = SysZOperandIterator<'a>; [ pub struct SysZOperandIterator<'a>(slice::Iter<'a, cs_sysz_op>); ] cs_arch_op = cs_sysz_op; cs_arch = cs_sysz; ); capstone-0.13.0/src/arch/tms320c64x.rs000064400000000000000000000261641046102023000153410ustar 00000000000000//! 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 Tms320c64xInsnDetail<'_> { /// 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 From<&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.13.0/src/arch/x86.rs000064400000000000000000000227311046102023000142250ustar 00000000000000//! 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, Eq)] pub struct X86Operand { /// Operand size pub size: u8, /// How is this operand accessed? /// /// NOTE: this field is always `None` if the "full" feataure is not enabled. 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, Eq)] 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 X86InsnDetail<'_> { /// 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) -> RegId { RegId(self.0.segment as RegIdInt) } /// 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 From<&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); } #[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; 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; 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.13.0/src/arch/xcore.rs000064400000000000000000000047111046102023000147160ustar 00000000000000//! 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 From<&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.13.0/src/capstone.rs000064400000000000000000000340121046102023000144720ustar 00000000000000use alloc::string::String; use core::convert::From; use core::marker::PhantomData; use libc::{c_int, c_void}; use capstone_sys::cs_opt_value::*; use capstone_sys::*; use crate::arch::CapstoneBuilder; use crate::constants::{Arch, Endian, ExtraMode, Mode, OptValue, Syntax}; use crate::error::*; use crate::instruction::{Insn, InsnDetail, InsnGroupId, InsnId, Instructions, RegId}; use {crate::ffi::str_from_cstr_ptr, alloc::string::ToString, libc::c_uint}; /// An instance of the capstone disassembler /// /// Create with an instance with [`.new()`](Self::new) and disassemble bytes with [`.disasm_all()`](Self::disasm_all). #[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(); /// ``` #[allow(clippy::new_ret_no_self)] 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 /// /// ``` /// # use capstone::prelude::*; /// # let cs = Capstone::new().x86().mode(arch::x86::ArchMode::Mode32).build().unwrap(); /// cs.disasm_all(b"\x90", 0x1000).unwrap(); /// ``` 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> { // SAFETY NOTE: `cs_disasm()` will write the error state into the // `struct cs_struct` (true form of the `self.csh`) `errnum` field. // CLAIM: since: // - `Capstone` is not `Send`/`Sync` // - The mutation is done through a `*mut c_void` (not through a const reference) // it *should* be safe to accept `&self` (instead of `&mut self`) in this method. let mut ptr: *mut cs_insn = core::ptr::null_mut(); let insn_count = unsafe { cs_disasm(self.csh(), code.as_ptr(), code.len(), addr, count, &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. /// Unavailable in Diet mode pub fn reg_name(&self, reg_id: RegId) -> Option { if cfg!(feature = "full") { 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) } else { None } } /// Converts an instruction id `insn_id` to a `String` containing the instruction name. /// Unavailable in Diet mode. /// Note: This function ignores the current syntax and uses the default syntax. pub fn insn_name(&self, insn_id: InsnId) -> Option { if cfg!(feature = "full") { 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) } else { None } } /// Converts a group id `group_id` to a `String` containing the group name. /// Unavailable in Diet mode pub fn group_name(&self, group_id: InsnGroupId) -> Option { if cfg!(feature = "full") { 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) } else { None } } /// Returns `Detail` structure for a given instruction /// /// Requires: /// /// 1. Instruction was created with detail enabled /// 2. Skipdata is disabled 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 { 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(cs_arch::from(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.13.0/src/constants.rs000064400000000000000000000242161046102023000146770ustar 00000000000000use 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; )* ) => { #[allow(clippy::redundant_closure_call)] 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; /// RISC-V => RISCV = CS_ARCH_RISCV; /// BPF => BPF = CS_ARCH_BPF; ); define_cs_enum_wrapper!( [ /// Disassembler modes => Mode = cs_mode ] /// 32-bit ARM => Arm = { cs_mode::CS_MODE_ARM }; /// 16-bit mode (X86) => Mode16 = { cs_mode::CS_MODE_16 }; /// 32-bit mode (X86) => Mode32 = { cs_mode::CS_MODE_32 }; /// 64-bit mode (X86, PPC) => Mode64 = { cs_mode::CS_MODE_64 }; /// ARM's Thumb mode, including Thumb-2 => Thumb = { cs_mode::CS_MODE_THUMB }; /// Mips II ISA => Mips2 = { cs_mode::CS_MODE_MIPS2 }; /// Mips III ISA => Mips3 = { cs_mode::CS_MODE_MIPS3 }; /// Mips32r6 ISA => Mips32R6 = { cs_mode::CS_MODE_MIPS32R6 }; /// Mips32 ISA (Mips) => Mips32 = { cs_mode::CS_MODE_MIPS32 }; /// Mips64 ISA (Mips) => Mips64 = { cs_mode::CS_MODE_MIPS64 }; /// SparcV9 mode (Sparc) => V9 = { cs_mode::CS_MODE_V9 }; /// Quad Processing eXtensions mode (PPC) => Qpx = { cs_mode::CS_MODE_QPX }; /// M68K 68000 mode => M68k000 = { cs_mode::CS_MODE_M68K_000 }; /// M68K 68010 mode => M68k010 = { cs_mode::CS_MODE_M68K_010 }; /// M68K 68020 mode => M68k020 = { cs_mode::CS_MODE_M68K_020 }; /// M68K 68030 mode => M68k030 = { cs_mode::CS_MODE_M68K_030 }; /// M68K 68040 mode => M68k040 = { cs_mode::CS_MODE_M68K_040 }; /// M680X Hitachi 6301,6303 mode => M680x6301 = { cs_mode::CS_MODE_M680X_6301 }; /// M680X Hitachi 6309 mode => M680x6309 = { cs_mode::CS_MODE_M680X_6309 }; /// M680X Motorola 6800,6802 mode => M680x6800 = { cs_mode::CS_MODE_M680X_6800 }; /// M680X Motorola 6801,6803 mode => M680x6801 = { cs_mode::CS_MODE_M680X_6801 }; /// M680X Motorola/Freescale 6805 mode => M680x6805 = { cs_mode::CS_MODE_M680X_6805 }; /// M680X Motorola/Freescale/NXP 68HC08 mode => M680x6808 = { cs_mode::CS_MODE_M680X_6808 }; /// M680X Motorola 6809 mode => M680x6809 = { cs_mode::CS_MODE_M680X_6809 }; /// M680X Motorola/Freescale/NXP 68HC11 mode => M680x6811 = { cs_mode::CS_MODE_M680X_6811 }; /// M680X Motorola/Freescale/NXP CPU12 => M680xCpu12 = { cs_mode::CS_MODE_M680X_CPU12 }; /// M680X Freescale/NXP HCS08 mode => M680xHcs08 = { cs_mode::CS_MODE_M680X_HCS08 }; /// RISC-V 32-bit mode => RiscV32 = { cs_mode::CS_MODE_RISCV32 }; /// RISC-V 64-bit mode => RiscV64 = { cs_mode::CS_MODE_RISCV64 }; /// Classic BPF mode => Cbpf = { cs_mode::CS_MODE_BPF_CLASSIC }; /// Extended BPF mode => Ebpf = { cs_mode::CS_MODE_BPF_EXTENDED }; /// Default mode for little-endian => Default = { cs_mode::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::CS_MODE_MCLASS }; /// ARMv8 A32 encodings for ARM. Works with `Arm` and `Thumb` modes. => V8 = { cs_mode::CS_MODE_V8 }; /// MicroMips mode. Works in `MIPS` mode. => Micro = { cs_mode::CS_MODE_MICRO }; /// RISC-V compressed instruction mode => RiscVC = { cs_mode::CS_MODE_RISCVC }; ); define_cs_enum_wrapper!( [ /// Disassembler endianness => Endian = cs_mode ] /// Little-endian mode => Little = { cs_mode::CS_MODE_LITTLE_ENDIAN }; /// Big-endian mode => Big = { cs_mode::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.13.0/src/error.rs000064400000000000000000000113201046102023000140040ustar 00000000000000//! Capstone errors 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; ); // Required until https://github.com/rust-lang/rust/issues/103765 is resolved #[cfg(feature = "std")] impl std::error::Error for Error {} 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.13.0/src/ffi.rs000064400000000000000000000027531046102023000134310ustar 00000000000000//! Functions useful for FFI use core::{slice, str}; use libc::{self, c_char}; // This function will go unused in Diet mode #[allow(unused)] /// Given a valid C-style, NUL terminated, UTF8-encoded string, returns a Rust `&str` /// /// Warnings: /// - No checks are made for: valid UTF-8 /// - This function "creates" a reference with an arbitrary lifetime, so be careful to limit the /// lifetime appropriately #[inline] #[allow(clippy::unnecessary_cast)] 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); /* ASSUMPTION: capstone returns a valid UTF-8 string */ Some(str::from_utf8_unchecked(view)) } #[cfg(test)] mod test { use super::*; #[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"\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.13.0/src/instruction.rs000064400000000000000000000354611046102023000152500ustar 00000000000000use alloc::{self, boxed::Box}; use core::convert::TryFrom; use core::fmt::{self, Debug, Display, Error, Formatter}; use core::marker::PhantomData; use core::ops::Deref; use core::slice; use core::str; use capstone_sys::*; use crate::arch::ArchDetail; use crate::constants::Arch; use crate::ffi::str_from_cstr_ptr; /// Represents a slice of [`Insn`] returned by [`Capstone`](crate::Capstone) `disasm*()` methods. /// /// To access inner [`&[Insn]`](Insn), use [`.as_ref()`](AsRef::as_ref). /// ``` /// # use capstone::Instructions; /// # use capstone::prelude::*; /// # let cs = Capstone::new().x86().mode(arch::x86::ArchMode::Mode32).build().unwrap(); /// let insns: Instructions = cs.disasm_all(b"\x55\x48\x8b\x05", 0x1000).unwrap(); /// for insn in insns.as_ref() { /// println!("{}", insn); /// } /// ``` #[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. /// /// To translate to a human-readable name, see [`Capstone::insn_name()`](crate::Capstone::insn_name). #[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. /// /// To translate to a human-readable name, see [`Capstone::group_name()`](crate::Capstone::group_name). #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] #[repr(transparent)] 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. /// /// To translate to a human-readable name, see [`Capstone::reg_name()`](crate::Capstone::reg_name). #[derive(Copy, Clone, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)] #[repr(transparent)] pub struct RegId(pub RegIdInt); impl RegId { /// Invalid Register pub const INVALID_REG: Self = Self(0); } /// 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_type::CS_AC_READ | cs_ac_type::CS_AC_WRITE).0; if (access.0 & unknown_flag_mask) != 0 { return Err(()); } let is_readable = (access & cs_ac_type::CS_AC_READ).0 != 0; let is_writable = (access & cs_ac_type::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 []) } } impl<'a> core::ops::Deref for Instructions<'a> { type Target = [Insn<'a>]; #[inline] fn deref(&self) -> &[Insn<'a>] { // SAFETY: `cs_insn` has the same memory layout as `Insn` unsafe { &*(self.0 as *const [cs_insn] as *const [Insn]) } } } impl<'a> AsRef<[Insn<'a>]> for Instructions<'a> { #[inline] fn as_ref(&self) -> &[Insn<'a>] { self.deref() } } impl Drop for Instructions<'_> { fn drop(&mut self) { if !self.is_empty() { unsafe { cs_free(self.0.as_mut_ptr(), self.len()); } } } } /// A single disassembled CPU instruction. /// /// # Detail /// /// To learn how to get more instruction details, see [`InsnDetail`]. #[repr(transparent)] 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 [`Insn`]. /// /// To get more detail about the instruction, enable extra details for the /// [`Capstone`](crate::Capstone) instance with /// [`Capstone::set_detail(True)`](crate::Capstone::set_detail) and use /// [`Capstone::insn_detail()`](crate::Capstone::insn_detail). /// /// ``` /// # use capstone::Instructions; /// # use capstone::prelude::*; /// let cs = Capstone::new() /// .x86() /// .mode(arch::x86::ArchMode::Mode32) /// .detail(true) // needed to enable detail /// .build() /// .unwrap(); /// let insns = cs.disasm_all(b"\x90", 0x1000).unwrap(); /// for insn in insns.as_ref() { /// println!("{}", insn); /// let insn_detail: InsnDetail = cs.insn_detail(insn).unwrap(); /// println!(" {:?}", insn_detail.groups()); /// } /// ``` /// /// # Arch-specific detail /// /// To get additional architecture-specific information, use the /// [`.arch_detail()`](Self::arch_detail) method to get an `ArchDetail` enum. /// pub struct InsnDetail<'a>(pub(crate) &'a cs_detail, pub(crate) Arch); #[allow(clippy::len_without_is_empty)] impl Insn<'_> { /// Create an `Insn` from a raw pointer to a [`capstone_sys::cs_insn`]. /// /// This function serves to allow integration with libraries which generate `capstone_sys::cs_insn`'s internally. /// /// # Safety /// /// Note that this function is unsafe, and assumes that you know what you are doing. In /// particular, it generates a lifetime for the `Insn` from nothing, and that lifetime is in /// no-way actually tied to the cs_insn itself. It is the responsibility of the caller to /// ensure that the resulting `Insn` lives only as long as the `cs_insn`. This function /// assumes that the pointer passed is non-null and a valid `cs_insn` pointer. /// /// The caller is fully responsible for the backing allocations lifetime, including freeing. pub unsafe fn from_raw(insn: *const cs_insn) -> Self { Self { insn: core::ptr::read(insn), _marker: PhantomData, } } /// The mnemonic for the instruction. /// Unavailable in Diet mode. #[inline] pub fn mnemonic(&self) -> Option<&str> { if cfg!(feature = "full") { unsafe { str_from_cstr_ptr(self.insn.mnemonic.as_ptr()) } } else { None } } /// The operand string associated with the instruction. /// Unavailable in Diet mode. #[inline] pub fn op_str(&self) -> Option<&str> { if cfg!(feature = "full") { unsafe { str_from_cstr_ptr(self.insn.op_str.as_ptr()) } } else { None } } /// Access instruction id #[inline] pub fn id(&self) -> InsnId { InsnId(self.insn.id) } /// Size of instruction (in bytes) #[inline] pub fn len(&self) -> usize { self.insn.size as usize } /// Instruction address #[inline] pub fn address(&self) -> u64 { self.insn.address } /// Byte-level representation of the instruction #[inline] 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 /// /// # Safety /// The [`cs_insn::detail`] pointer must be valid and non-null. #[inline] pub(crate) unsafe fn detail(&self, arch: Arch) -> InsnDetail { InsnDetail(&*self.insn.detail, arch) } } impl From<&Insn<'_>> for OwnedInsn<'_> { // SAFETY: assumes that `cs_detail` struct transitively only contains owned // types and no pointers, including the union over the architecture-specific // types. fn from(insn: &Insn<'_>) -> Self { let mut new = unsafe { <*const cs_insn>::read(&insn.insn as _) }; new.detail = if new.detail.is_null() { new.detail } else { unsafe { let new_detail = Box::new(*new.detail); Box::into_raw(new_detail) } }; Self { insn: new, _marker: PhantomData, } } } /// SAFETY: /// 1. [`OwnedInsn`] and [`Insn`] must be `#repr(transparent)` of [`cs_insn`] /// 2. all [`Insn`] methods must be safe to perform for an [`OwnedInsn`] impl<'a> Deref for OwnedInsn<'a> { type Target = Insn<'a>; fn deref(&self) -> &Self::Target { unsafe { &*(&self.insn as *const cs_insn as *const Insn) } } } /// A single disassembled CPU instruction that lives on the Rust heap. /// /// # Detail /// /// To learn how to get more instruction details, see [`InsnDetail`]. pub struct OwnedInsn<'a> { /// Inner cs_insn pub(crate) insn: cs_insn, /// Adds lifetime pub(crate) _marker: PhantomData<&'a InsnDetail<'a>>, } impl Debug for Insn<'_> { 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 Display for Insn<'_> { 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(()) } } impl Drop for OwnedInsn<'_> { fn drop(&mut self) { if let Some(ptr) = core::ptr::NonNull::new(self.insn.detail) { unsafe { drop(Box::from_raw(ptr.as_ptr())) } } } } impl Debug for OwnedInsn<'_> { fn fmt(&self, fmt: &mut Formatter) -> Result<(), Error> { Debug::fmt(&self.deref(), fmt) } } impl Display for OwnedInsn<'_> { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { Display::fmt(&self.deref(), fmt) } } impl InsnDetail<'_> { #[cfg(feature = "full")] /// Returns the implicit read registers pub fn regs_read(&self) -> &[RegId] { unsafe { &*(&self.0.regs_read[..self.0.regs_read_count as usize] as *const [RegIdInt] as *const [RegId]) } } #[cfg(feature = "full")] /// Returns the implicit write registers pub fn regs_write(&self) -> &[RegId] { unsafe { &*(&self.0.regs_write[..self.0.regs_write_count as usize] as *const [RegIdInt] as *const [RegId]) } } #[cfg(feature = "full")] /// Returns the groups to which this instruction belongs pub fn groups(&self) -> &[InsnGroupId] { unsafe { &*(&self.0.groups[..self.0.groups_count as usize] as *const [InsnGroupIdInt] as *const [InsnGroupId]) } } /// 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 })) } )* } } } 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] [RISCV, RiscVDetail, RiscVInsnDetail, riscv] [SPARC, SparcDetail, SparcInsnDetail, sparc] [TMS320C64X, Tms320c64xDetail, Tms320c64xInsnDetail, tms320c64x] [X86, X86Detail, X86InsnDetail, x86] [XCORE, XcoreDetail, XcoreInsnDetail, xcore] [BPF, BpfDetail, BpfInsnDetail, bpf] [SYSZ, SysZDetail, SysZInsnDetail, sysz] ); } } #[cfg(feature = "full")] impl Debug for InsnDetail<'_> { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { fmt.debug_struct("Detail") .field("regs_read", &self.regs_read()) .field("regs_write", &self.regs_write()) .field("groups", &self.groups()) .finish() } } #[cfg(not(feature = "full"))] impl<'a> Debug for InsnDetail<'a> { fn fmt(&self, fmt: &mut Formatter) -> fmt::Result { fmt.debug_struct("Detail").finish() } } impl Display for Instructions<'_> { 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.13.0/src/lib.rs000064400000000000000000000024011046102023000134210ustar 00000000000000#![doc = include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/README.md"))] #![cfg_attr(not(feature = "std"), no_std)] // derive Default on enums was not stable until 1.62.0 #![allow(clippy::derivable_impls)] // The `vec` macro cannot be imported directly since it conflicts with the `vec` module #[allow(unused_imports)] #[macro_use] extern crate alloc; #[cfg(any(test, not(feature = "std")))] #[macro_use] extern crate std; #[cfg(any(test, not(feature = "std")))] #[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.13.0/src/test.rs000064400000000000000000003224171046102023000136460ustar 00000000000000#![allow( clippy::approx_constant, clippy::too_many_arguments, clippy::type_complexity, clippy::upper_case_acronyms )] use core::{convert::TryInto, fmt::Debug}; use alloc::vec::Vec; #[cfg(feature = "full")] use {alloc::string::String, std::collections::HashSet}; use capstone_sys::cs_group_type; use libc::c_uint; use super::arch::*; use super::*; const X86_CODE: &[u8] = b"\x55\x48\x8b\x05\xb8\x13\x00\x00"; const ARM_CODE: &[u8] = b"\x55\x48\x8b\x05\xb8\x13\x00\x00"; const CBPF_CODE: &[u8] = b"\x94\x09\x00\x00\x37\x13\x03\x00\ \x87\x00\x00\x00\x00\x00\x00\x00\ \x07\x00\x00\x00\x00\x00\x00\x00\ \x16\x00\x00\x00\x00\x00\x00\x00\ \x80\x00\x00\x00\x00\x00\x00\x00"; const EBPF_CODE: &[u8] = b"\x97\x09\x00\x00\x37\x13\x03\x00\ \xdc\x02\x00\x00\x20\x00\x00\x00\ \x30\x00\x00\x00\x00\x00\x00\x00\ \xdb\x3a\x00\x01\x00\x00\x00\x00\ \x84\x02\x00\x00\x00\x00\x00\x00\ \x6d\x33\x17\x02\x00\x00\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(); #[cfg(feature = "full")] { 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) => panic!("Couldn't disasm instructions: {}", err), }, Err(e) => { panic!("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(); #[cfg(feature = "full")] { 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) => panic!("Couldn't disasm instructions: {}", err), }, Err(e) => { panic!("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()); } #[cfg(feature = "full")] #[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 => panic!("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 => panic!("Couldn't get instruction name"), } assert_eq!(cs.group_name(InsnGroupId(1)), Some(String::from("jump"))); let reg_id = RegId(250); if cs.reg_name(reg_id).is_some() { panic!("invalid register worked") } let insn_id = InsnId(6000); if cs.insn_name(insn_id).is_some() { panic!("invalid instruction worked") } assert_eq!(cs.group_name(InsnGroupId(250)), None); } Err(e) => { panic!("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); } #[cfg(feature = "full")] #[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 in insns.iter() { let detail = cs.insn_detail(insn).expect("Unable to get detail"); let groups = detail.groups(); for insn_group_id in &insn_group_ids { let insn_group = InsnGroupId(*insn_group_id as InsnGroupIdInt); assert!(!groups.contains(&insn_group)); } } } } #[allow(unused)] 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 && cfg!(feature = "full") { // 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"); } #[cfg(feature = "full")] 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 && cfg!(feature = "full") { // 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"); } #[cfg(feature = "full")] 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 for \"{}\" (bytes={:02x?})", insn, insn.bytes() ); } #[cfg(feature = "full")] /// 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, ) { 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().iter().copied().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.try_into() .unwrap_or_else(|_| panic!("Failed to convert {:?} to RegIdInt", x)), ) }) .collect(); expected_regs.sort_unstable(); let mut regs: Vec = $actual_regs.iter().map(|&x| x.into()).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" ); } type ExpectedInsns<'a, R> = ( &'a str, &'a [u8], &'a [cs_group_type::Type], &'a [R], &'a [R], ); #[allow(unused)] fn instructions_match_group>( cs: &mut Capstone, expected_insns: &[ExpectedInsns], has_default_syntax: bool, ) { let insns_buf: Vec = expected_insns .iter() .flat_map(|&(_, bytes, _, _, _)| bytes) .copied() .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<&Insn> = insns.iter().collect(); // Check number of instructions assert_eq!(insns.len(), expected_insns.len()); #[cfg(feature = "full")] 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) .copied() .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(|info| info.bytes).copied().collect(); // Details required to get groups information cs.set_detail(true).unwrap(); // todo(tmfink) eliminate check if info.is_empty() { // 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: &[ExpectedInsns] = &[ ("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); } #[allow(unused)] 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); #[cfg(feature = "full")] assert_eq!(insns.iter().next().unwrap().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().copied(); 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().copied(), endian).unwrap(); let mut cs_raw_endian_set = Capstone::new_raw(arch, mode, extra_mode.iter().copied(), 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); } #[allow(unused)] #[derive(Copy, Clone)] #[cfg_attr(feature = "full", derive(Debug))] struct DetailedInsnInfo<'a, T: 'a + Into> { pub mnemonic: &'a str, pub bytes: &'a [u8], pub operands: &'a [T], } #[allow(clippy::upper_case_acronyms)] 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().copied(); 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); } #[cfg(feature = "full")] #[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> = expected_insns .iter() .map(|&(mnemonic, _, bytes, groups, reads, writes)| { (mnemonic, bytes, groups, reads, writes) }) .collect(); let expected_insns_att: Vec> = 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().copied()) .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().copied()) .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_read = ArmOperand { op_type: Reg(RegId(ArmReg::ARM_REG_R0 as RegIdInt)), access: Some(RegAccessType::ReadOnly), ..Default::default() }; let r0_op_write = ArmOperand { op_type: Reg(RegId(ArmReg::ARM_REG_R0 as RegIdInt)), access: Some(RegAccessType::WriteOnly), ..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)), access: Some(RegAccessType::ReadOnly), ..Default::default() }, ArmOperand { op_type: Mem(ArmOpMem(arm_op_mem { base: ArmReg::ARM_REG_SP, index: 0, scale: 1, disp: -4, lshift: 0, })), access: Some(RegAccessType::WriteOnly), ..Default::default() }, ], ), // andeq r0, r0, r0 DII::new( "andeq", b"\x00\x00\x00\x00", &[r0_op_write.clone(), r0_op_read.clone(), r0_op_read.clone()], ), // str r8, [r2, #-0x3e0]! DII::new( "str", b"\xe0\x83\x22\xe5", &[ ArmOperand { op_type: Reg(RegId(ArmReg::ARM_REG_R8 as RegIdInt)), access: Some(RegAccessType::ReadOnly), ..Default::default() }, ArmOperand { op_type: Mem(ArmOpMem(arm_op_mem { base: ArmReg::ARM_REG_R2, index: 0, scale: 1, disp: -992, lshift: 0, })), access: Some(RegAccessType::WriteOnly), ..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_read.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_write, 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)), access: Some(RegAccessType::ReadOnly), ..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::*; 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 // todo(tmfink): https://github.com/capstone-engine/capstone/issues/1881 /* DII::new( "mrs", b"\x09\x00\x38\xd5", &[ Arm64Operand { op_type: Reg(RegId(ARM64_REG_X9 as RegIdInt)), ..Default::default() }, Arm64Operand { op_type: Sys(ARM64_SYSREG_MIDR_EL1 as u32), ..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, Arm64Operand { vector_index: Some(3), op_type: Reg(RegId(ARM64_REG_V0 as RegIdInt)), vas: ARM64_VAS_1S, ..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), op_type: Reg(RegId(ARM64_REG_V5 as RegIdInt)), vas: ARM64_VAS_1D, ..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, x1, 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() .copied(), ) .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() .copied(), ) .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().copied()) .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: 0, 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_CR0EQ as RegIdInt)), Reg(RegId(PPC_REG_CR0UN as RegIdInt)), Reg(RegId(PPC_REG_CR1LT 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_systemz_detail() { use crate::arch::sysz::SysZOperand::*; use crate::arch::sysz::SysZReg::*; use crate::arch::sysz::*; use capstone_sys::sysz_op_mem; test_arch_mode_endian_insns_detail( &mut Capstone::new() .sysz() .mode(sysz::ArchMode::Default) .build() .unwrap(), Arch::SYSZ, Mode::Default, None, &[], &[ // br %r7 DII::new("br", b"\x07\xf7", &[Reg(RegId(SYSZ_REG_7 as RegIdInt))]), // ear %r7, %a8 DII::new( "ear", b"\xb2\x4f\x00\x78", &[ Reg(RegId(SYSZ_REG_7 as RegIdInt)), Reg(RegId(SYSZ_REG_A8 as RegIdInt)), ], ), // adb %f0, 0 DII::new( "adb", b"\xed\x00\x00\x00\x00\x1a", &[Reg(RegId(SYSZ_REG_F0 as RegIdInt)), Imm(0)], ), // afi %r0, -0x80000000 DII::new( "afi", b"\xc2\x09\x80\x00\x00\x00", &[Reg(RegId(SYSZ_REG_0 as RegIdInt)), Imm(-0x80000000)], ), // a %r0, 0xfff(%r15, %r1) DII::new( "a", b"\x5a\x0f\x1f\xff", &[ Reg(RegId(SYSZ_REG_0 as RegIdInt)), Mem(SysZOpMem(sysz_op_mem { base: SYSZ_REG_1 as u8, index: SYSZ_REG_15 as u8, disp: 0xfff, length: 0, })), ], ), ], ); } #[test] fn test_arch_tms320c64x_detail() { use crate::arch::tms320c64x::{ Tms320c64xFuntionalUnit, Tms320c64xMemDirection, Tms320c64xMemDisplayType, Tms320c64xMemModify, Tms320c64xOpMem, Tms320c64xOperand::*, Tms320c64xReg::*, }; 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 as c_uint, disp: 0x46, unit: Tms320c64xFuntionalUnit::L as c_uint, scaled: false as c_uint, disptype: Tms320c64xMemDisplayType::Constant as c_uint, direction: Tms320c64xMemDirection::Forward as c_uint, modify: Tms320c64xMemModify::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 as c_uint, disp: 0x1, unit: Tms320c64xFuntionalUnit::L as c_uint, scaled: true as c_uint, disptype: Tms320c64xMemDisplayType::Constant as c_uint, direction: Tms320c64xMemDirection::Forward as c_uint, modify: Tms320c64xMemModify::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 as c_uint, disp: 0x46, unit: Tms320c64xFuntionalUnit::L as c_uint, scaled: false as c_uint, disptype: Tms320c64xMemDisplayType::Constant as c_uint, direction: Tms320c64xMemDirection::Forward as c_uint, modify: Tms320c64xMemModify::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 as c_uint, disp: 0x4, unit: Tms320c64xFuntionalUnit::L as c_uint, scaled: true as c_uint, disptype: Tms320c64xMemDisplayType::Constant as c_uint, direction: Tms320c64xMemDirection::Forward as c_uint, modify: Tms320c64xMemModify::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 as c_uint, disp: TMS320C64X_REG_A4 as c_uint, unit: Tms320c64xFuntionalUnit::D as c_uint, scaled: false as c_uint, disptype: Tms320c64xMemDisplayType::Register as c_uint, direction: Tms320c64xMemDirection::Forward as c_uint, modify: Tms320c64xMemModify::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), ], ), ], ); } #[test] fn test_arch_riscv() { test_arch_mode_endian_insns( &mut Capstone::new() .riscv() .mode(riscv::ArchMode::RiscV64) .extra_mode([riscv::ArchExtraMode::RiscVC].iter().copied()) .build() .unwrap(), Arch::RISCV, Mode::RiscV64, None, &[ExtraMode::RiscVC], &[ ("addi", b"\x93\x00\x31\x00"), ("add", b"\xb3\x00\x31\x00"), ("ld", b"\x03\xb2\x82\x00"), ("c.ebreak", b"\x02\x90"), ("c.addi", b"\x05\x04"), ("c.add", b"\x2a\x94"), ("c.ld", b"\x0c\x66"), ], ); } #[test] fn test_arch_riscv_detail() { use crate::arch::riscv::RiscVOperand::*; use crate::arch::riscv::RiscVReg::*; use crate::arch::riscv::*; use capstone_sys::riscv_op_mem; test_arch_mode_endian_insns_detail( &mut Capstone::new() .riscv() .mode(riscv::ArchMode::RiscV64) .extra_mode([riscv::ArchExtraMode::RiscVC].iter().copied()) .build() .unwrap(), Arch::RISCV, Mode::RiscV64, None, &[ExtraMode::RiscVC], &[ // addi x1, x2, 3 DII::new( "addi", b"\x93\x00\x31\x00", &[ Reg(RegId(RISCV_REG_X1 as RegIdInt)), Reg(RegId(RISCV_REG_X2 as RegIdInt)), Imm(3), ], ), // add x1, x2, x3 DII::new( "add", b"\xb3\x00\x31\x00", &[ Reg(RegId(RISCV_REG_X1 as RegIdInt)), Reg(RegId(RISCV_REG_X2 as RegIdInt)), Reg(RegId(RISCV_REG_X3 as RegIdInt)), ], ), // ld x4, 8(x5) DII::new( "ld", b"\x03\xb2\x82\x00", &[ Reg(RegId(RISCV_REG_X4 as RegIdInt)), Mem(RiscVOpMem(riscv_op_mem { base: RISCV_REG_X5 as c_uint, disp: 8, })), ], ), // c.ebreak DII::new("c.ebreak", b"\x02\x90", &[]), // c.addi x8, 1 DII::new( "c.addi", b"\x05\x04", &[Reg(RegId(RISCV_REG_X8 as RegIdInt)), Imm(1)], ), // c.add x8, x10 DII::new( "c.add", b"\x2a\x94", &[ Reg(RegId(RISCV_REG_X8 as RegIdInt)), Reg(RegId(RISCV_REG_X10 as RegIdInt)), ], ), // c.ld x11, 8(x12) DII::new( "c.ld", b"\x0c\x66", &[ // Upstream bug? Doesn't seem to use Mem type. Reg(RegId(RISCV_REG_X11 as RegIdInt)), Imm(8), Reg(RegId(RISCV_REG_X12 as RegIdInt)), ], ), ], ); } #[test] fn test_insn_size_and_alignment() { use capstone_sys::cs_insn; // Ensure that Insn and cs_insn have the same size // and alignment so that they can be safely transmuted // from and to each other: assert_eq!( core::mem::size_of::(), core::mem::size_of::(), "sizeof(Insn) == sizeof(cs_insn)" ); assert_eq!( core::mem::align_of::(), core::mem::align_of::(), "alignof(Insn) == alignof(cs_insn)" ); // Make sure that conversion is valid: 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_slice: &[Insn] = &insns; assert_eq!(insns.len(), insns_slice.len()); for (original, transmuted) in insns.iter().zip(insns_slice.iter()) { assert_eq!(original.id(), transmuted.id()); } } #[test] fn test_insn_from_raw() { use capstone_sys::cs_insn; let cs = Capstone::new() .x86() .mode(x86::ArchMode::Mode64) .build() .unwrap(); let insns = cs.disasm_all(X86_CODE, START_TEST_ADDR).unwrap(); for insn in insns.iter() { let raw_insn = &insn.insn as *const cs_insn; let from_raw_insn = unsafe { Insn::from_raw(raw_insn) }; assert_eq!(format!("{:?}", from_raw_insn), format!("{:?}", insn)); } } #[test] fn test_owned_insn() { let cs = Capstone::new() .x86() .mode(x86::ArchMode::Mode64) .detail(true) .build() .unwrap(); let insns = cs.disasm_all(X86_CODE, START_TEST_ADDR).unwrap(); let owned: Vec = insns.iter().map(|i| i.into()).collect(); for (insn, owned) in insns.iter().zip(&owned) { assert_eq!(format!("{:?}", insn), format!("{:?}", owned)); } } /// Print register names fn reg_names(cs: &Capstone, regs: &[RegId]) -> String { let names: Vec = regs.iter().map(|&x| cs.reg_name(x).unwrap()).collect(); names.join(", ") } /// Print instruction group names fn group_names(cs: &Capstone, regs: &[InsnGroupId]) -> String { let names: Vec = regs.iter().map(|&x| cs.group_name(x).unwrap()).collect(); names.join(", ") } #[test] fn test_cbpf() { let cs = Capstone::new() .bpf() .mode(bpf::ArchMode::Cbpf) .endian(Endian::Little) .detail(true) .build() .unwrap(); let insns = cs.disasm_all(CBPF_CODE, 0x1000); match insns { Ok(ins) => { for i in ins.as_ref() { println!(); eprintln!("{}", 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 (name, message) in output.iter() { eprintln!("{:4}{:12} {}", "", name, message); } println!("{:4}operands: {}", "", ops.len()); for op in ops { eprintln!("{:8}{:?}", "", op); } } } Err(e) => { panic!("{:?}", e); } } } #[test] fn test_ebpf() { let cs = Capstone::new() .bpf() .mode(bpf::ArchMode::Ebpf) .endian(Endian::Little) .detail(true) .build() .unwrap(); let insns = cs.disasm_all(EBPF_CODE, 0x1000); match insns { Ok(ins) => { for i in ins.as_ref() { println!(); eprintln!("{}", 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 (name, message) in output.iter() { eprintln!("{:4}{:12} {}", "", name, message); } println!("{:4}operands: {}", "", ops.len()); for op in ops { eprintln!("{:8}{:?}", "", op); } } } Err(e) => { panic!("{:?}", e); } } } #[test] fn test_arch_bpf_detail() { use crate::arch::bpf::BpfOperand::*; use crate::arch::bpf::BpfReg::*; use crate::arch::bpf::*; use capstone_sys::*; test_arch_mode_endian_insns_detail( &mut Capstone::new() .bpf() .mode(bpf::ArchMode::Ebpf) .endian(Endian::Little) .detail(true) .build() .unwrap(), Arch::BPF, Mode::Ebpf, None, &[], &[ // r1 = 0x1 DII::new( "mov64", b"\xb7\x01\x00\x00\x01\x00\x00\x00", &[Reg(RegId(BPF_REG_R1 as RegIdInt)), Imm(1)], ), // r0 = *(u32 *)(r10 - 0xc) DII::new( "ldxw", b"\x61\xa0\xf4\xff\x00\x00\x00\x00", &[ Reg(RegId(BPF_REG_R0 as RegIdInt)), Mem(BpfOpMem(bpf_op_mem { base: BPF_REG_R10, disp: 0xfff4, })), ], ), // *(u32 *)(r10 - 0xc) = r1 DII::new( "stxw", b"\x63\x1a\xf4\xff\x00\x00\x00\x00", &[ Mem(BpfOpMem(bpf_op_mem { base: BPF_REG_R10, disp: 0xfff4, })), Reg(RegId(BPF_REG_R1 as RegIdInt)), ], ), // exit DII::new("exit", b"\x95\x00\x00\x00\x00\x00\x00\x00", &[]), ], ); test_arch_mode_endian_insns_detail( &mut Capstone::new() .bpf() .mode(bpf::ArchMode::Cbpf) .endian(Endian::Little) .detail(true) .build() .unwrap(), Arch::BPF, Mode::Cbpf, None, &[], &[ DII::new("txa", b"\x87\x00\x00\x00\x00\x00\x00\x00", &[]), DII::new( "ret", b"\x16\x00\x00\x00\x00\x00\x00\x00", &[Reg(RegId(BPF_REG_A as RegIdInt))], ), ], ); } capstone-0.13.0/test-inputs/x86_64.bin_ls.bin000064400000000000000000002111311046102023000167140ustar 00000000000000AWAVAUATUSHHH>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މ[