diff --git a/bindings/const_generator.py b/bindings/const_generator.py index 5f8d6a24f813ec4575c7323416aa98d0b472e5d3..db69d55a9b71bca5d118810d09d190233e917c65 100644 --- a/bindings/const_generator.py +++ b/bindings/const_generator.py @@ -32,7 +32,7 @@ template = { ] }, 'rust': { - 'header': "// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [%s_const.rs]\nextern crate libc;\n\n", + 'header': "#![allow(non_camel_case_types)]\n// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [%s_const.rs]\nuse libc;\n", 'footer': "", # prefixes for constant filenames of all archs - case sensitive 'arm.h': 'keystone', @@ -47,65 +47,59 @@ template = { 'keystone.h': 'keystone', 'comment_open': '/*', 'comment_close': '*/', - 'out_file': './rust/src/%s_const.rs', + 'out_file': './rust/keystone-sys/src/%s_const.rs', 'rules': [ { - 'regex': r'(API|ARCH)_.*', + 'regex': r'(API)_.*', 'pre': '\n', - 'line_format': 'pub const KS_{0} : u32 = {1};\n', + 'line_format': 'pub const {0}: u32 = {1};\n', 'fn': (lambda x: x), - 'post': '\n', }, { 'regex': r'MODE_.*', - 'pre': 'bitflags! {{\n\tpub flags Mode : u32 {{\n', - 'line_format': '\t\tconst {0} = {1},\n', - 'fn': (lambda x: x), - 'post': '\t}\n}\n', + 'pre': '\n' + + 'bitflags! {{\n' + + '#[repr(C)]\n' + + ' pub struct Mode: u32 {{\n', + 'line_format': ' const {0} = {1};\n', + 'fn': (lambda x: '_'.join(x.split('_')[1:]) if not re.match(r'MODE_\d+', x) else x), + 'post': ' }\n}', }, { 'regex': r'ARCH_.*', - 'pre': '#[derive(Debug, PartialEq, Clone, Copy)]\n' + + 'pre': '\n' + + '#[repr(C)]\n' + + '#[derive(Debug, PartialEq, Clone, Copy)]\n' + 'pub enum Arch {{\n', - 'line_format': '\t{0},\n', - 'fn': (lambda x: '_'.join(x.split('_')[1:])), - 'post': '}\n\n', - }, - { 'regex': r'ARCH_.*', - 'pre': 'impl Arch {{\n\t#[inline]\n\tpub fn val(&self) -> u32 {{\n\t\tmatch *self {{\n', - 'line_format': '\t\t\tArch::{0} => {1},\n', + 'line_format': ' {0} = {1},\n', 'fn': (lambda x: '_'.join(x.split('_')[1:])), - 'post': '\t\t}\n\t}\n}\n', + 'post': '}\n', }, { - 'regex': r'OPT_([A-Z]+)$', - 'pre': '#[derive(Debug, PartialEq, Clone, Copy)]\n' + + 'regex': r'(OPT_([A-Z]+)|OPT_SYM_RESOLVER)$', + 'pre': '#[repr(C)]\n' + + '#[derive(Debug, PartialEq, Clone, Copy)]\n' + 'pub enum OptionType {{\n', - 'line_format': '\t{0},\n', - 'fn': (lambda x: '_'.join(x.split('_')[1:])), - 'post': '\tMAX,\n' + - '}\n', - }, - { - 'regex': r'OPT_([A-Z]+)$', - 'pre': 'impl OptionType {{\n\t#[inline]\n\tpub fn val(&self) -> u32 {{\n\t\tmatch *self {{\n', - 'line_format': '\t\t\tOptionType::{0} => {1},\n', + 'line_format': ' {0} = {1},\n', 'fn': (lambda x: '_'.join(x.split('_')[1:])), - 'post': '\t\t\tOptionType::MAX => 99\n' + - '\t\t}\n\t}\n}\n', + 'post': '}\n', }, { - 'regex': r'OPT_([A-Z]+\_)+[A-Z]+', - 'pre': 'bitflags! {{\n\tpub flags OptionValue : libc::size_t {{\n', - 'line_format': '\t\tconst {0} = {1},\n', - 'fn': (lambda x: x), - 'post': '\t}\n}\n', + 'regex': r'OPT_(?!SYM)([A-Z]+\_)+[A-Z]+', + 'pre': 'bitflags! {{\n' + '#[repr(C)]\n' + + ' pub struct OptionValue: libc::size_t {{\n', + 'line_format': ' const {0} = {1};\n', + 'fn': (lambda x: '_'.join(x.split('_')[1:])), + 'post': ' }\n}\n', }, { - 'regex': r'ERR_.*', - 'pre': 'bitflags! {{\n\tpub flags Error : u32 {{\n', - 'line_format': '\t\tconst {0} = {1},\n', - 'fn': (lambda x: x), - 'post': '\t}\n}\n', + 'regex': r'ERR_(.*)', + 'pre': 'bitflags! {{\n' + + '#[repr(C)]\n' + + ' pub struct Error: u32 {{\n', + 'line_format': ' const {0} = {1};\n', + 'fn': (lambda x: '_'.join(x.split('_')[1:])), + 'post': ' }\n}', }, ], }, @@ -128,7 +122,7 @@ template = { 'out_file': './go/keystone/%s_const.go', 'rules': [ { - 'regex': r'(API)_.*', + 'regex': r'API_.*', 'pre': 'const (\n', 'line_format': '\t{0} = {1}\n', 'fn': (lambda x: x), diff --git a/bindings/rust/.gitignore b/bindings/rust/.gitignore index ce906b8f897bcd1482cbfbd2b1205dfb4678ab8c..7a0c5013b951ff24b193adc3c07ef501bb3011c5 100644 --- a/bindings/rust/.gitignore +++ b/bindings/rust/.gitignore @@ -1,3 +1,4 @@ Cargo.lock -target/ +**/target/** *.bk +keystone-sys/keystone diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml index 24ea58fe6b6de2d07b15303d91a51f6cbe65b61e..b41e5988af1ea6dec0dbcf2c0fc37d7eaa5d2ab5 100644 --- a/bindings/rust/Cargo.toml +++ b/bindings/rust/Cargo.toml @@ -1,12 +1,26 @@ [package] name = "keystone" -version = "0.9.0" -authors = ["Remco Verhoef <remco.verhoef@dutchcoders.io>"] +version = "0.9.1" +authors = [ + "Remco Verhoef <remco.verhoef@dutchcoders.io>", + "Tasuku SUENAGA a.k.a. gunyarakun <tasuku-s-github@titech.ac>" +] description = "Rust bindings for the keystone-engine" license = "GPL-2.0" readme = "README.md" -include = ["src/*", "tests/*", "Cargo.toml", "COPYING", "README.md"] +repository = "https://github.com/keystone-engine/keystone" +include = [ + "src/*", "tests/*", "Cargo.toml", "COPYING", "README.md", + "keystone-sys/*" +] [dependencies] -libc = "0.2.*" -bitflags = "0.7.0" +bitflags = "1.0" +libc = "0.2" +keystone-sys = { path = "keystone-sys", version = "0.9.1" } + +[features] +default = [] + +use_system_keystone = ["keystone-sys/use_system_keystone"] +build_keystone_cmake = ["keystone-sys/build_keystone_cmake"] diff --git a/bindings/rust/Makefile b/bindings/rust/Makefile index 3606619707ac66275376568225ded240a735ca5b..7a3f1690cac9f93d1d3c61a589a5d1a0a0c8f7d4 100644 --- a/bindings/rust/Makefile +++ b/bindings/rust/Makefile @@ -1,23 +1,23 @@ # Rust binding for Keystone engine. Remco Verhoef <remco@honeytrap.io> -ifndef BUILDDIR -OBJDIR = ./build -else -OBJDIR = $(abspath $(BUILDDIR))/obj/bindings/rust -endif +.PHONY: gen_const build package clean check -.PHONY: gen_const install clean check +build: keystone-sys/keystone + cargo build -vv -gen_const: - cd .. && python const_generator.py rust - cargo fmt +package: keystone-sys/keystone + cd keystone-sys && cargo package -vv + cargo package -vv -install: - cargo build +keystone-sys/keystone: + rsync -a ../.. keystone-sys/keystone --exclude bindings --filter ":- ../../.gitignore" clean: - rm -rf target/ + rm -rf target/ keystone-sys/target/ keystone-sys/keystone/ check: cargo test +gen_const: + cd .. && python const_generator.py rust + cargo fmt diff --git a/bindings/rust/README.md b/bindings/rust/README.md index 3a40f0790264a698695dc873105250f1bdc373ec..32cb7a0e1514b3457c08440090c3cde22425433e 100644 --- a/bindings/rust/README.md +++ b/bindings/rust/README.md @@ -7,10 +7,10 @@ extern crate keystone; use keystone::*; fn main() { - let engine = Keystone::new(Arch::X86, MODE_32) + let engine = Keystone::new(Arch::X86, Mode::MODE_32) .expect("Could not initialize Keystone engine"); - engine.option(OptionType::SYNTAX, OPT_SYNTAX_NASM) + engine.option(OptionType::SYNTAX, OptionValue::SYNTAX_NASM) .expect("Could not set option to nasm syntax"); let result = engine.asm("mov ah, 0x80".to_string(), 0) @@ -24,6 +24,24 @@ fn main() { } ``` +## Installation +Add a dependency line into `Cargo.toml`. + +``` +[dependencies] +keystone = "0.10.0" +``` + +This package attempts to build keystone. That requires cmake and c/c++ compiler. + +If you want to use keystone already installed in the system, specify `use_system_keystone` feature on `Cargo.toml`. + +``` +[dependencies.keystone] +version = "0.10.0" +features = ["use_system_keystone"] +``` + ## Testing ``` cargo test @@ -31,6 +49,7 @@ cargo test ## Contributors - Remco Verhoef (@remco_verhoef) +- Tasuku SUENAGA a.k.a. gunyarakun (@tasukuchan) Special thanks to: - Sébastien Duquette (@ekse) for his [unicorn-rs](https://github.com/ekse/unicorn-rs) binding diff --git a/bindings/rust/examples/asm.rs b/bindings/rust/examples/asm.rs index 4676a645f9ae5e9c286c73f39ca750f4fa48ad03..86f32620bbfdb741729102d5d6f8970a75fc1f1e 100644 --- a/bindings/rust/examples/asm.rs +++ b/bindings/rust/examples/asm.rs @@ -2,13 +2,15 @@ extern crate keystone; use keystone::*; fn main() { - let engine = Keystone::new(Arch::X86, MODE_32) - .expect("Could not initialize Keystone engine"); + let engine = + Keystone::new(Arch::X86, Mode::MODE_32).expect("Could not initialize Keystone engine"); - engine.option(OptionType::SYNTAX, OPT_SYNTAX_NASM) + engine + .option(OptionType::SYNTAX, OptionValue::SYNTAX_NASM) .expect("Could not set option to nasm syntax"); - let result = engine.asm("mov ah, 0x80".to_string(), 0) + let result = engine + .asm("mov ah, 0x80".to_string(), 0) .expect("Could not assemble"); println!("ASM result: {}", result); @@ -17,4 +19,3 @@ fn main() { println!("Error: {}", err); } } - diff --git a/bindings/rust/keystone-sys/Cargo.toml b/bindings/rust/keystone-sys/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..f791327c54c129ff5e266fa347cc4c82f20db569 --- /dev/null +++ b/bindings/rust/keystone-sys/Cargo.toml @@ -0,0 +1,30 @@ +[package] +name = "keystone-sys" +version = "0.9.0" +authors = [ + "Remco Verhoef <remco.verhoef@dutchcoders.io>", + "Tasuku SUENAGA a.k.a. gunyarakun <tasuku-s-github@titech.ac>" +] +description = "Rust bindings for the keystone assembler" +repository = "https://github.com/keystone-engine/keystone" +documentation = "https://docs.rs/keystone/" +license = "GPL-2.0" +build = "build.rs" +exclude = [ + "keystone/build/**" +] + +[build-dependencies] +build-helper = "0.1" +os_type = "2.0" +pkg-config = { optional = true, version = "0.3" } + +[dependencies] +bitflags = "1.0" +libc = "0.2" + +[features] +default = [] + +use_system_keystone = ["pkg-config"] +build_keystone_cmake = [] diff --git a/bindings/rust/keystone-sys/build.rs b/bindings/rust/keystone-sys/build.rs new file mode 100644 index 0000000000000000000000000000000000000000..2f698ab7035eeca10c8acffe517d96ccddb9b316 --- /dev/null +++ b/bindings/rust/keystone-sys/build.rs @@ -0,0 +1,67 @@ +#[cfg(feature = "use_system_keystone")] +extern crate pkg_config; + +use std::env; +use std::path::PathBuf; +use std::process::Command; + +fn build_with_cmake() { + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + let cmake_dir = PathBuf::from("keystone"); + let build_dir = cmake_dir.join("build"); + + if !cmake_dir.exists() { + run(Command::new("ln").arg("-s").arg("../../..").arg("keystone")); + } + + run(Command::new("mkdir") + .current_dir(&cmake_dir) + .arg("-p") + .arg("build")); + + run(Command::new("../make-share.sh").current_dir(&build_dir)); + + run(Command::new("cmake").current_dir(&build_dir).args(&[ + &format!("-DCMAKE_INSTALL_PREFIX={}", out_dir.display()), + "-DCMAKE_BUILD_TYPE=Release", + "-DBUILD_LIBS_ONLY=1", + "-DCMAKE_OSX_ARCHITECTURES=", + "-DBUILD_SHARED_LIBS=ON", + "-DLLVM_TARGET_ARCH=host", + "-G", + "Unix Makefiles", + "..", + ])); + + run(Command::new("make").current_dir(&build_dir).arg("install")); + + println!("cargo:rustc-link-search=native={}/lib", out_dir.display()); + println!("cargo:rustc-link-lib=keystone"); +} + +fn main() { + if cfg!(feature = "use_system_keystone") { + #[cfg(feature = "use_system_keystone")] + pkg_config::find_library("keystone").expect("Could not find system keystone"); + } else { + build_with_cmake(); + } +} + +fn run(cmd: &mut Command) { + println!("run: {:?}", cmd); + let status = match cmd.status() { + Ok(s) => s, + Err(ref e) => fail(&format!("failed to execute command: {}", e)), + }; + if !status.success() { + fail(&format!( + "command did not execute successfully, got: {}", + status + )); + } +} + +fn fail(s: &str) -> ! { + panic!("\n{}\n\nbuild script failed, must exit now", s); +} diff --git a/bindings/rust/keystone-sys/src/keystone_const.rs b/bindings/rust/keystone-sys/src/keystone_const.rs new file mode 100644 index 0000000000000000000000000000000000000000..c81f3240c0cf953365bb4619888c4a9c530c57b3 --- /dev/null +++ b/bindings/rust/keystone-sys/src/keystone_const.rs @@ -0,0 +1,119 @@ +#![allow(non_camel_case_types)] +// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [keystone_const.rs] +use libc; + +pub const API_MAJOR: u32 = 0; +pub const API_MINOR: u32 = 9; + +bitflags! { +#[repr(C)] + pub struct Mode: u32 { + const LITTLE_ENDIAN = 0; + const BIG_ENDIAN = 1073741824; + const ARM = 1; + const THUMB = 16; + const V8 = 64; + const MICRO = 16; + const MIPS3 = 32; + const MIPS32R6 = 64; + const MIPS32 = 4; + const MIPS64 = 8; + const MODE_16 = 2; + const MODE_32 = 4; + const MODE_64 = 8; + const PPC32 = 4; + const PPC64 = 8; + const QPX = 16; + const SPARC32 = 4; + const SPARC64 = 8; + const V9 = 16; + } +} + +#[repr(C)] +#[derive(Debug, PartialEq, Clone, Copy)] +pub enum Arch { + ARM = 1, + ARM64 = 2, + MIPS = 3, + X86 = 4, + PPC = 5, + SPARC = 6, + SYSTEMZ = 7, + HEXAGON = 8, + EVM = 9, + MAX = 10, +} + +#[repr(C)] +#[derive(Debug, PartialEq, Clone, Copy)] +pub enum OptionType { + SYNTAX = 1, + SYM_RESOLVER = 2, +} + +bitflags! { +#[repr(C)] + pub struct OptionValue: libc::size_t { + const SYNTAX_INTEL = 1; + const SYNTAX_ATT = 2; + const SYNTAX_NASM = 4; + const SYNTAX_MASM = 8; + const SYNTAX_GAS = 16; + const SYNTAX_RADIX16 = 32; + } +} + +bitflags! { +#[repr(C)] + pub struct Error: u32 { + const ASM = 128; + const ASM_ARCH = 512; + const OK = 0; + const NOMEM = 1; + const ARCH = 2; + const HANDLE = 3; + const MODE = 4; + const VERSION = 5; + const OPT_INVALID = 6; + const ASM_EXPR_TOKEN = 128; + const ASM_DIRECTIVE_VALUE_RANGE = 129; + const ASM_DIRECTIVE_ID = 130; + const ASM_DIRECTIVE_TOKEN = 131; + const ASM_DIRECTIVE_STR = 132; + const ASM_DIRECTIVE_COMMA = 133; + const ASM_DIRECTIVE_RELOC_NAME = 134; + const ASM_DIRECTIVE_RELOC_TOKEN = 135; + const ASM_DIRECTIVE_FPOINT = 136; + const ASM_DIRECTIVE_UNKNOWN = 137; + const ASM_DIRECTIVE_EQU = 138; + const ASM_DIRECTIVE_INVALID = 139; + const ASM_VARIANT_INVALID = 140; + const ASM_EXPR_BRACKET = 141; + const ASM_SYMBOL_MODIFIER = 142; + const ASM_SYMBOL_REDEFINED = 143; + const ASM_SYMBOL_MISSING = 144; + const ASM_RPAREN = 145; + const ASM_STAT_TOKEN = 146; + const ASM_UNSUPPORTED = 147; + const ASM_MACRO_TOKEN = 148; + const ASM_MACRO_PAREN = 149; + const ASM_MACRO_EQU = 150; + const ASM_MACRO_ARGS = 151; + const ASM_MACRO_LEVELS_EXCEED = 152; + const ASM_MACRO_STR = 153; + const ASM_MACRO_INVALID = 154; + const ASM_ESC_BACKSLASH = 155; + const ASM_ESC_OCTAL = 156; + const ASM_ESC_SEQUENCE = 157; + const ASM_ESC_STR = 158; + const ASM_TOKEN_INVALID = 159; + const ASM_INSN_UNSUPPORTED = 160; + const ASM_FIXUP_INVALID = 161; + const ASM_LABEL_INVALID = 162; + const ASM_FRAGMENT_INVALID = 163; + const ASM_INVALIDOPERAND = 512; + const ASM_MISSINGFEATURE = 513; + const ASM_MNEMONICFAIL = 514; + } +} diff --git a/bindings/rust/keystone-sys/src/lib.rs b/bindings/rust/keystone-sys/src/lib.rs new file mode 100644 index 0000000000000000000000000000000000000000..c063c48a336042c98f473a963361b5a49edbf647 --- /dev/null +++ b/bindings/rust/keystone-sys/src/lib.rs @@ -0,0 +1,59 @@ +//! Keystone Assembler Engine (www.keystone-engine.org) */ +//! By Nguyen Anh Quynh <aquynh@gmail.com>, 2016 */ +//! Rust bindings by Remco Verhoef <remco@dutchcoders.io>, 2016 */ +//! + +#[macro_use] +extern crate bitflags; +extern crate libc; + +pub mod keystone_const; + +use std::fmt; +use std::ffi::CStr; +use std::os::raw::c_char; +use keystone_const::{Arch, Error, Mode, OptionType, OptionValue}; + +#[allow(non_camel_case_types)] +pub type ks_handle = libc::size_t; + +#[link(name = "keystone")] +extern "C" { + pub fn ks_version(major: *const u32, minor: *const u32) -> u32; + pub fn ks_arch_supported(arch: Arch) -> bool; + pub fn ks_open(arch: Arch, mode: Mode, engine: *mut ks_handle) -> Error; + pub fn ks_asm( + engine: ks_handle, + string: *const c_char, + address: u64, + encoding: *mut *mut libc::c_uchar, + encoding_size: *mut libc::size_t, + stat_count: *mut libc::size_t, + ) -> u32; + pub fn ks_errno(engine: ks_handle) -> Error; + pub fn ks_strerror(error_code: Error) -> *const c_char; + pub fn ks_option(engine: ks_handle, opt_type: OptionType, value: OptionValue) -> Error; + pub fn ks_close(engine: ks_handle); + pub fn ks_free(encoding: *mut libc::c_uchar); +} + +impl Error { + pub fn msg(&self) -> String { + error_msg(*self) + } +} + +/// Return a string describing given error code. +pub fn error_msg(error: Error) -> String { + unsafe { + CStr::from_ptr(ks_strerror(error)) + .to_string_lossy() + .into_owned() + } +} + +impl fmt::Display for Error { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.msg()) + } +} diff --git a/bindings/rust/src/enums.rs b/bindings/rust/src/enums.rs deleted file mode 100644 index 45516be71266e26d601742ec671ee6d93763b056..0000000000000000000000000000000000000000 --- a/bindings/rust/src/enums.rs +++ /dev/null @@ -1,101 +0,0 @@ -pub use keystone_const::*; - -#[repr(u32)] -#[derive(Debug, PartialEq, Clone, Copy)] -pub enum Arch { - ARM = KS_ARCH_ARM, - ARM64 =KS_ARCH_ARM64, - MIPS = KS_ARCH_MIPS, - X86 = KS_ARCH_X86, - PPC = KS_ARCH_PPC, - SPARC = KS_ARCH_SPARC, - SYSTEMZ = KS_ARCH_SYSTEMZ, - HEXAGON = KS_ARCH_HEXAGON, -} - -#[repr(u32)] -#[derive(Debug, PartialEq, Clone, Copy)] -pub enum Mode { - LittleEndian = KS_MODE_LITTLE_ENDIAN, - BigEndian = KS_MODE_BIG_ENDIAN, - /* - ARM = KS_MODE_ARM, - THUMB = KS_MODE_THUMB, - V8 = KS_MODE_V8, - MICRO = KS_MODE_MICRO, - MIPS3 = KS_MODE_MIPS3, - MIPS32R6 = KS_MODE_MIPS32R6, - MIPS32 = KS_MODE_MIPS32, - MIPS64 = KS_MODE_MIPS64, - SIZE_16= KS_MODE_16, - */ - Mode32 = KS_MODE_32, - /* - SIZE_64 = KS_MODE_64, - PPC32 = KS_MODE_PPC32, - PPC64 = KS_MODE_PPC64, - QPX = KS_MODE_QPX, - SPARC32 = KS_MODE_SPARC32, - SPARC64 = KS_MODE_SPARC64, - V9 = KS_MODE_V9, - */ -} - -#[repr(u32)] -#[derive(Debug, PartialEq, Clone, Copy)] -pub enum Error { - Ok = KS_ERR_OK, - // ASM = KS_ERR_ASM, - // ASM_ARCH = KS_ERR_ASM_ARCH, - NoMem = KS_ERR_NOMEM, - Arch = KS_ERR_ARCH, - Handle = KS_ERR_HANDLE, - Mode = KS_ERR_MODE, - Version = KS_ERR_VERSION, - OptionInvalid = KS_ERR_OPT_INVALID, - AsmExprToken = KS_ERR_ASM_EXPR_TOKEN, - AsmDirectiveValueRange = KS_ERR_ASM_DIRECTIVE_VALUE_RANGE, - AsmDirectiveToken = KS_ERR_ASM_DIRECTIVE_ID, - DirectiveToken = KS_ERR_ASM_DIRECTIVE_TOKEN, - DirectiveStr = KS_ERR_ASM_DIRECTIVE_STR, - DirectiveComma = KS_ERR_ASM_DIRECTIVE_COMMA, - DirectiveRelocName = KS_ERR_ASM_DIRECTIVE_RELOC_NAME, - DirectiveRelocToken = KS_ERR_ASM_DIRECTIVE_RELOC_TOKEN, - DirectiveFPoint = KS_ERR_ASM_DIRECTIVE_FPOINT, - VariantInvalid = KS_ERR_ASM_VARIANT_INVALID, - ExprBracket = KS_ERR_ASM_EXPR_BRACKET, - SymbolModifier = KS_ERR_ASM_SYMBOL_MODIFIER, - RParen = KS_ERR_ASM_RPAREN, - StatToken = KS_ERR_ASM_STAT_TOKEN, - AsmUnsupported = KS_ERR_ASM_UNSUPPORTED, - AsmMacroToken = KS_ERR_ASM_MACRO_TOKEN, - AsmMacroParen = KS_ERR_ASM_MACRO_PAREN, - AsmMacroEQU = KS_ERR_ASM_MACRO_EQU, - AsmMacroArgs = KS_ERR_ASM_MACRO_ARGS, - AsmMacroLevelsExceed = KS_ERR_ASM_MACRO_LEVELS_EXCEED, - AsmEscBackslash = KS_ERR_ASM_ESC_BACKSLASH, - AsmEscOctal = KS_ERR_ASM_ESC_OCTAL, - AsmEscSequence = KS_ERR_ASM_ESC_SEQUENCE, - AsmInvalidOperand = KS_ERR_ASM_INVALIDOPERAND, - AsmMissingFeature = KS_ERR_ASM_MISSINGFEATURE, - AsmMNemonicFail = KS_ERR_ASM_MNEMONICFAIL, -} - -#[repr(u32)] -#[derive(Debug, PartialEq, Clone, Copy)] -pub enum OptionType { - Syntax = KS_OPT_SYNTAX, - SYNTAX2 = KS_OPT_SYNTAX_NASM, -} - -#[repr(u32)] -#[derive(Debug, PartialEq, Clone, Copy)] -pub enum OptionValue { - SyntaxIntel = KS_OPT_SYNTAX_INTEL, - SyntaxATT = KS_OPT_SYNTAX_ATT, - SyntaxNASM = KS_OPT_SYNTAX_NASM, - SyntaxMASM = KS_OPT_SYNTAX_MASM, - SyntaxGAS = KS_OPT_SYNTAX_GAS, -} - - diff --git a/bindings/rust/src/ffi.rs b/bindings/rust/src/ffi.rs deleted file mode 100644 index 1fd2389557fe0d725e551ff857dbbe08a382c39d..0000000000000000000000000000000000000000 --- a/bindings/rust/src/ffi.rs +++ /dev/null @@ -1,28 +0,0 @@ -//! Keystone Assembler Engine (www.keystone-engine.org) */ -//! By Nguyen Anh Quynh <aquynh@gmail.com>, 2016 */ -//! Rust bindings by Remco Verhoef <remco@dutchcoders.io>, 2016 */ -//! - -use libc; -use std::os::raw::c_char; - -use ks_handle; - -#[link(name = "keystone")] -extern "C" { - pub fn ks_version(major: *const u32, minor: *const u32) -> u32; - pub fn ks_arch_supported(arch: u32) -> bool; - pub fn ks_open(arch: u32, mode: u32, engine: *mut ks_handle) -> u32; - pub fn ks_asm(engine: ks_handle, - string: *const c_char, - address: u64, - encoding: *mut *mut libc::c_uchar, - encoding_size: *mut libc::size_t, - stat_count: *mut libc::size_t) - -> u32; - pub fn ks_errno(engine: ks_handle) -> u32; - pub fn ks_strerror(error_code: u32) -> *const c_char; - pub fn ks_option(engine: ks_handle, type_: u32, value: libc::size_t) -> u32; - pub fn ks_close(engine: ks_handle); - pub fn ks_free(encoding: *mut libc::c_uchar); -} diff --git a/bindings/rust/src/keystone_const.rs b/bindings/rust/src/keystone_const.rs deleted file mode 100644 index d7f78963b98c4c96953b520757d683f39f1d5842..0000000000000000000000000000000000000000 --- a/bindings/rust/src/keystone_const.rs +++ /dev/null @@ -1,156 +0,0 @@ -// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [keystone_const.rs] -extern crate libc; - - -pub const KS_API_MAJOR : u32 = 0; -pub const KS_API_MINOR : u32 = 9; -pub const KS_ARCH_ARM : u32 = 1; -pub const KS_ARCH_ARM64 : u32 = 2; -pub const KS_ARCH_MIPS : u32 = 3; -pub const KS_ARCH_X86 : u32 = 4; -pub const KS_ARCH_PPC : u32 = 5; -pub const KS_ARCH_SPARC : u32 = 6; -pub const KS_ARCH_SYSTEMZ : u32 = 7; -pub const KS_ARCH_HEXAGON : u32 = 8; -pub const KS_ARCH_EVM : u32 = 9; -pub const KS_ARCH_MAX : u32 = 10; - - -bitflags! { - pub flags Mode : u32 { - const MODE_LITTLE_ENDIAN = 0, - const MODE_BIG_ENDIAN = 1073741824, - const MODE_ARM = 1, - const MODE_THUMB = 16, - const MODE_V8 = 64, - const MODE_MICRO = 16, - const MODE_MIPS3 = 32, - const MODE_MIPS32R6 = 64, - const MODE_MIPS32 = 4, - const MODE_MIPS64 = 8, - const MODE_16 = 2, - const MODE_32 = 4, - const MODE_64 = 8, - const MODE_PPC32 = 4, - const MODE_PPC64 = 8, - const MODE_QPX = 16, - const MODE_SPARC32 = 4, - const MODE_SPARC64 = 8, - const MODE_V9 = 16, - } -} - -#[derive(Debug, PartialEq, Clone, Copy)] -pub enum Arch { - ARM, - ARM64, - MIPS, - X86, - PPC, - SPARC, - SYSTEMZ, - HEXAGON, - EVM, - MAX, -} - - -impl Arch { - #[inline] - pub fn val(&self) -> u32 { - match *self { - Arch::ARM => 1, - Arch::ARM64 => 2, - Arch::MIPS => 3, - Arch::X86 => 4, - Arch::PPC => 5, - Arch::SPARC => 6, - Arch::SYSTEMZ => 7, - Arch::HEXAGON => 8, - Arch::EVM => 9, - Arch::MAX => 10, - } - } -} - -#[derive(Debug, PartialEq, Clone, Copy)] -pub enum OptionType { - SYNTAX, - MAX, -} - -impl OptionType { - #[inline] - pub fn val(&self) -> u32 { - match *self { - OptionType::SYNTAX => 1, - OptionType::MAX => 99 - } - } -} - -bitflags! { - pub flags OptionValue : libc::size_t { - const OPT_SYM_RESOLVER = 2, - const OPT_SYNTAX_INTEL = 1, - const OPT_SYNTAX_ATT = 2, - const OPT_SYNTAX_NASM = 4, - const OPT_SYNTAX_MASM = 8, - const OPT_SYNTAX_GAS = 16, - const OPT_SYNTAX_RADIX16 = 32, - } -} - -bitflags! { - pub flags Error : u32 { - const ERR_ASM = 128, - const ERR_ASM_ARCH = 512, - const ERR_OK = 0, - const ERR_NOMEM = 1, - const ERR_ARCH = 2, - const ERR_HANDLE = 3, - const ERR_MODE = 4, - const ERR_VERSION = 5, - const ERR_OPT_INVALID = 6, - const ERR_ASM_EXPR_TOKEN = 128, - const ERR_ASM_DIRECTIVE_VALUE_RANGE = 129, - const ERR_ASM_DIRECTIVE_ID = 130, - const ERR_ASM_DIRECTIVE_TOKEN = 131, - const ERR_ASM_DIRECTIVE_STR = 132, - const ERR_ASM_DIRECTIVE_COMMA = 133, - const ERR_ASM_DIRECTIVE_RELOC_NAME = 134, - const ERR_ASM_DIRECTIVE_RELOC_TOKEN = 135, - const ERR_ASM_DIRECTIVE_FPOINT = 136, - const ERR_ASM_DIRECTIVE_UNKNOWN = 137, - const ERR_ASM_DIRECTIVE_EQU = 138, - const ERR_ASM_DIRECTIVE_INVALID = 139, - const ERR_ASM_VARIANT_INVALID = 140, - const ERR_ASM_EXPR_BRACKET = 141, - const ERR_ASM_SYMBOL_MODIFIER = 142, - const ERR_ASM_SYMBOL_REDEFINED = 143, - const ERR_ASM_SYMBOL_MISSING = 144, - const ERR_ASM_RPAREN = 145, - const ERR_ASM_STAT_TOKEN = 146, - const ERR_ASM_UNSUPPORTED = 147, - const ERR_ASM_MACRO_TOKEN = 148, - const ERR_ASM_MACRO_PAREN = 149, - const ERR_ASM_MACRO_EQU = 150, - const ERR_ASM_MACRO_ARGS = 151, - const ERR_ASM_MACRO_LEVELS_EXCEED = 152, - const ERR_ASM_MACRO_STR = 153, - const ERR_ASM_MACRO_INVALID = 154, - const ERR_ASM_ESC_BACKSLASH = 155, - const ERR_ASM_ESC_OCTAL = 156, - const ERR_ASM_ESC_SEQUENCE = 157, - const ERR_ASM_ESC_STR = 158, - const ERR_ASM_TOKEN_INVALID = 159, - const ERR_ASM_INSN_UNSUPPORTED = 160, - const ERR_ASM_FIXUP_INVALID = 161, - const ERR_ASM_LABEL_INVALID = 162, - const ERR_ASM_FRAGMENT_INVALID = 163, - const ERR_ASM_INVALIDOPERAND = 512, - const ERR_ASM_MISSINGFEATURE = 513, - const ERR_ASM_MNEMONICFAIL = 514, - } -} - diff --git a/bindings/rust/src/lib.rs b/bindings/rust/src/lib.rs index b113e1fc150ec3112af0d1a72a9f408fc20e627f..5bcd5bfdded6ac0aaf4cca2170afb233547477d9 100644 --- a/bindings/rust/src/lib.rs +++ b/bindings/rust/src/lib.rs @@ -4,56 +4,28 @@ //! //! ```rust //! extern crate keystone; -//! use keystone::{Keystone, Arch, OptionType}; +//! use keystone::{Keystone, Arch, Mode, OptionType, OptionValue}; //! //! fn main() { -//! let engine = Keystone::new(Arch::X86, keystone::MODE_32) +//! let engine = Keystone::new(Arch::X86, Mode::MODE_32) //! .expect("Could not initialize Keystone engine"); -//! engine.option(OptionType::SYNTAX, keystone::OPT_SYNTAX_NASM) +//! engine.option(OptionType::SYNTAX, OptionValue::SYNTAX_NASM) //! .expect("Could not set option to nasm syntax"); //! let result = engine.asm("mov ah, 0x80".to_string(), 0) //! .expect("Could not assemble"); //! } //! ``` -#![doc(html_root_url="https://keystone/doc/here/v1")] +#![doc(html_root_url = "https://keystone/doc/here/v1")] -#[macro_use] -extern crate bitflags; +extern crate keystone_sys as ffi; extern crate libc; -pub mod ffi; -// pub mod enums; -pub mod keystone_const; -// pub mod arm64_const; -// pub mod arm_const; -// pub mod hexagon_const; -// pub mod mips_const; -// pub mod ppc_const; -// pub mod sparc_const; -// pub mod systemz_const; -// pub mod x86_const; - -use std::ffi::CStr; use std::ffi::CString; use std::fmt; -pub use keystone_const::*; - -#[allow(non_camel_case_types)] -pub type ks_handle = libc::size_t; - -impl Error { - pub fn msg(&self) -> String { - error_msg(*self) - } -} - -impl fmt::Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.msg()) - } -} +pub use ffi::keystone_const::*; +pub use ffi::ks_handle; #[derive(Debug, PartialEq)] pub struct AsmResult { @@ -73,7 +45,7 @@ impl fmt::Display for AsmResult { } pub fn bindings_version() -> (u32, u32) { - (KS_API_MAJOR, KS_API_MINOR) + (API_MAJOR, API_MINOR) } /// Return tuple `(major, minor)` API version numbers. @@ -89,12 +61,15 @@ pub fn version() -> (u32, u32) { /// Return tuple `(major, minor)` API version numbers. pub fn arch_supported(arch: Arch) -> bool { - unsafe { ffi::ks_arch_supported(arch.val()) } + unsafe { ffi::ks_arch_supported(arch) } } -/// Return a string describing given error code. pub fn error_msg(error: Error) -> String { - unsafe { CStr::from_ptr(ffi::ks_strerror(error.bits())).to_string_lossy().into_owned() } + unsafe { + CStr::from_ptr(ffi::ks_strerror(error.bits())) + .to_string_lossy() + .into_owned() + } } pub struct Keystone { @@ -105,15 +80,13 @@ impl Keystone { /// Create new instance of Keystone engine. pub fn new(arch: Arch, mode: Mode) -> Result<Keystone, Error> { if version() != bindings_version() { - return Err(ERR_VERSION); + return Err(Error::VERSION); } let mut handle: ks_handle = 0; - let err = Error::from_bits_truncate(unsafe { - ffi::ks_open(arch.val(), mode.bits(), &mut handle) - }); - if err == ERR_OK { + let err = unsafe { ffi::ks_open(arch, mode, &mut handle) }; + if err == Error::OK { Ok(Keystone { handle: handle }) } else { Err(err) @@ -122,8 +95,8 @@ impl Keystone { /// Report the last error number when some API function fail. pub fn error(&self) -> Result<(), Error> { - let err = Error::from_bits_truncate(unsafe { ffi::ks_errno(self.handle) }); - if err == ERR_OK { + let err = unsafe { ffi::ks_errno(self.handle) }; + if err == Error::OK { Ok(()) } else { Err(err) @@ -131,11 +104,9 @@ impl Keystone { } /// Set option for Keystone engine at runtime - pub fn option(&self, type_: OptionType, value: OptionValue) -> Result<(), Error> { - let err = Error::from_bits_truncate(unsafe { - ffi::ks_option(self.handle, type_.val(), value.bits()) - }); - if err == ERR_OK { + pub fn option(&self, option_type: OptionType, value: OptionValue) -> Result<(), Error> { + let err = unsafe { ffi::ks_option(self.handle, option_type, value) }; + if err == Error::OK { Ok(()) } else { Err(err) @@ -155,15 +126,17 @@ impl Keystone { let mut ptr: *mut libc::c_uchar = std::ptr::null_mut(); let err = Error::from_bits_truncate(unsafe { - ffi::ks_asm(self.handle, - s.as_ptr(), - address, - &mut ptr, - &mut size, - &mut stat_count) + ffi::ks_asm( + self.handle, + s.as_ptr(), + address, + &mut ptr, + &mut size, + &mut stat_count, + ) }); - if err == ERR_OK { + if err == Error::OK { let bytes_slice = unsafe { std::slice::from_raw_parts(ptr, size) }; let bytes = bytes_slice.to_vec(); @@ -174,10 +147,10 @@ impl Keystone { Ok(AsmResult { size: size as u32, stat_count: stat_count as u32, - bytes + bytes, }) } else { - let err = Error::from_bits_truncate(unsafe { ffi::ks_errno(self.handle) }); + let err = unsafe { ffi::ks_errno(self.handle) }; Err(err) } } diff --git a/bindings/rust/tests/keystone.rs b/bindings/rust/tests/keystone.rs index ed96ecec2f6f06187a08a5b6bba4a366284be9b1..f5d3e39db34eb2067b68c2d3ff1ec36c998d4920 100644 --- a/bindings/rust/tests/keystone.rs +++ b/bindings/rust/tests/keystone.rs @@ -1,6 +1,6 @@ extern crate keystone; -use keystone::{Keystone, Arch, OptionType}; +use keystone::{Arch, Error, Keystone, Mode, OptionType, OptionValue}; #[test] fn version() { @@ -18,31 +18,33 @@ fn arch_supported() { fn asm() { let asm = String::from("mov ah, 0x80\n nop\n mov al, 0x81\n"); - let engine = Keystone::new(Arch::X86, keystone::MODE_LITTLE_ENDIAN | keystone::MODE_32) + let engine = Keystone::new(Arch::X86, Mode::LITTLE_ENDIAN | Mode::MODE_32) .expect("Could not initialize Keystone engine"); - engine.option(OptionType::SYNTAX, keystone::OPT_SYNTAX_NASM) + engine + .option(OptionType::SYNTAX, OptionValue::SYNTAX_NASM) .expect("Could not set option to nasm syntax"); - let result = engine.asm(asm, 0) - .expect("Could not assemble"); + let result = engine.asm(asm, 0).expect("Could not assemble"); print!("{0:?}", result.bytes); - assert_eq!(result.bytes,[0xb4,0x80, 0x90, 0xb0, 0x81]); + assert_eq!(result.bytes, [0xb4, 0x80, 0x90, 0xb0, 0x81]); } #[test] fn invalid_asm() { let asm = String::from("invalid asm"); - let engine = Keystone::new(Arch::X86, keystone::MODE_32) - .expect("Could not initialize Keystone engine"); + let engine = + Keystone::new(Arch::X86, Mode::MODE_32).expect("Could not initialize Keystone engine"); let result = engine.asm(asm, 0); let err = result.unwrap_err(); - assert_eq!(err, keystone::ERR_ASM_MNEMONICFAIL); + assert_eq!(err, Error::ASM_MNEMONICFAIL); assert_eq!(err.msg(), "Invalid mnemonic (KS_ERR_ASM_MNEMONICFAIL)"); - assert_eq!(format!("{}", err), "Invalid mnemonic (KS_ERR_ASM_MNEMONICFAIL)"); + assert_eq!( + format!("{}", err), + "Invalid mnemonic (KS_ERR_ASM_MNEMONICFAIL)" + ); } -