diff --git a/bindings/Makefile b/bindings/Makefile index 2e11971aaeca63b90a39cc15b772436ca75f74ce..f166b210bb9e94e3bc156b2e299c2160c8488951 100644 --- a/bindings/Makefile +++ b/bindings/Makefile @@ -11,10 +11,14 @@ SAMPLE_M68K = $(TMPDIR)/sample_m68k SAMPLE_SPARC = $(TMPDIR)/sample_sparc SAMPLE_X86 = $(TMPDIR)/sample_x86 -.PHONY: all expected python +.PHONY: all expected python rust go nodejs ruby all: cd python && $(MAKE) gen_const + python const_generator.py rust + python const_generator.py go + python const_generator.py python + python const_generator.py ruby python const_generator.py nodejs cd ruby && $(MAKE) gen_const @@ -42,6 +46,18 @@ python: FORCE python python/sample_x86.py > $(SAMPLE_X86)_o $(MAKE) sample_diff +ruby: FORCE + cd ruby && $(MAKE) + +nodejs: FORCE + cd nodejs && $(MAKE) + +rust: FORCE + cd rust && $(MAKE) + +go: FORCE + cd go && $(MAKE) + sample_diff: FORCE $(DIFF) $(SAMPLE_ARM)_e $(SAMPLE_ARM)_o $(DIFF) $(SAMPLE_ARM64)_e $(SAMPLE_ARM64)_o @@ -53,8 +69,16 @@ sample_diff: FORCE clean: rm -rf $(TMPDIR) cd python && $(MAKE) clean + cd rust && $(MAKE) clean + cd go && $(MAKE) clean + cd nodejs && $(MAKE) clean + cd ruby && $(MAKE) clean check: make -C python check + make -C rust check + make -C go check + make -C nodejs check + make -C ruby check FORCE: diff --git a/bindings/const_generator.py b/bindings/const_generator.py index beababa15c820823497ede5b59558bd82f5616cd..c8f1f43580139a1cd20072b62781c3ee541aae7c 100644 --- a/bindings/const_generator.py +++ b/bindings/const_generator.py @@ -10,11 +10,151 @@ ks_err_val = { 'KS_ERR_ASM': '128', 'KS_ERR_ASM_ARCH': '512' } include = [ 'arm.h', 'arm64.h', 'mips.h', 'x86.h', 'sparc.h', 'ppc.h', 'systemz.h', 'hexagon.h', 'keystone.h' ] +def CamelCase(s): + # return re.sub(r'(\w)+\_?', lambda m:m.group(0).capitalize(), s) + return ''.join(''.join([w[0].upper(), w[1:].lower()]) for w in s.split('_')) + template = { + 'rust': { + 'header': "// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [%s_const.rs]\nextern crate libc;\n\n", + 'footer': "", + # prefixes for constant filenames of all archs - case sensitive + 'arm.h': 'keystone', + 'arm64.h': 'keystone', + 'mips.h': 'keystone', + 'x86.h': 'keystone', + 'sparc.h': 'keystone', + 'systemz.h': 'keystone', + 'ppc.h': 'keystone', + 'hexagon.h': 'keystone', + 'keystone.h': 'keystone', + 'comment_open': '/*', + 'comment_close': '*/', + 'out_file': './rust/src/%s_const.rs', + 'rules': [ + { + 'regex': r'(API|ARCH)_.*', + 'pre': '\n', + 'line_format': 'pub const KS_{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', + }, + { + 'regex': r'ARCH_.*', + 'pre': '#[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', + 'fn': (lambda x: '_'.join(x.split('_')[1:])), + 'post': '\t\t}\n\t}\n}\n', + }, + { + 'regex': r'OPT_([A-Z]+)$', + 'pre': '#[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', + 'fn': (lambda x: '_'.join(x.split('_')[1:])), + 'post': '\t\t\tOptionType::MAX => 99\n' + + '\t\t}\n\t}\n}\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'ERR_.*', + 'pre': 'bitflags! {\n\tpub flags Error : u32 {\n', + 'line_format': '\t\tconst {0} = {1},\n', + 'fn': (lambda x: x), + 'post': '\t}\n}\n', + }, + ], + }, + 'go': { + 'header': "package keystone\n// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [%s_const.go]\n\n", + 'footer': "", + # prefixes for constant filenames of all archs - case sensitive + 'arm.h': 'arm', + 'arm64.h': 'arm64', + 'mips.h': 'mips', + 'x86.h': 'x86', + 'sparc.h': 'sparc', + 'systemz.h': 'systemz', + 'ppc.h': 'ppc', + 'hexagon.h': 'hexagon', + 'keystone.h': 'keystone', + 'comment_open': '/*', + 'comment_close': '*/', + 'out_file': './go/keystone/%s_const.go', + 'rules': [ + { + 'regex': r'(API)_.*', + 'pre': 'const (\n', + 'line_format': '\t\t{0} = {1}\n', + 'fn': (lambda x: x), + 'post': ')\n', + }, + { 'regex': r'MODE_.*', + 'pre': 'const (\n', + 'line_format': '\t\t{0} Mode = {1}\n', + 'fn': (lambda x: x), + 'post': ')\n', + }, + { + 'regex': r'ARCH_.*', + 'pre': 'const (\n', + 'line_format': '\t\t{0} Architecture = {1}\n', + 'fn': (lambda x: x), + 'post': ')\n', + }, + { + 'regex': r'OPT_([A-Z]+)$', + 'pre': 'const (\n', + 'line_format': '\t\t{0} OptionType = {1}\n', + 'fn': (lambda x: x), + 'post': ')\n', + }, + { + 'regex': r'OPT_([A-Z]+\_)+[A-Z]+', + 'pre': 'const (\n', + 'line_format': '\t\t{0} OptionValue = {1}\n', + 'fn': (lambda x: x), + 'post': ')\n', + }, + { + 'regex': r'ERR_.*', + 'pre': 'const (\n', + 'line_format': '\t\t{0} Error = {1}\n', + 'fn': (lambda x: x), + 'post': ')\n', + }, + ] + }, 'python': { 'header': "# For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [%s_const.py]\n", 'footer': "", - 'line_format': 'KS_%s = %s\n', 'out_file': './python/keystone/%s_const.py', # prefixes for constant filenames of all archs - case sensitive 'arm.h': 'arm', @@ -28,11 +168,17 @@ template = { 'keystone.h': 'keystone', 'comment_open': '#', 'comment_close': '', + 'rules': [ + { + 'regex': r'.*', + 'line_format': 'KS_{0} = {1}\n', + 'fn': (lambda x: x), + }, + ] }, 'nodejs': { 'header': "// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [%s_const.js]\n", 'footer': "", - 'line_format': 'module.exports.%s = %s\n', 'out_file': './nodejs/consts/%s.js', # prefixes for constant filenames of all archs - case sensitive 'arm.h': 'arm', @@ -46,11 +192,17 @@ template = { 'keystone.h': 'keystone', 'comment_open': '//', 'comment_close': '', + 'rules': [ + { + 'regex': r'.*', + 'line_format': 'module.exports.{0} = {1}\n', + 'fn': (lambda x: x), + }, + ] }, 'ruby': { 'header': "# For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [%s_const.rb]\n\nmodule Keystone\n", 'footer': "end", - 'line_format': '\tKS_%s = %s\n', 'out_file': './ruby/keystone_gem/lib/keystone/%s_const.rb', # prefixes for constant filenames of all archs - case sensitive 'arm.h': 'arm', @@ -64,7 +216,14 @@ template = { 'keystone.h': 'keystone', 'comment_open': '#', 'comment_close': '', - }, + 'rules': [ + { + 'regex': r'.*', + 'line_format': '\tKS_{0} = {1}\n', + 'fn': (lambda x: x), + }, + ] + }, } # markup for comments to be added to autogen files @@ -72,15 +231,18 @@ MARKUP = '//>' def gen(lang): global include, INCL_DIR + + consts = {} + templ = template[lang] for target in include: prefix = templ[target] - outfile = open(templ['out_file'] %(prefix), 'wb') # open as binary prevents windows newlines - outfile.write((templ['header'] % (prefix)).encode("utf-8")) if target == 'keystone.h': - prefix = '' + prefix = 'keystone' lines = open(os.path.join(INCL_DIR, target)).readlines() + consts[prefix] = [] + previous = {} count = 0 for line in lines: @@ -141,14 +303,45 @@ def gen(lang): rhs = ks_err_val[rhs] lhs_strip = re.sub(r'^KS_', '', lhs) + consts[prefix].append((lhs_strip, rhs)) + count = int(rhs) + 1 - if (count == 1): - outfile.write(("\n").encode("utf-8")) - outfile.write((templ['line_format'] % (lhs_strip, rhs)).encode("utf-8")) previous[lhs] = str(rhs) - outfile.write((templ['footer']).encode("utf-8")) + + rules = templ['rules'] + + for prefix in consts.keys(): + outfile = open(templ['out_file'] % prefix, 'wb') # open as binary prevents windows newlines + outfile.write (templ['header'] % prefix) + + for rule in rules: + regex = rule['regex'] + + consts2 = [] + for const in consts.get(prefix): + if not (re.match(regex, const[0])): + continue + + consts2.append(const) + + if len(consts2) == 0: + continue + + if rule.get('pre'): + outfile.write (rule.get('pre')) + + for const in consts2: + lhs_strip = const[0] + rhs = const[1] + outfile.write(rule['line_format'].format(rule['fn'](lhs_strip), rhs, lhs_strip).encode("utf-8")) + + if rule.get('post'): + outfile.write (rule.get('post')) + outfile.write ('\n') + + outfile.write (templ['footer']) outfile.close() def main(): diff --git a/bindings/go/Makefile b/bindings/go/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..7dc07269ae15badb30498c88173f0aed4caef495 --- /dev/null +++ b/bindings/go/Makefile @@ -0,0 +1,20 @@ +# Python binding for Keystone engine. Nguyen Anh Quynh <aquynh@gmail.com> + +ifndef BUILDDIR +OBJDIR = ./build +else +OBJDIR = $(abspath $(BUILDDIR))/obj/bindings/go +endif + +.PHONY: gen_const install clean + +gen_const: + cd .. && python const_generator.py go + +install: + cd keystone && go build + +clean: + +check: + cd keystone && go test diff --git a/bindings/go/README.md b/bindings/go/README.md new file mode 100644 index 0000000000000000000000000000000000000000..ca289f858d2d1ba0e70d71f1f1f968f5c03b6fce --- /dev/null +++ b/bindings/go/README.md @@ -0,0 +1,45 @@ +# keystone +Go bindings for the [keystone](http://www.keystone-engine.org/) engine. + +```go +package main + +import ( + "fmt" + "os" + + keystone "github.com/keystone-engine/beta/bindings/go/keystone" +) + +func main() { + assembly := os.Args[1] + + if ks, err := keystone.New(keystone.ArchitectureX86, keystone.Mode32); err != nil { + panic(err) + } else { + defer ks.Close() + + if err := ks.Option(keystone.OptionSyntax, keystone.OptionSyntaxIntel); err != nil { + panic(fmt.Errorf("Could not set syntax option to intel")) + } + + if insn, _, ok := ks.Assemble(assembly, 0); !ok { + panic(fmt.Errorf("Could not assemble instruction")) + } else { + fmt.Printf("%s: [%x]", assembly, insn) + } + } +} +``` + +## Installation + +## Notes + +## Contributing + +Contributors: +- Remco Verhoef (@remco_verhoef) + +Special thanks to: +- Sébastien Duquette (@ekse) for his [unicorn-rs](https://github.com/ekse/unicorn-rs) binding diff --git a/bindings/go/keystone/arm64_const.go b/bindings/go/keystone/arm64_const.go new file mode 100644 index 0000000000000000000000000000000000000000..004af4da2ae6a2dff125f3aceac07bb403d0b709 --- /dev/null +++ b/bindings/go/keystone/arm64_const.go @@ -0,0 +1,9 @@ +package keystone +// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [arm64_const.go] + +const ( + ERR_ASM_ARM64_INVALIDOPERAND Error = 512 + ERR_ASM_ARM64_MISSINGFEATURE Error = 513 + ERR_ASM_ARM64_MNEMONICFAIL Error = 514 +) + diff --git a/bindings/go/keystone/arm_const.go b/bindings/go/keystone/arm_const.go new file mode 100644 index 0000000000000000000000000000000000000000..90d3efe0388fe0a7f9021829140316030d0e938e --- /dev/null +++ b/bindings/go/keystone/arm_const.go @@ -0,0 +1,9 @@ +package keystone +// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [arm_const.go] + +const ( + ERR_ASM_ARM_INVALIDOPERAND Error = 512 + ERR_ASM_ARM_MISSINGFEATURE Error = 513 + ERR_ASM_ARM_MNEMONICFAIL Error = 514 +) + diff --git a/bindings/go/keystone/hexagon_const.go b/bindings/go/keystone/hexagon_const.go new file mode 100644 index 0000000000000000000000000000000000000000..a0fc92d6f3e5f8d4e0b4b9e235bf24a9ba9e3014 --- /dev/null +++ b/bindings/go/keystone/hexagon_const.go @@ -0,0 +1,9 @@ +package keystone +// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [hexagon_const.go] + +const ( + ERR_ASM_HEXAGON_INVALIDOPERAND Error = 512 + ERR_ASM_HEXAGON_MISSINGFEATURE Error = 513 + ERR_ASM_HEXAGON_MNEMONICFAIL Error = 514 +) + diff --git a/bindings/go/keystone/keystone-binding.c b/bindings/go/keystone/keystone-binding.c new file mode 100644 index 0000000000000000000000000000000000000000..8498366bbe3dc35e09d23c51f908ca1dd4e16a0d --- /dev/null +++ b/bindings/go/keystone/keystone-binding.c @@ -0,0 +1,5 @@ +// Copyright © 2015, Keylocker. All rights reserved. + +// +build darwin,linux,cgo + +#include <keystone.h> diff --git a/bindings/go/keystone/keystone-binding.go b/bindings/go/keystone/keystone-binding.go new file mode 100644 index 0000000000000000000000000000000000000000..a6b20ab2991073a3dd8349688d05775091c34e66 --- /dev/null +++ b/bindings/go/keystone/keystone-binding.go @@ -0,0 +1,65 @@ +/* Keystone Assembler Engine (www.keystone-engine.org) */ +/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2016 */ +/* Golang bindings by Remco Verhoef <remco@dutchcoders.io>, 2016 */ + +// +build darwin,linux,cgo +package keystone + +// #cgo linux LDFLAGS:-lkeystone +// #include "keystone-binding.h" +// #include "keystone.h" +import "C" +import "unsafe" + +func ks_version() (uint, uint) { + major := C.uint(0) + minor := C.uint(0) + C.ks_version(&major, &minor) + return uint(major), uint(minor) +} + +func ks_arch_supported(a Architecture) bool { + return bool(C.ks_arch_supported((C.ks_arch)(a))) +} + +func ks_open(a Architecture, m Mode, engine **C.ks_engine) error { + if err := C.ks_open((C.ks_arch)(a), (C.int)(m), (**C.ks_engine)(unsafe.Pointer(engine))); err != 0 { + return Error(err) + } + + return nil +} + +func ks_option(engine *C.ks_engine, type_ OptionType, value OptionValue) error { + if err := C.ks_option(engine, C.ks_opt_type(type_), C.size_t(value)); err != 0 { + return Error(err) + } + return nil +} + +func ks_errno(engine *C.ks_engine) error { + if err := C.ks_errno(engine); err != 0 { + return Error(err) + } + return nil +} + +func ks_asm(engine *C.ks_engine, str string, address uint64, encoding *[]byte, stat_count *uint64) bool { + cstr := C.CString(str) + defer C.free(unsafe.Pointer(cstr)) + + var p_insn unsafe.Pointer + defer C.free(unsafe.Pointer(p_insn)) + + l_insn := C.size_t(0) + err := C.ks_asm(engine, cstr, C.uint64_t(address), (**C.uchar)(unsafe.Pointer(&p_insn)), &l_insn, (*C.size_t)(stat_count)) + *encoding = C.GoBytes(p_insn, C.int(l_insn)) + return err == 0 +} + +func ks_close(engine *C.ks_engine) error { + if err := C.ks_close(engine); err != 0 { + return Error(err) + } + return nil +} diff --git a/bindings/go/keystone/keystone-binding.h b/bindings/go/keystone/keystone-binding.h new file mode 100644 index 0000000000000000000000000000000000000000..3bbc3f1797fded539a7ad08ea541678af3e1fcba --- /dev/null +++ b/bindings/go/keystone/keystone-binding.h @@ -0,0 +1,7 @@ +/* Keystone Assembler Engine (www.keystone-engine.org) */ +/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2016 */ +/* Golang bindings by Remco Verhoef <remco@dutchcoders.io>, 2016 */ + +// #include <keystone.h> + + diff --git a/bindings/go/keystone/keystone.go b/bindings/go/keystone/keystone.go new file mode 100644 index 0000000000000000000000000000000000000000..18305c5e5d38cc8ac8ae219fd93769f0e4262812 --- /dev/null +++ b/bindings/go/keystone/keystone.go @@ -0,0 +1,67 @@ +/* Keystone Assembler Engine (www.keystone-engine.org) */ +/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2016 */ +/* Golang bindings by Remco Verhoef <remco@dutchcoders.io>, 2016 */ + +package keystone + +// #include "keystone.h" +import "C" + +type Architecture uint + +type Mode uint + +type OptionType uint + +type OptionValue uint + +type Error uint32 + +func (e Error) Error() string { + s := C.ks_strerror((C.ks_err)(e)) + return C.GoString(s) +} + +func (a Architecture) Supported() bool { + return ks_arch_supported(a) +} + +func Version() (uint, uint) { + return ks_version() +} + +type Keystone struct { + engine *C.ks_engine +} + +func New(a Architecture, m Mode) (*Keystone, error) { + ks := &Keystone{} + if err := ks_open(a, m, &ks.engine); err != nil { + return nil, err + } else { + return ks, nil + } +} + +func (ks *Keystone) LastError() error { + return ks_errno(ks.engine) +} + +func (ks *Keystone) Option(type_ OptionType, value OptionValue) error { + if err := ks_option(ks.engine, type_, value); err != nil { + return err + } + + return nil +} + +func (ks *Keystone) Assemble(str string, address uint64) ([]byte, uint64, bool) { + encoding := []byte{} + stat_count := uint64(0) + ok := ks_asm(ks.engine, str, address, &encoding, &stat_count) + return encoding, stat_count, ok +} + +func (ks *Keystone) Close() error { + return ks_close(ks.engine) +} diff --git a/bindings/go/keystone/keystone_const.go b/bindings/go/keystone/keystone_const.go new file mode 100644 index 0000000000000000000000000000000000000000..5a3f530ab2b3b639a44f45040ad27b22b95ba3db --- /dev/null +++ b/bindings/go/keystone/keystone_const.go @@ -0,0 +1,101 @@ +package keystone +// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [keystone_const.go] + +const ( + API_MAJOR = 1 + API_MINOR = 0 +) + +const ( + MODE_LITTLE_ENDIAN Mode = 0 + MODE_BIG_ENDIAN Mode = 1073741824 + MODE_ARM Mode = 1 + MODE_THUMB Mode = 16 + MODE_V8 Mode = 64 + MODE_MICRO Mode = 16 + MODE_MIPS3 Mode = 32 + MODE_MIPS32R6 Mode = 64 + MODE_MIPS32 Mode = 4 + MODE_MIPS64 Mode = 8 + MODE_16 Mode = 2 + MODE_32 Mode = 4 + MODE_64 Mode = 8 + MODE_PPC32 Mode = 4 + MODE_PPC64 Mode = 8 + MODE_QPX Mode = 16 + MODE_SPARC32 Mode = 4 + MODE_SPARC64 Mode = 8 + MODE_V9 Mode = 16 +) + +const ( + ARCH_ARM Architecture = 1 + ARCH_ARM64 Architecture = 2 + ARCH_MIPS Architecture = 3 + ARCH_X86 Architecture = 4 + ARCH_PPC Architecture = 5 + ARCH_SPARC Architecture = 6 + ARCH_SYSTEMZ Architecture = 7 + ARCH_HEXAGON Architecture = 8 + ARCH_MAX Architecture = 9 +) + +const ( + OPT_SYNTAX OptionType = 1 +) + +const ( + OPT_SYNTAX_INTEL OptionValue = 1 + OPT_SYNTAX_ATT OptionValue = 2 + OPT_SYNTAX_NASM OptionValue = 4 + OPT_SYNTAX_MASM OptionValue = 8 + OPT_SYNTAX_GAS OptionValue = 16 +) + +const ( + ERR_ASM Error = 128 + ERR_ASM_ARCH Error = 512 + ERR_OK Error = 0 + ERR_NOMEM Error = 1 + ERR_ARCH Error = 2 + ERR_HANDLE Error = 3 + ERR_MODE Error = 4 + ERR_VERSION Error = 5 + ERR_OPT_INVALID Error = 6 + ERR_ASM_EXPR_TOKEN Error = 128 + ERR_ASM_DIRECTIVE_VALUE_RANGE Error = 129 + ERR_ASM_DIRECTIVE_ID Error = 130 + ERR_ASM_DIRECTIVE_TOKEN Error = 131 + ERR_ASM_DIRECTIVE_STR Error = 132 + ERR_ASM_DIRECTIVE_COMMA Error = 133 + ERR_ASM_DIRECTIVE_RELOC_NAME Error = 134 + ERR_ASM_DIRECTIVE_RELOC_TOKEN Error = 135 + ERR_ASM_DIRECTIVE_FPOINT Error = 136 + ERR_ASM_DIRECTIVE_UNKNOWN Error = 137 + ERR_ASM_VARIANT_INVALID Error = 138 + ERR_ASM_DIRECTIVE_EQU Error = 139 + ERR_ASM_EXPR_BRACKET Error = 140 + ERR_ASM_SYMBOL_MODIFIER Error = 141 + ERR_ASM_SYMBOL_REDEFINED Error = 142 + ERR_ASM_SYMBOL_MISSING Error = 143 + ERR_ASM_RPAREN Error = 144 + ERR_ASM_STAT_TOKEN Error = 145 + ERR_ASM_UNSUPPORTED Error = 146 + ERR_ASM_MACRO_TOKEN Error = 147 + ERR_ASM_MACRO_PAREN Error = 148 + ERR_ASM_MACRO_EQU Error = 149 + ERR_ASM_MACRO_ARGS Error = 150 + ERR_ASM_MACRO_LEVELS_EXCEED Error = 151 + ERR_ASM_MACRO_STR Error = 152 + ERR_ASM_ESC_BACKSLASH Error = 153 + ERR_ASM_ESC_OCTAL Error = 154 + ERR_ASM_ESC_SEQUENCE Error = 155 + ERR_ASM_ESC_STR Error = 156 + ERR_ASM_TOKEN_INVALID Error = 157 + ERR_ASM_INSN_UNSUPPORTED Error = 158 + ERR_ASM_FIXUP_INVALID Error = 159 + ERR_ASM_INVALIDOPERAND Error = 512 + ERR_ASM_MISSINGFEATURE Error = 513 + ERR_ASM_MNEMONICFAIL Error = 514 +) + diff --git a/bindings/go/keystone/keystone_test.go b/bindings/go/keystone/keystone_test.go new file mode 100644 index 0000000000000000000000000000000000000000..0f18997a1d38c120f4263930b34725610dad2d29 --- /dev/null +++ b/bindings/go/keystone/keystone_test.go @@ -0,0 +1,66 @@ +/* Keystone Assembler Engine (www.keystone-engine.org) */ +/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2016 */ +/* Golang bindings by Remco Verhoef <remco@dutchcoders.io>, 2016 */ + +package keystone + +import ( + "fmt" + "reflect" + "testing" +) + +func TestVersion(t *testing.T) { + major, minor := Version() + if major == API_MAJOR && minor == API_MINOR { + } else { + t.Error(fmt.Errorf("Unexpected version: got %d.%d expected %d.%d", major, minor, 1, 0)) + } +} + +func TestArchitectureSupported(t *testing.T) { + if !ARCH_ARM.Supported() { + t.Error(fmt.Errorf("ARM not supported")) + } +} + +type Test struct { + Architecture Architecture + Mode Mode + Address uint64 + Assembly string + ExpectedResult []byte +} + +type Syntax struct { + Syntax OptionValue + Tests []Test +} + +var tests = []Syntax{ + Syntax{ + OPT_SYNTAX_INTEL, []Test{ + Test{ARCH_X86, MODE_32 | MODE_LITTLE_ENDIAN, 0, "mov ah, al", []byte{0x88, 0xc4}}, + }, + }, +} + +func TestRun(t *testing.T) { + for _, st := range tests { + for _, tr := range st.Tests { + if ks, err := New(tr.Architecture, tr.Mode); err != nil { + t.Error(err) + } else { + defer ks.Close() + + if err := ks.Option(OPT_SYNTAX, st.Syntax); err != nil { + t.Error(fmt.Errorf("Could not set syntax option to intel")) + } else if insn, _, ok := ks.Assemble(tr.Assembly, tr.Address); !ok { + t.Error(fmt.Errorf("Could not assemble instruction")) + } else if !reflect.DeepEqual(insn, tr.ExpectedResult) { + t.Error(fmt.Errorf("Not expected result: expected %#v got %#v", tr.ExpectedResult, insn)) + } + } + } + } +} diff --git a/bindings/go/keystone/mips_const.go b/bindings/go/keystone/mips_const.go new file mode 100644 index 0000000000000000000000000000000000000000..ffa9bcedacb3b14fdd906c85f380d3645bb257c7 --- /dev/null +++ b/bindings/go/keystone/mips_const.go @@ -0,0 +1,9 @@ +package keystone +// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [mips_const.go] + +const ( + ERR_ASM_MIPS_INVALIDOPERAND Error = 512 + ERR_ASM_MIPS_MISSINGFEATURE Error = 513 + ERR_ASM_MIPS_MNEMONICFAIL Error = 514 +) + diff --git a/bindings/go/keystone/ppc_const.go b/bindings/go/keystone/ppc_const.go new file mode 100644 index 0000000000000000000000000000000000000000..995c9f059cf55823d1ebdcfb50e4ae4c170766bb --- /dev/null +++ b/bindings/go/keystone/ppc_const.go @@ -0,0 +1,9 @@ +package keystone +// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [ppc_const.go] + +const ( + ERR_ASM_PPC_INVALIDOPERAND Error = 512 + ERR_ASM_PPC_MISSINGFEATURE Error = 513 + ERR_ASM_PPC_MNEMONICFAIL Error = 514 +) + diff --git a/bindings/go/keystone/samples/main.go b/bindings/go/keystone/samples/main.go new file mode 100644 index 0000000000000000000000000000000000000000..567fd14670dea22dd4a555af70386d69e3fcc319 --- /dev/null +++ b/bindings/go/keystone/samples/main.go @@ -0,0 +1,32 @@ +/* Keystone Assembler Engine (www.keystone-engine.org) */ +/* By Nguyen Anh Quynh <aquynh@gmail.com>, 2016 */ +/* Golang bindings by Remco Verhoef <remco@dutchcoders.io>, 2016 */ + +package main + +import ( + "fmt" + "os" + + keystone "github.com/keystone-engine/beta/bindings/go/keystone" +) + +func main() { + assembly := os.Args[1] + + if ks, err := keystone.New(keystone.ArchitectureX86, keystone.Mode32); err != nil { + panic(err) + } else { + defer ks.Close() + + if err := ks.Option(keystone.OptionSyntax, keystone.OptionSyntaxIntel); err != nil { + panic(fmt.Errorf("Could not set syntax option to intel")) + } + + if insn, _, ok := ks.Assemble(assembly, 0); !ok { + panic(fmt.Errorf("Could not assemble instruction")) + } else { + fmt.Printf("%s: [%x]", assembly, insn) + } + } +} diff --git a/bindings/go/keystone/sparc_const.go b/bindings/go/keystone/sparc_const.go new file mode 100644 index 0000000000000000000000000000000000000000..0852618c1d0f3bc77214fdef01bb091f7682ca28 --- /dev/null +++ b/bindings/go/keystone/sparc_const.go @@ -0,0 +1,9 @@ +package keystone +// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [sparc_const.go] + +const ( + ERR_ASM_SPARC_INVALIDOPERAND Error = 512 + ERR_ASM_SPARC_MISSINGFEATURE Error = 513 + ERR_ASM_SPARC_MNEMONICFAIL Error = 514 +) + diff --git a/bindings/go/keystone/systemz_const.go b/bindings/go/keystone/systemz_const.go new file mode 100644 index 0000000000000000000000000000000000000000..f33c772efc1a5c169dc408fcca6d53da9ebf55d5 --- /dev/null +++ b/bindings/go/keystone/systemz_const.go @@ -0,0 +1,9 @@ +package keystone +// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [systemz_const.go] + +const ( + ERR_ASM_SYSTEMZ_INVALIDOPERAND Error = 512 + ERR_ASM_SYSTEMZ_MISSINGFEATURE Error = 513 + ERR_ASM_SYSTEMZ_MNEMONICFAIL Error = 514 +) + diff --git a/bindings/go/keystone/x86_const.go b/bindings/go/keystone/x86_const.go new file mode 100644 index 0000000000000000000000000000000000000000..5e5ccef6eb002f80098b785def2676c574eedc07 --- /dev/null +++ b/bindings/go/keystone/x86_const.go @@ -0,0 +1,9 @@ +package keystone +// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [x86_const.go] + +const ( + ERR_ASM_X86_INVALIDOPERAND Error = 512 + ERR_ASM_X86_MISSINGFEATURE Error = 513 + ERR_ASM_X86_MNEMONICFAIL Error = 514 +) + diff --git a/bindings/nodejs/Makefile b/bindings/nodejs/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..188296ca4087d5e2286975e5b6723788f0b30452 --- /dev/null +++ b/bindings/nodejs/Makefile @@ -0,0 +1,21 @@ +# Python binding for Keystone engine. Nguyen Anh Quynh <aquynh@gmail.com> + +ifndef BUILDDIR +OBJDIR = ./build +else +OBJDIR = $(abspath $(BUILDDIR))/obj/bindings/nodejs +endif + +.PHONY: gen_const install clean + +gen_const: + cd .. && python const_generator.py nodejs + +install: + npm install ./ + +clean: + +check: + node sample.js + diff --git a/bindings/nodejs/consts/keystone.js b/bindings/nodejs/consts/keystone.js index 6b5967b508a7c392bb1346d11fa3d4605c00d6bd..ee0b5308f73c3d8dea4ce80ed34d4d06cddf1220 100644 --- a/bindings/nodejs/consts/keystone.js +++ b/bindings/nodejs/consts/keystone.js @@ -1,6 +1,5 @@ // For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [keystone_const.js] module.exports.API_MAJOR = 1 - module.exports.API_MINOR = 0 module.exports.ARCH_ARM = 1 module.exports.ARCH_ARM64 = 2 @@ -11,7 +10,6 @@ module.exports.ARCH_SPARC = 6 module.exports.ARCH_SYSTEMZ = 7 module.exports.ARCH_HEXAGON = 8 module.exports.ARCH_MAX = 9 - module.exports.MODE_LITTLE_ENDIAN = 0 module.exports.MODE_BIG_ENDIAN = 1073741824 module.exports.MODE_ARM = 1 @@ -33,7 +31,6 @@ module.exports.MODE_SPARC64 = 8 module.exports.MODE_V9 = 16 module.exports.ERR_ASM = 128 module.exports.ERR_ASM_ARCH = 512 - module.exports.ERR_OK = 0 module.exports.ERR_NOMEM = 1 module.exports.ERR_ARCH = 2 diff --git a/bindings/python/keystone/keystone_const.py b/bindings/python/keystone/keystone_const.py index 8836f8f9b00bd52cf626f398830f54657549c065..17c70680988b7400ae43747623301b21feabda44 100644 --- a/bindings/python/keystone/keystone_const.py +++ b/bindings/python/keystone/keystone_const.py @@ -1,6 +1,5 @@ # For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [keystone_const.py] KS_API_MAJOR = 1 - KS_API_MINOR = 0 KS_ARCH_ARM = 1 KS_ARCH_ARM64 = 2 @@ -11,7 +10,6 @@ KS_ARCH_SPARC = 6 KS_ARCH_SYSTEMZ = 7 KS_ARCH_HEXAGON = 8 KS_ARCH_MAX = 9 - KS_MODE_LITTLE_ENDIAN = 0 KS_MODE_BIG_ENDIAN = 1073741824 KS_MODE_ARM = 1 @@ -33,7 +31,6 @@ KS_MODE_SPARC64 = 8 KS_MODE_V9 = 16 KS_ERR_ASM = 128 KS_ERR_ASM_ARCH = 512 - KS_ERR_OK = 0 KS_ERR_NOMEM = 1 KS_ERR_ARCH = 2 diff --git a/bindings/ruby/keystone_gem/lib/keystone/keystone_const.rb b/bindings/ruby/keystone_gem/lib/keystone/keystone_const.rb index 3c8c23c4554a9c7a81d40a2e474e6a621aaa5cd2..470664544cfde4110c36fb45ca82875d2628fd37 100644 --- a/bindings/ruby/keystone_gem/lib/keystone/keystone_const.rb +++ b/bindings/ruby/keystone_gem/lib/keystone/keystone_const.rb @@ -2,7 +2,6 @@ module Keystone KS_API_MAJOR = 1 - KS_API_MINOR = 0 KS_ARCH_ARM = 1 KS_ARCH_ARM64 = 2 @@ -13,7 +12,6 @@ module Keystone KS_ARCH_SYSTEMZ = 7 KS_ARCH_HEXAGON = 8 KS_ARCH_MAX = 9 - KS_MODE_LITTLE_ENDIAN = 0 KS_MODE_BIG_ENDIAN = 1073741824 KS_MODE_ARM = 1 @@ -35,7 +33,6 @@ module Keystone KS_MODE_V9 = 16 KS_ERR_ASM = 128 KS_ERR_ASM_ARCH = 512 - KS_ERR_OK = 0 KS_ERR_NOMEM = 1 KS_ERR_ARCH = 2 diff --git a/bindings/rust/.gitignore b/bindings/rust/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..ce906b8f897bcd1482cbfbd2b1205dfb4678ab8c --- /dev/null +++ b/bindings/rust/.gitignore @@ -0,0 +1,3 @@ +Cargo.lock +target/ +*.bk diff --git a/bindings/rust/COPYING b/bindings/rust/COPYING new file mode 100644 index 0000000000000000000000000000000000000000..00ccfbb62888382f5a4ce201a9d5e4bf85bbc373 --- /dev/null +++ b/bindings/rust/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along + with this program; if not, write to the Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. diff --git a/bindings/rust/Cargo.toml b/bindings/rust/Cargo.toml new file mode 100644 index 0000000000000000000000000000000000000000..93c9f384b36c7be0f1e58ebab3846dbad2da4d52 --- /dev/null +++ b/bindings/rust/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "keystone" +version = "0.1.0" +authors = ["Remco Verhoef <remco.verhoef@dutchcoders.io>"] +description = "Rust bindings for the keystone-engine" +license = "GPL-2.0" +readme = "README.md" +include = ["src/*", "tests/*", "Cargo.toml", "COPYING", "README.md"] + +[dependencies] +libc = "0.2.*" +bitflags = "0.7.0" diff --git a/bindings/rust/Makefile b/bindings/rust/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..b360c22a5628b694c3fd75dd2af675024dda8c8c --- /dev/null +++ b/bindings/rust/Makefile @@ -0,0 +1,22 @@ +# Python binding for Keystone engine. Nguyen Anh Quynh <aquynh@gmail.com> + +ifndef BUILDDIR +OBJDIR = ./build +else +OBJDIR = $(abspath $(BUILDDIR))/obj/bindings/rust +endif + +.PHONY: gen_const install install3 clean + +gen_const: + cd .. && python const_generator.py rust + +install: + cargo build + +clean: + rm -rf target/ + +check: + cargo test + diff --git a/bindings/rust/README.md b/bindings/rust/README.md new file mode 100644 index 0000000000000000000000000000000000000000..a8ef3eba1fb8edaba4c1c0e085362dc7c089f18d --- /dev/null +++ b/bindings/rust/README.md @@ -0,0 +1,32 @@ +# keystone-rs +Rust bindings for the [keystone](http://www.keystone-engine.org/) engine. + +```rust +extern crate keystone; +use keystone::{Keystone, Arch, Mode, OptionType, OptionValue}; + +fn main() { + let engine = Keystone::new(Arch::X86, Mode::Mode32) + .expect("Could not initialize Keystone engine"); + + engine.option(OptionType::Syntax, OptionValue::SyntaxNASM) + .expect("Could not set option to nasm syntax"); + + let result = engine.asm("mov ah, 0x80".to_string(), 0) + .expect("Could not assemble"); + + let _ = result; +} +``` + +## Installation + +## Notes + +## Contributing + +Contributors: +- Remco Verhoef (@remco_verhoef) + +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 new file mode 100644 index 0000000000000000000000000000000000000000..4676a645f9ae5e9c286c73f39ca750f4fa48ad03 --- /dev/null +++ b/bindings/rust/examples/asm.rs @@ -0,0 +1,20 @@ +extern crate keystone; +use keystone::*; + +fn main() { + let engine = Keystone::new(Arch::X86, MODE_32) + .expect("Could not initialize Keystone engine"); + + engine.option(OptionType::SYNTAX, OPT_SYNTAX_NASM) + .expect("Could not set option to nasm syntax"); + + let result = engine.asm("mov ah, 0x80".to_string(), 0) + .expect("Could not assemble"); + + println!("ASM result: {}", result); + + if let Err(err) = engine.asm("INVALID".to_string(), 0) { + println!("Error: {}", err); + } +} + diff --git a/bindings/rust/src/enums.rs b/bindings/rust/src/enums.rs new file mode 100644 index 0000000000000000000000000000000000000000..45516be71266e26d601742ec671ee6d93763b056 --- /dev/null +++ b/bindings/rust/src/enums.rs @@ -0,0 +1,101 @@ +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 new file mode 100644 index 0000000000000000000000000000000000000000..1fd2389557fe0d725e551ff857dbbe08a382c39d --- /dev/null +++ b/bindings/rust/src/ffi.rs @@ -0,0 +1,28 @@ +//! 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 new file mode 100644 index 0000000000000000000000000000000000000000..ed0b0e8496f42258c57bba20ec4f5adf98642c63 --- /dev/null +++ b/bindings/rust/src/keystone_const.rs @@ -0,0 +1,146 @@ +// For Keystone Engine. AUTO-GENERATED FILE, DO NOT EDIT [keystone_const.rs] +extern crate libc; + + +pub const KS_API_MAJOR: u32 = 1; +pub const KS_API_MINOR: u32 = 0; +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_MAX: u32 = 9; + + +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, + 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::MAX => 9, + } + } +} + +#[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_SYNTAX_INTEL = 1, + const OPT_SYNTAX_ATT = 2, + const OPT_SYNTAX_NASM = 4, + const OPT_SYNTAX_MASM = 8, + const OPT_SYNTAX_GAS = 16, + } +} + +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_VARIANT_INVALID = 138, + const ERR_ASM_DIRECTIVE_EQU = 139, + const ERR_ASM_EXPR_BRACKET = 140, + const ERR_ASM_SYMBOL_MODIFIER = 141, + const ERR_ASM_SYMBOL_REDEFINED = 142, + const ERR_ASM_SYMBOL_MISSING = 143, + const ERR_ASM_RPAREN = 144, + const ERR_ASM_STAT_TOKEN = 145, + const ERR_ASM_UNSUPPORTED = 146, + const ERR_ASM_MACRO_TOKEN = 147, + const ERR_ASM_MACRO_PAREN = 148, + const ERR_ASM_MACRO_EQU = 149, + const ERR_ASM_MACRO_ARGS = 150, + const ERR_ASM_MACRO_LEVELS_EXCEED = 151, + const ERR_ASM_MACRO_STR = 152, + const ERR_ASM_ESC_BACKSLASH = 153, + const ERR_ASM_ESC_OCTAL = 154, + const ERR_ASM_ESC_SEQUENCE = 155, + const ERR_ASM_ESC_STR = 156, + const ERR_ASM_TOKEN_INVALID = 157, + const ERR_ASM_INSN_UNSUPPORTED = 158, + const ERR_ASM_FIXUP_INVALID = 159, + 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 new file mode 100644 index 0000000000000000000000000000000000000000..dac0912be91fd0b6360710af4222e70222a93289 --- /dev/null +++ b/bindings/rust/src/lib.rs @@ -0,0 +1,189 @@ +//! Keystone Assembler Engine (www.keystone-engine.org) */ +//! By Nguyen Anh Quynh <aquynh@gmail.com>, 2016 */ +//! Rust bindings by Remco Verhoef <remco@dutchcoders.io>, 2016 */ +//! +//! ```rust +//! extern crate keystone; +//! use keystone::{Keystone, Arch, OptionType}; +//! +//! fn main() { +//! let engine = Keystone::new(Arch::X86, keystone::MODE_32) +//! .expect("Could not initialize Keystone engine"); +//! engine.option(OptionType::SYNTAX, keystone::OPT_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")] + +#[macro_use] +extern crate bitflags; +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()) + } +} + +#[derive(Debug, PartialEq)] +pub struct AsmResult { + pub size: u32, + pub stat_count: u32, + pub bytes: Vec<u8>, +} + +impl fmt::Display for AsmResult { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for byte in &self.bytes { + try!(f.write_fmt(format_args!("{:02x}", byte))); + } + + Ok(()) + } +} + +pub fn bindings_version() -> (u32, u32) { + (KS_API_MAJOR, KS_API_MINOR) +} + +/// Return tuple `(major, minor)` API version numbers. +pub fn version() -> (u32, u32) { + let mut major: u32 = 0; + let mut minor: u32 = 0; + + unsafe { + ffi::ks_version(&mut major, &mut minor); + } + (major, minor) +} + +/// Return tuple `(major, minor)` API version numbers. +pub fn arch_supported(arch: Arch) -> bool { + unsafe { ffi::ks_arch_supported(arch.val()) } +} + +/// 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() } +} + +pub struct Keystone { + handle: ks_handle, +} + +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); + } + + 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 { + Ok(Keystone { handle: handle }) + } else { + Err(err) + } + } + + /// 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 { + Ok(()) + } else { + Err(err) + } + } + + /// 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 { + Ok(()) + } else { + Err(err) + } + } + + /// Assemble a string given its the buffer, size, start address and number + /// of instructions to be decoded. + /// + /// This API dynamically allocate memory to contain assembled instruction. + /// Resulted array of bytes containing the machine code is put into @*encoding + pub fn asm(&self, str: String, address: u64) -> Result<AsmResult, Error> { + let mut size: libc::size_t = 0; + let mut stat_count: libc::size_t = 0; + + let s = CString::new(str).unwrap(); + 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) + }); + + if err == ERR_OK { + let bytes = unsafe { std::slice::from_raw_parts(ptr, size) }; + + unsafe { + ffi::ks_free(ptr); + }; + + Ok(AsmResult { + size: size as u32, + stat_count: stat_count as u32, + bytes: From::from(&bytes[..]), + }) + } else { + let err = Error::from_bits_truncate(unsafe { ffi::ks_errno(self.handle) }); + Err(err) + } + } +} + +impl Drop for Keystone { + fn drop(&mut self) { + unsafe { ffi::ks_close(self.handle) }; + } +} diff --git a/bindings/rust/tests/keystone.rs b/bindings/rust/tests/keystone.rs new file mode 100644 index 0000000000000000000000000000000000000000..ed96ecec2f6f06187a08a5b6bba4a366284be9b1 --- /dev/null +++ b/bindings/rust/tests/keystone.rs @@ -0,0 +1,48 @@ +extern crate keystone; + +use keystone::{Keystone, Arch, OptionType}; + +#[test] +fn version() { + let (major, minor) = keystone::version(); + assert_eq!((major, minor), keystone::bindings_version()); +} + +#[test] +fn arch_supported() { + assert_eq!(keystone::arch_supported(Arch::ARM), true); + assert_eq!(keystone::arch_supported(Arch::X86), true); +} + +#[test] +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) + .expect("Could not initialize Keystone engine"); + + engine.option(OptionType::SYNTAX, keystone::OPT_SYNTAX_NASM) + .expect("Could not set option to nasm syntax"); + + let result = engine.asm(asm, 0) + .expect("Could not assemble"); + + print!("{0:?}", result.bytes); + 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 result = engine.asm(asm, 0); + let err = result.unwrap_err(); + + assert_eq!(err, keystone::ERR_ASM_MNEMONICFAIL); + assert_eq!(err.msg(), "Invalid mnemonic (KS_ERR_ASM_MNEMONICFAIL)"); + assert_eq!(format!("{}", err), "Invalid mnemonic (KS_ERR_ASM_MNEMONICFAIL)"); +} +