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}