keystone_engine/ffi/
mod.rs

1//! Unsafe Rust bindings for the Keystone Engine assembler library.
2#![allow(non_camel_case_types)]
3
4use bitflags::bitflags;
5use libc::*;
6
7use core::marker::{PhantomData, PhantomPinned};
8
9// -----------------------------------------------------------------------------------------------
10// Types
11// -----------------------------------------------------------------------------------------------
12
13/// Opaque type for the keystone engine.
14#[repr(C)]
15pub struct KsEngine {
16    _data: [u8; 0],
17    _marker: PhantomData<(*mut u8, PhantomPinned)>,
18}
19
20/// Pointer to a [`KsEngine`] object.
21pub type KsHandle = std::ptr::NonNull<KsEngine>;
22
23// -----------------------------------------------------------------------------------------------
24// Constants
25// -----------------------------------------------------------------------------------------------
26
27// These values have been generated using the const_generator.py script of the official
28// keystone repository:
29//     - https://github.com/keystone-engine/keystone/blob/0.9.2/bindings/const_generator.py
30
31/// Keystone major API version.
32pub const API_MAJOR: c_uint = 0;
33/// Keystone minor API version.
34pub const API_MINOR: c_uint = 9;
35
36/// Architecture type.
37#[repr(C)]
38#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
39pub enum Arch {
40    /// ARM architecture (including Thumb, Thumb-2).
41    ARM = 1,
42    /// ARM-64, also called AArch64.
43    ARM64 = 2,
44    /// Mips architecture.
45    MIPS = 3,
46    /// X86 architecture (including x86 & x86-64).
47    X86 = 4,
48    /// PowerPC architecture (currently unsupported).
49    PPC = 5,
50    /// Sparc architecture.
51    SPARC = 6,
52    /// SystemZ architecture (S390X).
53    SYSTEMZ = 7,
54    /// Hexagon architecture.
55    HEXAGON = 8,
56    /// Ethereum Virtual Machine architecture.
57    EVM = 9,
58    /// Maximum value for the architecture enum.
59    MAX = 10,
60}
61
62bitflags! {
63    ///  Mode type.
64    #[repr(C)]
65    pub struct Mode: c_int {
66        /// Little-endian mode (default mode).
67        const LITTLE_ENDIAN = 0;
68        /// Big-endian mode.
69        const BIG_ENDIAN = 1073741824;
70        /// ARM/ARM64 - ARM mode.
71        const ARM = 1;
72        /// ARM/ARM64 - THUMB mode (including Thumb-2).
73        const THUMB = 16;
74        /// ARM/ARM64 - ARMv8 A32 encodings for ARM.
75        const V8 = 64;
76        /// MIPS - MicroMips mode.
77        const MICRO = 16;
78        /// MIPS - Mips III ISA.
79        const MIPS3 = 32;
80        /// MIPS - Mips32r6 ISA.
81        const MIPS32R6 = 64;
82        /// MIPS - Mips32 ISA.
83        const MIPS32 = 4;
84        /// MIPS - Mips64 ISA.
85        const MIPS64 = 8;
86        /// X86/X64 - 16-bit mode.
87        const MODE_16 = 2;
88        /// X86/X64 - 32-bit mode.
89        const MODE_32 = 4;
90        /// X86/X64 - 64-bit mode.
91        const MODE_64 = 8;
92        /// X86/X64 - 32-bit mode.
93        const PPC32 = 4;
94        /// PPC - 64-bit mode.
95        const PPC64 = 8;
96        /// PPC - Quad Processing eXtensions mode.
97        const QPX = 16;
98        /// PPC - 32-bit mode.
99        const SPARC32 = 4;
100        /// SPARC - 64-bit mode.
101        const SPARC64 = 8;
102        /// SPARC - SparcV9 mode.
103        const V9 = 16;
104    }
105}
106
107/// All type of errors encountered by Keystone API.
108#[repr(C)]
109#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
110pub enum Error {
111    /// No error: everything was fine.
112    OK = 0,
113    /// Out-Of-Memory error: ks_open(), ks_emulate().
114    NOMEM = 1,
115    /// Unsupported architecture: ks_open().
116    ARCH = 2,
117    /// Invalid handle.
118    HANDLE = 3,
119    /// Invalid/unsupported mode: ks_open().
120    MODE = 4,
121    /// Unsupported version (bindings).
122    VERSION = 5,
123    /// Unsupported option.
124    OPT_INVALID = 6,
125    /// Unknown token in expression.
126    ASM_EXPR_TOKEN = 128,
127    /// Literal value out of range for directive.
128    ASM_DIRECTIVE_VALUE_RANGE = 129,
129    /// Expected identifier in directive.
130    ASM_DIRECTIVE_ID = 130,
131    /// Unexpected token in directive.
132    ASM_DIRECTIVE_TOKEN = 131,
133    /// Expected string in directive.
134    ASM_DIRECTIVE_STR = 132,
135    /// Expected comma in directive.
136    ASM_DIRECTIVE_COMMA = 133,
137    /// Expected relocation name in directive.
138    ASM_DIRECTIVE_RELOC_NAME = 134,
139    /// Unexpected token in .reloc directive.
140    ASM_DIRECTIVE_RELOC_TOKEN = 135,
141    /// Invalid floating point in directive.
142    ASM_DIRECTIVE_FPOINT = 136,
143    /// Unknown directive.
144    ASM_DIRECTIVE_UNKNOWN = 137,
145    /// Invalid equal directive.
146    ASM_DIRECTIVE_EQU = 138,
147    /// (Generic) invalid directive.
148    ASM_DIRECTIVE_INVALID = 139,
149    /// Invalid variant.
150    ASM_VARIANT_INVALID = 140,
151    /// Brackets expression not supported on this target.
152    ASM_EXPR_BRACKET = 141,
153    /// Unexpected symbol modifier following '@'.
154    ASM_SYMBOL_MODIFIER = 142,
155    /// Invalid symbol redefinition.
156    ASM_SYMBOL_REDEFINED = 143,
157    /// Cannot find a symbol.
158    ASM_SYMBOL_MISSING = 144,
159    /// Expected ')' in parentheses expression.
160    ASM_RPAREN = 145,
161    /// Unexpected token at start of statement.
162    ASM_STAT_TOKEN = 146,
163    /// Unsupported token yet.
164    ASM_UNSUPPORTED = 147,
165    /// Unexpected token in macro instantiation.
166    ASM_MACRO_TOKEN = 148,
167    /// Unbalanced parentheses in macro argument.
168    ASM_MACRO_PAREN = 149,
169    /// Expected '=' after formal parameter identifier.
170    ASM_MACRO_EQU = 150,
171    /// Too many positional arguments.
172    ASM_MACRO_ARGS = 151,
173    /// Macros cannot be nested more than 20 levels deep.
174    ASM_MACRO_LEVELS_EXCEED = 152,
175    /// Invalid macro string.
176    ASM_MACRO_STR = 153,
177    /// Invalid macro (generic error).
178    ASM_MACRO_INVALID = 154,
179    /// Unexpected backslash at end of escaped string.
180    ASM_ESC_BACKSLASH = 155,
181    /// Invalid octal escape sequence  (out of range).
182    ASM_ESC_OCTAL = 156,
183    /// Invalid escape sequence (unrecognized character).
184    ASM_ESC_SEQUENCE = 157,
185    /// Broken escape string.
186    ASM_ESC_STR = 158,
187    /// Invalid token.
188    ASM_TOKEN_INVALID = 159,
189    /// This instruction is unsupported in this mode.
190    ASM_INSN_UNSUPPORTED = 160,
191    /// Invalid fixup.
192    ASM_FIXUP_INVALID = 161,
193    /// Invalid label.
194    ASM_LABEL_INVALID = 162,
195    /// Invalid fragment.
196    ASM_FRAGMENT_INVALID = 163,
197    /// Generic input assembly errors (invalid operand) - architecture specific.
198    ASM_INVALIDOPERAND = 512,
199    /// Generic input assembly errors (missing feature) - architecture specific.
200    ASM_MISSINGFEATURE = 513,
201    /// Generic input assembly errors (mnemonic fail) - architecture specific.
202    ASM_MNEMONICFAIL = 514,
203}
204
205impl Error {
206    /// Returns the latest error recorded error, if any.
207    pub fn new(ks: KsHandle) -> Option<Self> {
208        let err = unsafe { ks_errno(ks) };
209        if err == Error::OK {
210            None
211        } else {
212            Some(err)
213        }
214    }
215
216    /// Returns a description for a given Keystone error.
217    pub fn strerror(self) -> String {
218        unsafe {
219            std::ffi::CStr::from_ptr(ks_strerror(self))
220                .to_string_lossy()
221                .into_owned()
222        }
223    }
224}
225
226impl std::error::Error for Error {}
227
228impl core::fmt::Display for Error {
229    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
230        write!(f, "{}", self.strerror())
231    }
232}
233
234/// Runtime option for the Keystone engine.
235#[repr(C)]
236#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
237pub enum OptionType {
238    /// Choose syntax for input assembly.
239    SYNTAX = 1,
240    /// Set symbol resolver callback.
241    SYM_RESOLVER = 2,
242}
243
244bitflags! {
245    /// Runtime option value (associated with OptionType above)
246    #[repr(C)]
247    pub struct OptionValue: size_t {
248        /// X86 Intel syntax - default on X86 (KS_OPT_SYNTAX).
249        const SYNTAX_INTEL = 1;
250        /// X86 ATT asm syntax (KS_OPT_SYNTAX).
251        const SYNTAX_ATT = 2;
252        /// X86 Nasm syntax (KS_OPT_SYNTAX).
253        const SYNTAX_NASM = 4;
254        /// X86 Masm syntax (KS_OPT_SYNTAX) - unsupported yet.
255        const SYNTAX_MASM = 8;
256        /// X86 GNU GAS syntax (KS_OPT_SYNTAX).
257        const SYNTAX_GAS = 16;
258        /// All immediates are in hex format (i.e 12 is 0x12).
259        const SYNTAX_RADIX16 = 32;
260    }
261}
262
263// -----------------------------------------------------------------------------------------------
264// API
265// -----------------------------------------------------------------------------------------------
266
267extern "C" {
268    /// Returns the combined API version, as well as the major and minor version numbers.
269    ///
270    /// **Inputs:**
271    ///
272    ///  * `major`: major number of API version;
273    ///  * `minor`: minor number of API version.
274    ///
275    /// **Return value:**
276    ///
277    ///  * hexical number as `major << 8 | minor`, which encodes both major & minor versions.
278    pub fn ks_version(major: *mut c_uint, minor: *mut c_uint) -> c_uint;
279
280    /// Determines if the given architecture is supported by this library.
281    ///
282    /// **Input:**
283    ///
284    ///  * `arch`: architecture type ([`Arch`]).
285    ///
286    /// **Return value:**
287    ///
288    ///  * `True` if this library supports the given arch.
289    pub fn ks_arch_supported(arch: Arch) -> c_int;
290
291    /// Crates a new instance of the Keystone engine.
292    ///
293    /// **Inputs:**
294    ///
295    ///  * `arch`: architecture type ([`Arch`]);
296    ///  * `mode`: hardware mode ([`Mode`]);
297    ///  * `ks`: pointer to a Keystone engine object created by the function if it returns without
298    ///    error.
299    ///
300    /// **Return value:**
301    ///
302    ///  * [`Error::OK`] on success, or another value on failure (refer to [`Error`] for more
303    ///    details).
304    pub fn ks_open(arch: Arch, mode: Mode, ks: *mut Option<KsHandle>) -> Error;
305
306    /// Closes the Keystone instance.
307    ///
308    /// This operation must be performed once the handle is not used anymore to release it. This
309    /// API releases cached memory, thus accessing the Keystone API through this handle once it
310    /// has been closed with `ks_close` could crash the application.
311    ///
312    /// **Input:**
313    ///
314    ///  * `ks`: pointer to a handle returned by ks_open().
315    ///
316    /// **Return value:**
317    ///
318    ///  * [`Error::OK`] on success, or another value on failure (refer to [`Error`] for more
319    ///    details).
320    pub fn ks_close(ks: KsHandle);
321
322    /// Reports the latest error number after an API call failed.
323    ///
324    /// Similarly to glibc's errno, `ks_errno` might not retain its old error once accessed.
325    ///
326    /// **Input:**
327    ///
328    ///  * `ks`: pointer to a handle returned by ks_open().
329    ///
330    /// **Return value:**
331    ///
332    ///  * The latest error code number (refer to [`Error`] for more details).
333    pub fn ks_errno(ks: KsHandle) -> Error;
334
335    /// Returns a string describing the given error code.
336    ///
337    /// **Input:**
338    ///
339    ///  * `code`: error code number (refer to [`Error`] for more details).
340    ///
341    /// **Return value:**
342    ///
343    ///  * A pointer to a string that describes the error code.
344    pub fn ks_strerror(code: Error) -> *const c_char;
345
346    /// Sets an option of the Keystone engine after the instance has been created.
347    ///
348    /// **Input:**
349    ///
350    ///  * `ks`: pointer to a handle returned by ks_open();
351    ///  * `opt_type: [`OptionType`] to set;
352    ///  * `value: corresponding [`OptionValue`] to set;
353    ///  * `code`:  error code number (refer to [`Error`] for more details).
354    ///
355    /// **Return value:**
356    ///
357    ///  * A pointer to a string that describes the error code.
358    pub fn ks_option(engine: KsHandle, opt_type: OptionType, value: OptionValue) -> Error;
359
360    /// Assembles a program from an input string containing assembly instructions.
361    ///
362    /// The resulting machine code depends on the input buffer, its size, a base address and the
363    /// number of instructions to encode. This API dynamically allocates memory to contain the
364    /// assembled instructions. The caller is responsible for freeing memory allocated by the
365    /// function and returned through `encoding`.
366    ///
367    /// **Input:**
368    ///
369    ///  * `ks`: pointer to a handle returned by ks_open();
370    ///  * `string: NULL-terminated assembly string. Use ; or \n to separate statements;
371    ///  * `address`: address of the first assembly instruction, or 0 to ignore;
372    ///  * `encoding`: array of bytes containing the resulting encoding of the input assembly
373    ///    string (this array is allocated by the function and should be freed manually using
374    ///    [`ks_free`]);
375    ///  * `encoding_size`: size of `*encoding`;
376    ///  * `stat_count`: the number of statements successfully processed.
377    ///
378    /// **Return value:**
379    ///
380    ///  * 0 on success, or -1 on failure.
381    pub fn ks_asm(
382        ks: KsHandle,
383        string: *const c_char,
384        address: u64,
385        encoding: *mut *mut c_uchar,
386        encoding_size: *mut size_t,
387        stat_count: *mut size_t,
388    ) -> c_int;
389
390    /// Frees memory allocated by ks_asm().
391    ///
392    /// **Input:**
393    ///
394    ///  * `p`: memory allocated by `ks_asm()` and returned in `encoding`.
395    pub fn ks_free(p: *mut c_uchar);
396}
397
398// -----------------------------------------------------------------------------------------------
399// Tests
400// -----------------------------------------------------------------------------------------------
401
402#[cfg(test)]
403mod tests {
404    use super::*;
405
406    #[test]
407    fn test_ks_version() {
408        let mut major = 0;
409        let mut minor = 0;
410        let version = unsafe { ks_version(&mut major, &mut minor) };
411        assert_eq!(major, API_MAJOR);
412        assert_eq!(minor, API_MINOR);
413        assert_eq!(version, API_MAJOR << 8 | API_MINOR);
414    }
415
416    #[test]
417    fn test_ks_arch_supported() {
418        assert!(unsafe { ks_arch_supported(Arch::ARM) != 0 });
419        assert!(unsafe { ks_arch_supported(Arch::ARM64) != 0 });
420        assert!(unsafe { ks_arch_supported(Arch::MIPS) != 0 });
421        assert!(unsafe { ks_arch_supported(Arch::X86) != 0 });
422        assert!(unsafe { ks_arch_supported(Arch::PPC) != 0 });
423        assert!(unsafe { ks_arch_supported(Arch::SPARC) != 0 });
424        assert!(unsafe { ks_arch_supported(Arch::SYSTEMZ) != 0 });
425        assert!(unsafe { ks_arch_supported(Arch::HEXAGON) != 0 });
426        assert!(unsafe { ks_arch_supported(Arch::EVM) != 0 });
427    }
428
429    #[test]
430    fn test_ks_open_ks_close() {
431        // ARM - valid arch/mode combination
432        let mut ks = None;
433        let err = unsafe { ks_open(Arch::ARM, Mode::LITTLE_ENDIAN | Mode::ARM, &mut ks) };
434        assert_eq!(err, Error::OK);
435        assert!(ks.is_some());
436        unsafe { ks_close(ks.unwrap()) };
437
438        // ARM64 - invalid arch/mode combination
439        let mut ks = None;
440        let err = unsafe { ks_open(Arch::ARM64, Mode::LITTLE_ENDIAN | Mode::ARM, &mut ks) };
441        assert_eq!(err, Error::MODE);
442        assert!(ks.is_none());
443    }
444
445    #[test]
446    fn test_ks_asm() {
447        // Create a handle to the Keystone engine.
448        let mut ks = None;
449        let err = unsafe { ks_open(Arch::ARM, Mode::LITTLE_ENDIAN | Mode::ARM, &mut ks) };
450        assert_eq!(err, Error::OK);
451        assert!(ks.is_some());
452
453        // Assemble instructions.
454        let mut encoding: *mut c_uchar = std::ptr::null_mut();
455        let mut encoding_size: size_t = 0;
456        let mut stat_count: size_t = 0;
457        let address = 0x1000;
458        let insns = std::ffi::CString::new(
459            "mov r0, #0x42
460            label:
461                str r0, [r1, #4]
462                b label",
463        )
464        .unwrap();
465        let err = unsafe {
466            ks_asm(
467                ks.unwrap(),
468                insns.as_ptr(),
469                address,
470                &mut encoding,
471                &mut encoding_size,
472                &mut stat_count,
473            )
474        };
475        assert_eq!(err, 0);
476        assert!(!encoding.is_null());
477
478        // Check the resulting machine code.
479        let insns_slice = unsafe { std::slice::from_raw_parts(encoding, encoding_size) };
480        let insns = insns_slice.to_vec();
481        assert_eq!(
482            insns,
483            vec![66, 0, 160, 227, 4, 0, 129, 229, 253, 255, 255, 234]
484        );
485        unsafe { ks_free(encoding) };
486    }
487
488    #[test]
489    fn test_ks_errno() {
490        // Create a handle to the Keystone engine.
491        let mut ks = None;
492        let err = unsafe { ks_open(Arch::ARM, Mode::LITTLE_ENDIAN | Mode::ARM, &mut ks) };
493        assert_eq!(err, Error::OK);
494        assert!(ks.is_some());
495
496        // Assemble instructions.
497        let mut encoding: *mut c_uchar = std::ptr::null_mut();
498        let mut encoding_size: size_t = 0;
499        let mut stat_count: size_t = 0;
500        let address = 0x1000;
501        let insns = std::ffi::CString::new(
502            "mov x0, #0x42", // Using X0 on ARM should result in an error.
503        )
504        .unwrap();
505        let err = unsafe {
506            ks_asm(
507                ks.unwrap(),
508                insns.as_ptr(),
509                address,
510                &mut encoding,
511                &mut encoding_size,
512                &mut stat_count,
513            )
514        };
515        assert_eq!(err, -1);
516        let errno = unsafe { ks_errno(ks.unwrap()) };
517        assert_eq!(errno, Error::ASM_INVALIDOPERAND);
518        let err_str = unsafe {
519            std::ffi::CStr::from_ptr(ks_strerror(errno))
520                .to_string_lossy()
521                .into_owned()
522        };
523        assert_eq!(err_str, "Invalid operand (KS_ERR_ASM_INVALIDOPERAND)");
524    }
525
526    #[test]
527    fn test_ks_option() {
528        // Create a handle to the Keystone engine.
529        let mut ks = None;
530        let err = unsafe { ks_open(Arch::X86, Mode::MODE_32, &mut ks) };
531        assert_eq!(err, Error::OK);
532        assert!(ks.is_some());
533
534        // Change an option after the instance has been created.
535        let err = unsafe { ks_option(ks.unwrap(), OptionType::SYNTAX, OptionValue::SYNTAX_ATT) };
536        assert_eq!(err, Error::OK);
537    }
538}