#![allow(non_camel_case_types)]
use bitflags::bitflags;
use libc::*;
use core::marker::{PhantomData, PhantomPinned};
#[repr(C)]
pub struct KsEngine {
_data: [u8; 0],
_marker: PhantomData<(*mut u8, PhantomPinned)>,
}
pub type KsHandle = std::ptr::NonNull<KsEngine>;
pub const API_MAJOR: c_uint = 0;
pub const API_MINOR: c_uint = 9;
#[repr(C)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum Arch {
ARM = 1,
ARM64 = 2,
MIPS = 3,
X86 = 4,
PPC = 5,
SPARC = 6,
SYSTEMZ = 7,
HEXAGON = 8,
EVM = 9,
MAX = 10,
}
bitflags! {
#[repr(C)]
pub struct Mode: c_int {
const LITTLE_ENDIAN = 0;
const BIG_ENDIAN = 1073741824;
const ARM = 1;
const THUMB = 16;
const V8 = 64;
const MICRO = 16;
const MIPS3 = 32;
const MIPS32R6 = 64;
const MIPS32 = 4;
const MIPS64 = 8;
const MODE_16 = 2;
const MODE_32 = 4;
const MODE_64 = 8;
const PPC32 = 4;
const PPC64 = 8;
const QPX = 16;
const SPARC32 = 4;
const SPARC64 = 8;
const V9 = 16;
}
}
#[repr(C)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum Error {
OK = 0,
NOMEM = 1,
ARCH = 2,
HANDLE = 3,
MODE = 4,
VERSION = 5,
OPT_INVALID = 6,
ASM_EXPR_TOKEN = 128,
ASM_DIRECTIVE_VALUE_RANGE = 129,
ASM_DIRECTIVE_ID = 130,
ASM_DIRECTIVE_TOKEN = 131,
ASM_DIRECTIVE_STR = 132,
ASM_DIRECTIVE_COMMA = 133,
ASM_DIRECTIVE_RELOC_NAME = 134,
ASM_DIRECTIVE_RELOC_TOKEN = 135,
ASM_DIRECTIVE_FPOINT = 136,
ASM_DIRECTIVE_UNKNOWN = 137,
ASM_DIRECTIVE_EQU = 138,
ASM_DIRECTIVE_INVALID = 139,
ASM_VARIANT_INVALID = 140,
ASM_EXPR_BRACKET = 141,
ASM_SYMBOL_MODIFIER = 142,
ASM_SYMBOL_REDEFINED = 143,
ASM_SYMBOL_MISSING = 144,
ASM_RPAREN = 145,
ASM_STAT_TOKEN = 146,
ASM_UNSUPPORTED = 147,
ASM_MACRO_TOKEN = 148,
ASM_MACRO_PAREN = 149,
ASM_MACRO_EQU = 150,
ASM_MACRO_ARGS = 151,
ASM_MACRO_LEVELS_EXCEED = 152,
ASM_MACRO_STR = 153,
ASM_MACRO_INVALID = 154,
ASM_ESC_BACKSLASH = 155,
ASM_ESC_OCTAL = 156,
ASM_ESC_SEQUENCE = 157,
ASM_ESC_STR = 158,
ASM_TOKEN_INVALID = 159,
ASM_INSN_UNSUPPORTED = 160,
ASM_FIXUP_INVALID = 161,
ASM_LABEL_INVALID = 162,
ASM_FRAGMENT_INVALID = 163,
ASM_INVALIDOPERAND = 512,
ASM_MISSINGFEATURE = 513,
ASM_MNEMONICFAIL = 514,
}
impl Error {
pub fn new(ks: KsHandle) -> Option<Self> {
let err = unsafe { ks_errno(ks) };
if err == Error::OK {
None
} else {
Some(err)
}
}
pub fn strerror(self) -> String {
unsafe {
std::ffi::CStr::from_ptr(ks_strerror(self))
.to_string_lossy()
.into_owned()
}
}
}
impl std::error::Error for Error {}
impl core::fmt::Display for Error {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}", self.strerror())
}
}
#[repr(C)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
pub enum OptionType {
SYNTAX = 1,
SYM_RESOLVER = 2,
}
bitflags! {
#[repr(C)]
pub struct OptionValue: size_t {
const SYNTAX_INTEL = 1;
const SYNTAX_ATT = 2;
const SYNTAX_NASM = 4;
const SYNTAX_MASM = 8;
const SYNTAX_GAS = 16;
const SYNTAX_RADIX16 = 32;
}
}
extern "C" {
pub fn ks_version(major: *mut c_uint, minor: *mut c_uint) -> c_uint;
pub fn ks_arch_supported(arch: Arch) -> c_int;
pub fn ks_open(arch: Arch, mode: Mode, ks: *mut Option<KsHandle>) -> Error;
pub fn ks_close(ks: KsHandle);
pub fn ks_errno(ks: KsHandle) -> Error;
pub fn ks_strerror(code: Error) -> *const c_char;
pub fn ks_option(engine: KsHandle, opt_type: OptionType, value: OptionValue) -> Error;
pub fn ks_asm(
ks: KsHandle,
string: *const c_char,
address: u64,
encoding: *mut *mut c_uchar,
encoding_size: *mut size_t,
stat_count: *mut size_t,
) -> c_int;
pub fn ks_free(p: *mut c_uchar);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_ks_version() {
let mut major = 0;
let mut minor = 0;
let version = unsafe { ks_version(&mut major, &mut minor) };
assert_eq!(major, API_MAJOR);
assert_eq!(minor, API_MINOR);
assert_eq!(version, API_MAJOR << 8 | API_MINOR);
}
#[test]
fn test_ks_arch_supported() {
assert!(unsafe { ks_arch_supported(Arch::ARM) != 0 });
assert!(unsafe { ks_arch_supported(Arch::ARM64) != 0 });
assert!(unsafe { ks_arch_supported(Arch::MIPS) != 0 });
assert!(unsafe { ks_arch_supported(Arch::X86) != 0 });
assert!(unsafe { ks_arch_supported(Arch::PPC) != 0 });
assert!(unsafe { ks_arch_supported(Arch::SPARC) != 0 });
assert!(unsafe { ks_arch_supported(Arch::SYSTEMZ) != 0 });
assert!(unsafe { ks_arch_supported(Arch::HEXAGON) != 0 });
assert!(unsafe { ks_arch_supported(Arch::EVM) != 0 });
}
#[test]
fn test_ks_open_ks_close() {
let mut ks = None;
let err = unsafe { ks_open(Arch::ARM, Mode::LITTLE_ENDIAN | Mode::ARM, &mut ks) };
assert_eq!(err, Error::OK);
assert!(ks.is_some());
unsafe { ks_close(ks.unwrap()) };
let mut ks = None;
let err = unsafe { ks_open(Arch::ARM64, Mode::LITTLE_ENDIAN | Mode::ARM, &mut ks) };
assert_eq!(err, Error::MODE);
assert!(ks.is_none());
}
#[test]
fn test_ks_asm() {
let mut ks = None;
let err = unsafe { ks_open(Arch::ARM, Mode::LITTLE_ENDIAN | Mode::ARM, &mut ks) };
assert_eq!(err, Error::OK);
assert!(ks.is_some());
let mut encoding: *mut c_uchar = std::ptr::null_mut();
let mut encoding_size: size_t = 0;
let mut stat_count: size_t = 0;
let address = 0x1000;
let insns = std::ffi::CString::new(
"mov r0, #0x42
label:
str r0, [r1, #4]
b label",
)
.unwrap();
let err = unsafe {
ks_asm(
ks.unwrap(),
insns.as_ptr(),
address,
&mut encoding,
&mut encoding_size,
&mut stat_count,
)
};
assert_eq!(err, 0);
assert!(!encoding.is_null());
let insns_slice = unsafe { std::slice::from_raw_parts(encoding, encoding_size) };
let insns = insns_slice.to_vec();
assert_eq!(
insns,
vec![66, 0, 160, 227, 4, 0, 129, 229, 253, 255, 255, 234]
);
unsafe { ks_free(encoding) };
}
#[test]
fn test_ks_errno() {
let mut ks = None;
let err = unsafe { ks_open(Arch::ARM, Mode::LITTLE_ENDIAN | Mode::ARM, &mut ks) };
assert_eq!(err, Error::OK);
assert!(ks.is_some());
let mut encoding: *mut c_uchar = std::ptr::null_mut();
let mut encoding_size: size_t = 0;
let mut stat_count: size_t = 0;
let address = 0x1000;
let insns = std::ffi::CString::new(
"mov x0, #0x42", )
.unwrap();
let err = unsafe {
ks_asm(
ks.unwrap(),
insns.as_ptr(),
address,
&mut encoding,
&mut encoding_size,
&mut stat_count,
)
};
assert_eq!(err, -1);
let errno = unsafe { ks_errno(ks.unwrap()) };
assert_eq!(errno, Error::ASM_INVALIDOPERAND);
let err_str = unsafe {
std::ffi::CStr::from_ptr(ks_strerror(errno))
.to_string_lossy()
.into_owned()
};
assert_eq!(err_str, "Invalid operand (KS_ERR_ASM_INVALIDOPERAND)");
}
#[test]
fn test_ks_option() {
let mut ks = None;
let err = unsafe { ks_open(Arch::X86, Mode::MODE_32, &mut ks) };
assert_eq!(err, Error::OK);
assert!(ks.is_some());
let err = unsafe { ks_option(ks.unwrap(), OptionType::SYNTAX, OptionValue::SYNTAX_ATT) };
assert_eq!(err, Error::OK);
}
}