mirror of
https://github.com/bitdefender/bddisasm.git
synced 2024-11-18 05:28:09 +00:00
Initial Rust bindings implementation
This commit is contained in:
parent
4a485853b6
commit
51dbf5fb0a
4
.gitignore
vendored
4
.gitignore
vendored
@ -64,6 +64,8 @@ bdshemu_fuzz/out-32
|
||||
bdshemu_fuzz/out-64
|
||||
docs/build
|
||||
libbddisasm.pc
|
||||
build*
|
||||
build/
|
||||
.vscode
|
||||
disasmtool_lix/_build
|
||||
rsbddisasm/target
|
||||
rsbddisasm/Cargo.lock
|
||||
|
6
rsbddisasm/Cargo.toml
Normal file
6
rsbddisasm/Cargo.toml
Normal file
@ -0,0 +1,6 @@
|
||||
[workspace]
|
||||
|
||||
members = [
|
||||
"bddisasm-sys",
|
||||
"bddisasm",
|
||||
]
|
26
rsbddisasm/README.md
Normal file
26
rsbddisasm/README.md
Normal file
@ -0,0 +1,26 @@
|
||||
# bddisasm Rust bindings
|
||||
|
||||
## Build
|
||||
|
||||
Run `cargo build` or `cargo build --release`.
|
||||
|
||||
## Directory structure
|
||||
|
||||
### bddisasm-sys
|
||||
|
||||
This crate uses the [*-sys crate convention](https://doc.rust-lang.org/cargo/reference/build-scripts.html#-sys-packages) and links with `libbddisasm`, exposing a raw, low-level, interface.
|
||||
|
||||
### bddisasm
|
||||
|
||||
This crate aims to offer a higher-level interface over [bddisasm-sys](#bddisasm-sys). It is currently held back by the fact that I don't really know Rust.
|
||||
|
||||
Parts of it are auto-generated, with slight manual changes (for example, the `Mnemonic` enum and related functions).
|
||||
|
||||
#### TODO
|
||||
|
||||
- [ ] encode registers, not just the register index (for example, instead of `Gpr` we should have `Gpr::Rax`, `Gpr::Eax`, etc)
|
||||
- [ ] more documentation for the `operand` module, with examples on how to work wit h each operand type
|
||||
- [ ] more examples for `cpuid`
|
||||
- [ ] more examples for `cpu_modes`
|
||||
- [ ] an API to check if an instruction is supported with certain CPU modes on or off (currently a user has to manually check the `CpuModes` structure)
|
||||
- [ ] implement `Display` for more types (especially those in `operand`)
|
25
rsbddisasm/bddisasm-sys/Cargo.toml
Normal file
25
rsbddisasm/bddisasm-sys/Cargo.toml
Normal file
@ -0,0 +1,25 @@
|
||||
[package]
|
||||
name = "bddisasm-sys"
|
||||
version = "0.1.0"
|
||||
authors = ["Cristi Anichitei <ianichitei@bitdefender.com>"]
|
||||
edition = "2018"
|
||||
links = "bddisasm"
|
||||
build = "build.rs"
|
||||
license = "Apache-2.0"
|
||||
repository = "https://github.com/bitdefender/bddisasm"
|
||||
documentation = "https://docs.rs/bddisasm-sys"
|
||||
description = """
|
||||
Bindings to bddisasm instruction decoder library
|
||||
"""
|
||||
categories = ["external-ffi-bindings", "hardware-support"]
|
||||
keywords = ["disassembler", "decoder", "x86", "amd64", "x86_64", "bindings"]
|
||||
|
||||
[lib]
|
||||
name = "bddisasm_sys"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
|
||||
[build-dependencies]
|
||||
bindgen = "0.59.1"
|
||||
cc = "1.0.70"
|
1
rsbddisasm/bddisasm-sys/LICENSE
Symbolic link
1
rsbddisasm/bddisasm-sys/LICENSE
Symbolic link
@ -0,0 +1 @@
|
||||
../../LICENSE
|
39
rsbddisasm/bddisasm-sys/build.rs
Normal file
39
rsbddisasm/bddisasm-sys/build.rs
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Bitdefender
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
use std::env;
|
||||
use std::path::PathBuf;
|
||||
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=csrc");
|
||||
|
||||
cc::Build::new()
|
||||
.file("csrc/bddisasm/bddisasm.c")
|
||||
.file("csrc/bddisasm/bdformat.c")
|
||||
.file("csrc/bddisasm/crt.c")
|
||||
.include("csrc/bddisasm/include")
|
||||
.include("csrc/inc")
|
||||
.define("BDDISASM_HAS_VSNPRINTF", Some("1"))
|
||||
.define("BDDISASM_HAS_MEMSET", Some("1"))
|
||||
.compile("bddisasm");
|
||||
|
||||
let bindings = bindgen::Builder::default()
|
||||
.header("csrc/bddisasm_wrapper.h")
|
||||
.allowlist_function("Nd.*")
|
||||
.allowlist_type("ND.*")
|
||||
.allowlist_var("ND.*")
|
||||
.rustified_enum(".*")
|
||||
.impl_debug(true)
|
||||
.generate_comments(false)
|
||||
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
|
||||
.generate()
|
||||
.expect("Unable to generate bindings");
|
||||
|
||||
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
|
||||
bindings
|
||||
.write_to_file(out_path.join("bindings.rs"))
|
||||
.expect("Couldn't write bindings!");
|
||||
|
||||
println!("cargo:rustc-link-lib=static=bddisasm");
|
||||
}
|
1
rsbddisasm/bddisasm-sys/csrc/bddisasm
Symbolic link
1
rsbddisasm/bddisasm-sys/csrc/bddisasm
Symbolic link
@ -0,0 +1 @@
|
||||
../../../bddisasm
|
10
rsbddisasm/bddisasm-sys/csrc/bddisasm_wrapper.h
Normal file
10
rsbddisasm/bddisasm-sys/csrc/bddisasm_wrapper.h
Normal file
@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Bitdefender
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "inc/bddisasm.h"
|
1
rsbddisasm/bddisasm-sys/csrc/inc
Symbolic link
1
rsbddisasm/bddisasm-sys/csrc/inc
Symbolic link
@ -0,0 +1 @@
|
||||
../../../inc
|
93
rsbddisasm/bddisasm-sys/src/lib.rs
Normal file
93
rsbddisasm/bddisasm-sys/src/lib.rs
Normal file
@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Bitdefender
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#![allow(non_camel_case_types)]
|
||||
#![allow(non_snake_case)]
|
||||
#![allow(non_upper_case_globals)]
|
||||
#![allow(deref_nullptr)] // See https://github.com/rust-lang/rust-bindgen/issues/1651
|
||||
#![allow(clippy::all)]
|
||||
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use ::std::os::raw::c_char;
|
||||
use std::mem;
|
||||
|
||||
#[test]
|
||||
fn get_version() {
|
||||
let mut major: u32 = 0;
|
||||
let mut minor: u32 = 0;
|
||||
let mut revision: u32 = 0;
|
||||
let mut build_date: *mut c_char = std::ptr::null_mut();
|
||||
let mut build_time: *mut c_char = std::ptr::null_mut();
|
||||
|
||||
unsafe {
|
||||
NdGetVersion(
|
||||
&mut major,
|
||||
&mut minor,
|
||||
&mut revision,
|
||||
&mut build_date,
|
||||
&mut build_time,
|
||||
);
|
||||
}
|
||||
|
||||
println!("major: {} minor: {} rev: {}", major, minor, revision);
|
||||
|
||||
let build_date = unsafe { std::ffi::CStr::from_ptr(build_date) };
|
||||
let build_date = build_date.to_str().unwrap().to_string();
|
||||
let build_time = unsafe { std::ffi::CStr::from_ptr(build_time) };
|
||||
let build_time = build_time.to_str().unwrap().to_string();
|
||||
println!("Build date: {} build time: {}", build_date, build_time);
|
||||
|
||||
// There are no other asserts in this test. Enforcing a known minor version is not worth it, we mainly want to
|
||||
// see that `NdGetVersion` works.
|
||||
assert_eq!(major, 1);
|
||||
}
|
||||
|
||||
fn do_decode(code: &[u8]) -> (INSTRUX, NDSTATUS) {
|
||||
let mut instrux: mem::MaybeUninit<INSTRUX> = mem::MaybeUninit::uninit();
|
||||
let instrux = instrux.as_mut_ptr();
|
||||
|
||||
let status = unsafe {
|
||||
NdDecodeEx(
|
||||
instrux,
|
||||
code.as_ptr(),
|
||||
code.len() as u64,
|
||||
ND_CODE_32 as u8,
|
||||
ND_DATA_32 as u8,
|
||||
)
|
||||
};
|
||||
|
||||
(unsafe { *instrux }, status)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decode() {
|
||||
let code = vec![0x90];
|
||||
let (instrux, status) = do_decode(&code);
|
||||
|
||||
assert_eq!(status, 0, "Failed to decode instruction {:#x?}", code);
|
||||
assert_eq!(
|
||||
unsafe { instrux.__bindgen_anon_2.Instruction },
|
||||
_ND_INS_CLASS::ND_INS_NOP
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn format() {
|
||||
let code = vec![0x89, 0x29];
|
||||
let (instrux, status) = do_decode(&code);
|
||||
|
||||
assert_eq!(status, 0, "Failed to decode instruction {:#x?}", code);
|
||||
|
||||
let mut buffer: [i8; ND_MIN_BUF_SIZE as usize] = [0; ND_MIN_BUF_SIZE as usize];
|
||||
let status = unsafe { NdToText(&instrux, 0, ND_MIN_BUF_SIZE, buffer.as_mut_ptr()) };
|
||||
assert_eq!(status, 0, "Failed to decode format {:#x?}", code);
|
||||
|
||||
let text = String::from_utf8(buffer.iter().map(|&c| c as u8).collect()).unwrap();
|
||||
let text = text.trim_matches(char::from(0));
|
||||
assert_eq!(text, "MOV dword ptr [ecx], ebp");
|
||||
}
|
||||
}
|
17
rsbddisasm/bddisasm/Cargo.toml
Normal file
17
rsbddisasm/bddisasm/Cargo.toml
Normal file
@ -0,0 +1,17 @@
|
||||
[package]
|
||||
name = "bddisasm"
|
||||
version = "0.1.0"
|
||||
authors = ["Cristi Anichitei <ianichitei@bitdefender.com>"]
|
||||
edition = "2018"
|
||||
license = "Apache-2.0"
|
||||
readme = "README.md"
|
||||
repository = "https://github.com/bitdefender/bddisasm"
|
||||
documentation = "https://docs.rs/bddisasm"
|
||||
description = """
|
||||
Bindings to bddisasm instruction decoder library
|
||||
"""
|
||||
categories = ["api-bindings", "hardware-support"]
|
||||
keywords = ["disassembler", "decoder", "x86", "amd64", "x86_64", "bindings"]
|
||||
|
||||
[dependencies]
|
||||
bddisasm-sys = { version = "0.1.0", path = "../bddisasm-sys" }
|
147
rsbddisasm/bddisasm/src/cpu_modes.rs
Normal file
147
rsbddisasm/bddisasm/src/cpu_modes.rs
Normal file
@ -0,0 +1,147 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Bitdefender
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
//! Offers information about the CPU modes in which an instruction is supported.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```
|
||||
//! # use std::error::Error;
|
||||
//! #
|
||||
//! # fn main() -> Result<(), Box<dyn Error>> {
|
||||
//! use bddisasm::decoded_instruction::{DecodedInstruction, DecodeMode, Mnemonic, OperandSize};
|
||||
//!
|
||||
//! // `VMXON qword ptr [rax]`
|
||||
//! let ins = DecodedInstruction::decode(&[0xf3, 0x0f, 0xc7, 0x30], DecodeMode::Bits64)?;
|
||||
//! let modes = ins.valid_cpu_modes();
|
||||
//!
|
||||
//! // Check if the instruction is available from user mode
|
||||
//! if modes.privilege_level.ring3 {
|
||||
//! println!("Available in user mode");
|
||||
//! } else {
|
||||
//! println!("Not available in user mode");
|
||||
//! }
|
||||
//!
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
|
||||
extern crate bddisasm_sys as ffi;
|
||||
|
||||
// TODO: maybe use something like the `bitflags` crate and have all these as flags?
|
||||
|
||||
/// Privilege levels (rings) in which an instruction is supported.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct PrivilegeLevel {
|
||||
pub ring0: bool,
|
||||
pub ring1: bool,
|
||||
pub ring2: bool,
|
||||
pub ring3: bool,
|
||||
}
|
||||
|
||||
/// Operating modes in which an instruction is supported.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct OperatingMode {
|
||||
/// The instruction is valid in real mode.
|
||||
pub real: bool,
|
||||
|
||||
/// The instruction is valid in Virtual 8086 mode.
|
||||
pub v8086: bool,
|
||||
|
||||
/// The instruction is valid in protected mode (32 bit).
|
||||
pub protected: bool,
|
||||
|
||||
/// The instruction is valid in compatibility mode (32 bit in 64 bit).
|
||||
pub compat: bool,
|
||||
|
||||
/// The instruction is valid in long mode.
|
||||
pub long: bool,
|
||||
}
|
||||
|
||||
/// Special modes - these may be active inside other modes (example: `TSX` in `Long mode`).
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct SpecialModes {
|
||||
/// The instruction is valid in System Management Mode.
|
||||
pub smm: bool,
|
||||
|
||||
/// The instruction is valid outside System Management Mode.
|
||||
pub smm_off: bool,
|
||||
|
||||
/// The instruction is valid in SGX mode.
|
||||
pub sgx: bool,
|
||||
|
||||
/// The instruction is valid outside SGX mode.
|
||||
pub sgx_off: bool,
|
||||
|
||||
/// The instruction is valid in transactional regions.
|
||||
pub tsx: bool,
|
||||
|
||||
/// The instruction is valid in transactional regions.
|
||||
pub tsx_off: bool,
|
||||
}
|
||||
|
||||
/// VMX mode - they engulf all the other modes.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct VmxMode {
|
||||
/// The instruction is valid in VMX root mode.
|
||||
pub root: bool,
|
||||
|
||||
/// The instruction is valid in VMX non root mode.
|
||||
pub non_root: bool,
|
||||
|
||||
/// The instruction is valid in VMX root SEAM.
|
||||
pub root_seam: bool,
|
||||
|
||||
/// The instruction is valid in VMX non-root SEAM.
|
||||
pub non_root_seam: bool,
|
||||
|
||||
/// The instruction is valid outside VMX operation.
|
||||
pub off: bool,
|
||||
}
|
||||
|
||||
/// Indicates in which modes the instruction is valid in
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct CpuModes {
|
||||
pub privilege_level: PrivilegeLevel,
|
||||
pub operating_mode: OperatingMode,
|
||||
pub special_modes: SpecialModes,
|
||||
pub vmx: VmxMode,
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl From<ffi::ND_VALID_MODES> for CpuModes {
|
||||
fn from(raw: ffi::ND_VALID_MODES) -> CpuModes {
|
||||
let raw = unsafe { raw.__bindgen_anon_1 };
|
||||
|
||||
CpuModes {
|
||||
privilege_level: PrivilegeLevel {
|
||||
ring0: raw.Ring0() != 0,
|
||||
ring1: raw.Ring1() != 0,
|
||||
ring2: raw.Ring2() != 0,
|
||||
ring3: raw.Ring3() != 0,
|
||||
},
|
||||
operating_mode: OperatingMode {
|
||||
real: raw.Real() != 0,
|
||||
v8086: raw.V8086() != 0,
|
||||
protected: raw.Protected() != 0,
|
||||
compat: raw.Compat() != 0,
|
||||
long: raw.Long() != 0,
|
||||
},
|
||||
special_modes: SpecialModes {
|
||||
smm: raw.Smm() != 0,
|
||||
smm_off: raw.SmmOff() != 0,
|
||||
sgx: raw.Sgx() != 0,
|
||||
sgx_off: raw.SgxOff() != 0,
|
||||
tsx: raw.Tsx() != 0,
|
||||
tsx_off: raw.TsxOff() != 0,
|
||||
},
|
||||
vmx: VmxMode {
|
||||
root: raw.VmxRoot() != 0,
|
||||
non_root: raw.VmxNonRoot() != 0,
|
||||
root_seam: raw.VmxRootSeam() != 0,
|
||||
non_root_seam: raw.VmxNonRootSeam() != 0,
|
||||
off: raw.VmxOff() != 0,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
63
rsbddisasm/bddisasm/src/cpuid.rs
Normal file
63
rsbddisasm/bddisasm/src/cpuid.rs
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Bitdefender
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
//! CPUID information used to check if an instruction is supported on a CPU or not.
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ```
|
||||
//! # use std::error::Error;
|
||||
//! #
|
||||
//! # fn test() -> Option<()> {
|
||||
//! use bddisasm::decoded_instruction::{DecodedInstruction, DecodeMode};
|
||||
//!
|
||||
//! // `ENCLS`
|
||||
//! let ins = DecodedInstruction::decode(&[0x0f, 0x01, 0xcf], DecodeMode::Bits64).ok()?;
|
||||
//! let cpuid = ins.cpuid()?;
|
||||
//! println!("Leaf: {}", cpuid.leaf);
|
||||
//! match cpuid.sub_leaf {
|
||||
//! Some(sub_leaf) => println!("Sub-leaf: {}", sub_leaf),
|
||||
//! None => println!("Sub-leaf: None"),
|
||||
//! }
|
||||
//! println!("Register to check: {} Bit: {}", cpuid.register, cpuid.bit);
|
||||
//!
|
||||
//! # Some(())
|
||||
//! # }
|
||||
|
||||
extern crate bddisasm_sys as ffi;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
/// Describes the CPUID leaf, sub-leaf, register & bit that indicate whether an instruction is supported or not.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct Cpuid {
|
||||
/// CPUID leaf.
|
||||
pub leaf: u32,
|
||||
|
||||
/// CPUID sub-leaf.
|
||||
pub sub_leaf: Option<u32>,
|
||||
|
||||
/// The register that contains information regarding the instruction.
|
||||
pub register: u8,
|
||||
|
||||
/// Bit inside the register that indicates whether the instruction is present.
|
||||
pub bit: u64,
|
||||
}
|
||||
|
||||
impl fmt::Display for Cpuid {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.sub_leaf {
|
||||
Some(sub_leaf) => write!(
|
||||
f,
|
||||
"leaf: {:#x} sub-leaf: {:#x} register: {} bit to test: {:#x}",
|
||||
self.leaf, sub_leaf, self.register, self.bit
|
||||
),
|
||||
None => write!(
|
||||
f,
|
||||
"leaf: {:#x} sub-leaf: - register: {} bit to test: {:#x}",
|
||||
self.leaf, self.register, self.bit
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
256
rsbddisasm/bddisasm/src/decode_error.rs
Normal file
256
rsbddisasm/bddisasm/src/decode_error.rs
Normal file
@ -0,0 +1,256 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Bitdefender
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
//! Errors that can be encountered when decoding an instruction or when trying to get details about a decoded
|
||||
//! instruction.
|
||||
//!
|
||||
//! # Notes
|
||||
//!
|
||||
//! All error codes that can be returned by `bddisasm-sys` are encapsulated in the [DecodeError](DecodeError) enum.
|
||||
//! However, some of these are unlikely to be encountered when using this crate (for example,
|
||||
//! [BufferOverflow](DecodeError::BufferOverflow)) which indicates that a buffer passed to the `bddisasm` C library is
|
||||
//! not large enough.
|
||||
//!
|
||||
//! Other errors, such as [UnknownStatus](DecodeError::UnknownStatus) or
|
||||
//! [UnknownInstruction](DecodeError::UnknownInstruction) are used to indicate that this crate is out of sync with
|
||||
//! `bddisasm-sys`, which also should never happen.
|
||||
|
||||
extern crate bddisasm_sys as ffi;
|
||||
|
||||
use std::error::Error;
|
||||
use std::fmt;
|
||||
|
||||
/// Holds all the possible errors that can be encountered by the decoder.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub enum DecodeError {
|
||||
/// The underlying bddisasm status is not known. The inner value holds the status as it was returned by bddisasm.
|
||||
///
|
||||
/// This usually means that this library is out of sync with bddisasm-sys and is not aware that a new error status
|
||||
/// was added.
|
||||
UnknownStatus(u32),
|
||||
|
||||
/// The instruction class returned by bddisasm is not known. The inner value holds the instruction class as it was
|
||||
/// returned by bddisasm.
|
||||
///
|
||||
/// This usually means that this library is out of sync with bddisasm-sys and is not aware that a new instruction
|
||||
/// class was added.
|
||||
UnknownInstruction(u32),
|
||||
|
||||
/// The provided input buffer is too small and does not contain a valid instruction.
|
||||
BufferTooSmall,
|
||||
|
||||
/// Invalid encoding/instruction.
|
||||
InvalidEncoding,
|
||||
|
||||
/// Instruction exceeds the maximum 15 bytes.
|
||||
InstructionTooLong,
|
||||
|
||||
/// Invalid prefix sequence is present.
|
||||
InvalidPrefixSequence,
|
||||
|
||||
/// The instruction uses an invalid register.
|
||||
InvalidRegisterInInstruction,
|
||||
|
||||
/// XOP is present, but also a legacy prefix.
|
||||
XopWithPrefix,
|
||||
|
||||
/// VEX is present, but also a legacy prefix.
|
||||
VexWithPrefix,
|
||||
|
||||
/// EVEX is present, but also a legacy prefix.
|
||||
EvexWithPrefix,
|
||||
|
||||
/// Invalid encoding/instruction.
|
||||
InvalidEncodingInMode,
|
||||
|
||||
/// Invalid usage of LOCK.
|
||||
BadLockPrefix,
|
||||
|
||||
/// An attempt to load the CS register.
|
||||
CsLoad,
|
||||
|
||||
/// 0x66 prefix is not accepted.
|
||||
Prefix66NotAccepted,
|
||||
|
||||
/// 16 bit addressing mode not supported.
|
||||
AddressingNotSupported16Bit,
|
||||
|
||||
/// RIP-relative addressing not supported.
|
||||
RipRelAddressingNotSupported,
|
||||
|
||||
/// Instruction uses VSIB, but SIB is not present.
|
||||
VsibWithoutSib,
|
||||
|
||||
/// VSIB addressing, same vector reg used more than once.
|
||||
InvalidVsibRegs,
|
||||
|
||||
/// VEX.VVVV field must be zero.
|
||||
VexVvvvMustBeZero,
|
||||
|
||||
/// Masking is not supported.
|
||||
MaskNotSupported,
|
||||
|
||||
/// Masking is mandatory.
|
||||
MaskRequired,
|
||||
|
||||
/// Embedded rounding/SAE not supported.
|
||||
ErSaeNotSupported,
|
||||
|
||||
/// Zeroing not supported.
|
||||
ZeroingNotSupported,
|
||||
|
||||
/// Zeroing on memory.
|
||||
ZeroingOnMemory,
|
||||
|
||||
/// Zeroing without masking.
|
||||
ZeroingNoMask,
|
||||
|
||||
/// Broadcast not supported.
|
||||
BroadcastNotSupported,
|
||||
|
||||
/// EVEX.V' field must be one (negated 0).
|
||||
BadEvexVPrime,
|
||||
|
||||
/// EVEX.L'L field is invalid for the instruction.
|
||||
BadEvexLl,
|
||||
|
||||
/// Instruction uses SIBMEM, but SIB is not present.
|
||||
SibmemWithoutSib,
|
||||
|
||||
/// Tile registers are not unique.
|
||||
InvalidTileRegs,
|
||||
|
||||
/// Destination register is not unique (used as src).
|
||||
InvalidDestRegs,
|
||||
|
||||
/// An invalid parameter was provided.
|
||||
InvalidParameter,
|
||||
|
||||
/// The INSTRUX contains unexpected values.
|
||||
InvalidInstrux,
|
||||
|
||||
/// Not enough space is available.
|
||||
BufferOverflow,
|
||||
|
||||
/// Internal library error.
|
||||
InternalError,
|
||||
}
|
||||
|
||||
impl fmt::Display for DecodeError {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
DecodeError::UnknownStatus(value) => write!(f, "unknown status: {:#x}", value),
|
||||
DecodeError::UnknownInstruction(value) => {
|
||||
write!(f, "unknown instruction: {:#x}", value)
|
||||
}
|
||||
DecodeError::BufferTooSmall => write!(f, "the provided input buffer is too small"),
|
||||
DecodeError::InvalidEncoding => write!(f, "invalid encoding/instruction"),
|
||||
DecodeError::InstructionTooLong => {
|
||||
write!(f, "instruction exceeds the maximum 15 bytes")
|
||||
}
|
||||
DecodeError::InvalidPrefixSequence => write!(f, "invalid prefix sequence is present"),
|
||||
DecodeError::InvalidRegisterInInstruction => {
|
||||
write!(f, "the instruction uses an invalid register")
|
||||
}
|
||||
DecodeError::XopWithPrefix => write!(f, "XOP is present, but also a legacy prefix"),
|
||||
DecodeError::VexWithPrefix => write!(f, "VEX is present, but also a legacy prefix"),
|
||||
DecodeError::EvexWithPrefix => write!(f, "EVEX is present, but also a legacy prefix"),
|
||||
DecodeError::InvalidEncodingInMode => write!(f, "invalid encoding/instruction"),
|
||||
DecodeError::BadLockPrefix => write!(f, "invalid usage of LOCK"),
|
||||
DecodeError::CsLoad => write!(f, "an attempt to load the CS register"),
|
||||
DecodeError::Prefix66NotAccepted => write!(f, "0x66 prefix is not accepted"),
|
||||
DecodeError::AddressingNotSupported16Bit => {
|
||||
write!(f, "16 bit addressing mode not supported")
|
||||
}
|
||||
DecodeError::RipRelAddressingNotSupported => {
|
||||
write!(f, "RIP-relative addressing not supported")
|
||||
}
|
||||
DecodeError::VsibWithoutSib => {
|
||||
write!(f, "instruction uses VSIB, but SIB is not present")
|
||||
}
|
||||
DecodeError::InvalidVsibRegs => {
|
||||
write!(
|
||||
f,
|
||||
"VSIB addressing with the same vector register used more than once"
|
||||
)
|
||||
}
|
||||
DecodeError::VexVvvvMustBeZero => write!(f, "VEX.VVVV field must be zero"),
|
||||
DecodeError::MaskNotSupported => write!(f, "masking is not supported"),
|
||||
DecodeError::MaskRequired => write!(f, "masking is mandatory"),
|
||||
DecodeError::ErSaeNotSupported => write!(f, "embedded rounding/SAE not supported"),
|
||||
DecodeError::ZeroingNotSupported => write!(f, "zeroing not supported"),
|
||||
DecodeError::ZeroingOnMemory => write!(f, "zeroing on memory"),
|
||||
DecodeError::ZeroingNoMask => write!(f, "zeroing without masking"),
|
||||
DecodeError::BroadcastNotSupported => write!(f, "broadcast not supported"),
|
||||
DecodeError::BadEvexVPrime => write!(f, "EVEX.V' field must be one (negated 0)"),
|
||||
DecodeError::BadEvexLl => write!(f, "EVEX.L'L field is invalid for the instruction"),
|
||||
DecodeError::SibmemWithoutSib => {
|
||||
write!(f, "instruction uses SIBMEM, but SIB is not present")
|
||||
}
|
||||
DecodeError::InvalidTileRegs => write!(f, "tile registers are not unique"),
|
||||
DecodeError::InvalidDestRegs => {
|
||||
write!(f, "destination register is not unique (used as src)")
|
||||
}
|
||||
DecodeError::InvalidParameter => write!(f, "an invalid parameter was provided"),
|
||||
DecodeError::InvalidInstrux => {
|
||||
write!(f, "the INSTRUX structure contains unexpected values")
|
||||
}
|
||||
DecodeError::BufferOverflow => {
|
||||
write!(f, "not enough space is available to format instruction")
|
||||
}
|
||||
DecodeError::InternalError => write!(f, "internal error"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Error for DecodeError {}
|
||||
|
||||
pub(crate) fn status_to_error(status: ffi::NDSTATUS) -> Result<(), DecodeError> {
|
||||
if status == ffi::ND_STATUS_SUCCESS || status == ffi::ND_STATUS_HINT_OPERAND_NOT_USED {
|
||||
Ok(())
|
||||
} else {
|
||||
match status {
|
||||
ffi::ND_STATUS_BUFFER_TOO_SMALL => Err(DecodeError::BufferTooSmall),
|
||||
ffi::ND_STATUS_INVALID_ENCODING => Err(DecodeError::InvalidEncoding),
|
||||
ffi::ND_STATUS_INSTRUCTION_TOO_LONG => Err(DecodeError::InstructionTooLong),
|
||||
ffi::ND_STATUS_INVALID_PREFIX_SEQUENCE => Err(DecodeError::InvalidPrefixSequence),
|
||||
ffi::ND_STATUS_INVALID_REGISTER_IN_INSTRUCTION => {
|
||||
Err(DecodeError::InvalidRegisterInInstruction)
|
||||
}
|
||||
ffi::ND_STATUS_XOP_WITH_PREFIX => Err(DecodeError::XopWithPrefix),
|
||||
ffi::ND_STATUS_VEX_WITH_PREFIX => Err(DecodeError::VexWithPrefix),
|
||||
ffi::ND_STATUS_EVEX_WITH_PREFIX => Err(DecodeError::EvexWithPrefix),
|
||||
ffi::ND_STATUS_INVALID_ENCODING_IN_MODE => Err(DecodeError::InvalidEncodingInMode),
|
||||
ffi::ND_STATUS_BAD_LOCK_PREFIX => Err(DecodeError::BadLockPrefix),
|
||||
ffi::ND_STATUS_CS_LOAD => Err(DecodeError::CsLoad),
|
||||
ffi::ND_STATUS_66_NOT_ACCEPTED => Err(DecodeError::Prefix66NotAccepted),
|
||||
ffi::ND_STATUS_16_BIT_ADDRESSING_NOT_SUPPORTED => {
|
||||
Err(DecodeError::AddressingNotSupported16Bit)
|
||||
}
|
||||
ffi::ND_STATUS_RIP_REL_ADDRESSING_NOT_SUPPORTED => {
|
||||
Err(DecodeError::RipRelAddressingNotSupported)
|
||||
}
|
||||
ffi::ND_STATUS_VSIB_WITHOUT_SIB => Err(DecodeError::VsibWithoutSib),
|
||||
ffi::ND_STATUS_INVALID_VSIB_REGS => Err(DecodeError::InvalidVsibRegs),
|
||||
ffi::ND_STATUS_VEX_VVVV_MUST_BE_ZERO => Err(DecodeError::VexVvvvMustBeZero),
|
||||
ffi::ND_STATUS_MASK_NOT_SUPPORTED => Err(DecodeError::MaskNotSupported),
|
||||
ffi::ND_STATUS_MASK_REQUIRED => Err(DecodeError::MaskRequired),
|
||||
ffi::ND_STATUS_ER_SAE_NOT_SUPPORTED => Err(DecodeError::ErSaeNotSupported),
|
||||
ffi::ND_STATUS_ZEROING_NOT_SUPPORTED => Err(DecodeError::ZeroingNotSupported),
|
||||
ffi::ND_STATUS_ZEROING_ON_MEMORY => Err(DecodeError::ZeroingOnMemory),
|
||||
ffi::ND_STATUS_ZEROING_NO_MASK => Err(DecodeError::ZeroingNoMask),
|
||||
ffi::ND_STATUS_BROADCAST_NOT_SUPPORTED => Err(DecodeError::BroadcastNotSupported),
|
||||
ffi::ND_STATUS_BAD_EVEX_V_PRIME => Err(DecodeError::BadEvexVPrime),
|
||||
ffi::ND_STATUS_BAD_EVEX_LL => Err(DecodeError::BadEvexLl),
|
||||
ffi::ND_STATUS_SIBMEM_WITHOUT_SIB => Err(DecodeError::SibmemWithoutSib),
|
||||
ffi::ND_STATUS_INVALID_TILE_REGS => Err(DecodeError::InvalidTileRegs),
|
||||
ffi::ND_STATUS_INVALID_DEST_REGS => Err(DecodeError::InvalidDestRegs),
|
||||
ffi::ND_STATUS_INVALID_PARAMETER => Err(DecodeError::InvalidParameter),
|
||||
ffi::ND_STATUS_INVALID_INSTRUX => Err(DecodeError::InvalidInstrux),
|
||||
ffi::ND_STATUS_BUFFER_OVERFLOW => Err(DecodeError::BufferOverflow),
|
||||
ffi::ND_STATUS_INTERNAL_ERROR => Err(DecodeError::InternalError),
|
||||
_ => Err(DecodeError::UnknownStatus(status)),
|
||||
}
|
||||
}
|
||||
}
|
1538
rsbddisasm/bddisasm/src/decoded_instruction.rs
Normal file
1538
rsbddisasm/bddisasm/src/decoded_instruction.rs
Normal file
File diff suppressed because it is too large
Load Diff
215
rsbddisasm/bddisasm/src/decoder.rs
Normal file
215
rsbddisasm/bddisasm/src/decoder.rs
Normal file
@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Bitdefender
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
//! Decodes instructions.
|
||||
|
||||
extern crate bddisasm_sys as ffi;
|
||||
|
||||
pub use crate::decode_error::DecodeError;
|
||||
pub use crate::decoded_instruction::{DecodeMode, DecodeResult, DecodedInstruction};
|
||||
pub use crate::mnemonic::Mnemonic;
|
||||
|
||||
/// Decodes instructions.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct Decoder<'a> {
|
||||
code: &'a [u8],
|
||||
ip: u64,
|
||||
mode: DecodeMode,
|
||||
offset: usize,
|
||||
}
|
||||
|
||||
impl<'a> Decoder<'a> {
|
||||
/// Creates a new decoder.
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `code` - An [u8](u8) slice that holds the code to be decoded.
|
||||
/// * `mode` - The mode in which to decode the instruction.
|
||||
/// * `ip` - The instruction pointer value to use when formatting the decoded instruction. Does not affect the
|
||||
/// decoding process in any way.
|
||||
pub fn new(code: &'a [u8], mode: DecodeMode, ip: u64) -> Self {
|
||||
Self {
|
||||
code,
|
||||
mode,
|
||||
ip,
|
||||
offset: 0,
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to decode the next instruction from the given code chunk.
|
||||
///
|
||||
/// # Returns
|
||||
///
|
||||
/// * `Some(DecodeResult)` - if there are still undecoded bytes in the given code chunk. The decoding may have
|
||||
/// still failed. See `Remarks`.
|
||||
/// * `None` - if all the bytes in the given code chunk were decoded.
|
||||
///
|
||||
/// # Remarks
|
||||
///
|
||||
/// This function decodes one instruction from the given code chunk at a time. After each call, the offset inside
|
||||
/// the code chunk is advanced by:
|
||||
///
|
||||
/// - the size of the decoded instruction, if decoding was succesfull
|
||||
/// - 1, if decoding was not succesfull
|
||||
///
|
||||
/// The `ip` value specified when the decoder was created is automatically updated:
|
||||
///
|
||||
/// - if the decoding was succesfull, it is incremented with the instruction size
|
||||
/// - if the decoding was not succesfull, it is incremented with 1
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use std::error::Error;
|
||||
/// #
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// use bddisasm::decoder::{Decoder, DecodeMode};
|
||||
///
|
||||
/// let mut decoder = Decoder::new(&[0x51, 0x53], DecodeMode::Bits32, 0);
|
||||
///
|
||||
/// // As long as we have something to decode
|
||||
/// while let Some(result) = decoder.decode_next() {
|
||||
/// // Check if the decoding succeeded
|
||||
/// match result {
|
||||
/// Ok(instruction) => println!("{}", instruction),
|
||||
/// Err(e) => println!("Unable to decode: {}", e),
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn decode_next(&mut self) -> Option<DecodeResult> {
|
||||
if self.offset >= self.code.len() {
|
||||
None
|
||||
} else {
|
||||
let result =
|
||||
DecodedInstruction::decode_with_ip(&self.code[self.offset..], self.mode, self.ip);
|
||||
match result {
|
||||
Ok(ins) => {
|
||||
self.offset += ins.length() as usize;
|
||||
self.ip += ins.length() as u64;
|
||||
}
|
||||
Err(_) => {
|
||||
self.offset += 1;
|
||||
self.ip += 1;
|
||||
}
|
||||
};
|
||||
|
||||
Some(result)
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempts to decode the next instruction from the given code chunk.
|
||||
///
|
||||
/// Behaves like [decode_next](Decoder::decode_next), but in addition to the [DecodeResult](DecodeResult) it will
|
||||
/// also return the offset from which decoding was attempted, as well as the corresponding instruction pointer.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use std::error::Error;
|
||||
/// #
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// use bddisasm::decoder::{Decoder, DecodeMode};
|
||||
///
|
||||
/// let mut decoder = Decoder::new(&[0x51, 0x53], DecodeMode::Bits32, 0x1000);
|
||||
///
|
||||
/// // As long as we have something to decode
|
||||
/// while let Some((result, offset, ip)) = decoder.decode_next_with_info() {
|
||||
/// // Check if the decoding succeeded
|
||||
/// match result {
|
||||
/// Ok(instruction) => println!("IP: {:#x} {}", ip, instruction),
|
||||
/// Err(e) => println!("Unable to decode at offset {:#x}: {}", offset, e),
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
pub fn decode_next_with_info(&mut self) -> Option<(DecodeResult, usize, u64)> {
|
||||
let offset = self.offset;
|
||||
let ip = self.ip;
|
||||
|
||||
self.decode_next().map(|res| (res, offset, ip))
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for Decoder<'_> {
|
||||
type Item = DecodeResult;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.decode_next()
|
||||
}
|
||||
}
|
||||
|
||||
impl std::iter::FusedIterator for Decoder<'_> {}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn decode_next() {
|
||||
let code = vec![0xb8, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8b, 0xf9, 0xff, 0xff];
|
||||
let mut decoder = Decoder::new(&code, DecodeMode::Bits64, 0x1000);
|
||||
let expected: Vec<Result<(Mnemonic, &str, &[u8]), DecodeError>> = vec![
|
||||
Ok((
|
||||
Mnemonic::Mov,
|
||||
"MOV eax, 0x00000000",
|
||||
&[0xb8, 0x00, 0x00, 0x00, 0x00],
|
||||
)),
|
||||
Ok((Mnemonic::Mov, "MOV rdi, rcx", &[0x48, 0x8b, 0xf9])),
|
||||
Err(DecodeError::InvalidEncoding),
|
||||
Err(DecodeError::BufferTooSmall),
|
||||
];
|
||||
let mut exected_index = 0usize;
|
||||
while let Some(ins) = decoder.decode_next() {
|
||||
match expected[exected_index] {
|
||||
Ok((i, s, b)) => {
|
||||
let ins = ins.expect("Unable to decode");
|
||||
assert_eq!(i, ins.mnemonic());
|
||||
assert_eq!(b, ins.bytes());
|
||||
assert_eq!(s, format!("{}", ins));
|
||||
}
|
||||
Err(e) => {
|
||||
assert_eq!(e, ins.expect_err("Expected error"));
|
||||
}
|
||||
};
|
||||
|
||||
exected_index += 1;
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn decoder_iter() {
|
||||
let code = vec![0xb8, 0x00, 0x00, 0x00, 0x00, 0x48, 0x8b, 0xf9, 0xff, 0xff];
|
||||
let decoder = Decoder::new(&code, DecodeMode::Bits64, 0x1000);
|
||||
let expected: Vec<Result<(Mnemonic, &str, &[u8]), DecodeError>> = vec![
|
||||
Ok((
|
||||
Mnemonic::Mov,
|
||||
"MOV eax, 0x00000000",
|
||||
&[0xb8, 0x00, 0x00, 0x00, 0x00],
|
||||
)),
|
||||
Ok((Mnemonic::Mov, "MOV rdi, rcx", &[0x48, 0x8b, 0xf9])),
|
||||
Err(DecodeError::InvalidEncoding),
|
||||
Err(DecodeError::BufferTooSmall),
|
||||
];
|
||||
|
||||
for (index, ins) in decoder.enumerate() {
|
||||
match expected[index] {
|
||||
Ok((i, s, b)) => {
|
||||
let ins = ins.expect("Unable to decode");
|
||||
assert_eq!(i, ins.mnemonic());
|
||||
assert_eq!(b, ins.bytes());
|
||||
assert_eq!(s, format!("{}", ins));
|
||||
}
|
||||
Err(e) => {
|
||||
assert_eq!(e, ins.expect_err("Expected error"));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
58
rsbddisasm/bddisasm/src/fpu_flags.rs
Normal file
58
rsbddisasm/bddisasm/src/fpu_flags.rs
Normal file
@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Bitdefender
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
//! Offers information about how an instructions accesses the FPU status registers.
|
||||
|
||||
extern crate bddisasm_sys as ffi;
|
||||
|
||||
/// The mode in which a FPU status flag is accessed.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub enum FpuFlagsAccess {
|
||||
/// The FPU status flag is cleared to 0.
|
||||
Cleared,
|
||||
/// The FPU status flag is set to 1.
|
||||
Set,
|
||||
/// The FPU status flag is modified according to a result.
|
||||
Modified,
|
||||
/// The FPU status flag is undefined or unaffected.
|
||||
Undefined,
|
||||
}
|
||||
|
||||
impl From<u8> for FpuFlagsAccess {
|
||||
fn from(value: u8) -> FpuFlagsAccess {
|
||||
let value = value as u32;
|
||||
match value {
|
||||
ffi::ND_FPU_FLAG_SET_0 => FpuFlagsAccess::Cleared,
|
||||
ffi::ND_FPU_FLAG_SET_1 => FpuFlagsAccess::Set,
|
||||
ffi::ND_FPU_FLAG_MODIFIED => FpuFlagsAccess::Modified,
|
||||
ffi::ND_FPU_FLAG_UNDEFINED => FpuFlagsAccess::Undefined,
|
||||
_ => panic!("Unexpected FPU flag access: {}", value),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes the way an instruction accesses the FPU flags registers.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct FpuFlags {
|
||||
/// C0 flag access mode.
|
||||
pub c0: FpuFlagsAccess,
|
||||
/// C1 flag access mode.
|
||||
pub c1: FpuFlagsAccess,
|
||||
/// C2 flag access mode.
|
||||
pub c2: FpuFlagsAccess,
|
||||
/// C3 flag access mode.
|
||||
pub c3: FpuFlagsAccess,
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl From<ffi::ND_FPU_FLAGS> for FpuFlags {
|
||||
fn from(flags: ffi::ND_FPU_FLAGS) -> FpuFlags {
|
||||
FpuFlags {
|
||||
c0: FpuFlagsAccess::from(flags.C0()),
|
||||
c1: FpuFlagsAccess::from(flags.C1()),
|
||||
c2: FpuFlagsAccess::from(flags.C2()),
|
||||
c3: FpuFlagsAccess::from(flags.C3()),
|
||||
}
|
||||
}
|
||||
}
|
236
rsbddisasm/bddisasm/src/instruction_category.rs
Normal file
236
rsbddisasm/bddisasm/src/instruction_category.rs
Normal file
@ -0,0 +1,236 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Bitdefender
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
//! Instruction categories.
|
||||
|
||||
extern crate bddisasm_sys as ffi;
|
||||
|
||||
use super::decode_error;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
/// Instruction category.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub enum Category {
|
||||
I3dnow,
|
||||
Aes,
|
||||
Aeskl,
|
||||
Amx,
|
||||
Arith,
|
||||
Avx,
|
||||
Avx2,
|
||||
Avx2gather,
|
||||
Avx512,
|
||||
Avx512bf16,
|
||||
Avx512fp16,
|
||||
Avx512vbmi,
|
||||
Avx512vp2intersect,
|
||||
Avxvnni,
|
||||
Bitbyte,
|
||||
Blend,
|
||||
Bmi1,
|
||||
Bmi2,
|
||||
Broadcast,
|
||||
Call,
|
||||
Cet,
|
||||
Cldemote,
|
||||
Cmov,
|
||||
Compress,
|
||||
CondBr,
|
||||
Conflict,
|
||||
Convert,
|
||||
Dataxfer,
|
||||
Decimal,
|
||||
Enqcmd,
|
||||
Expand,
|
||||
Flagop,
|
||||
Fma4,
|
||||
Fred,
|
||||
Gather,
|
||||
Gfni,
|
||||
Hreset,
|
||||
I386,
|
||||
Ifma,
|
||||
Interrupt,
|
||||
Io,
|
||||
Iostringop,
|
||||
Kl,
|
||||
Kmask,
|
||||
Knl,
|
||||
Lkgs,
|
||||
Logic,
|
||||
Logical,
|
||||
LogicalFp,
|
||||
Lwp,
|
||||
Lzcnt,
|
||||
Misc,
|
||||
Mmx,
|
||||
Movdir64b,
|
||||
Movdiri,
|
||||
Mpx,
|
||||
Nop,
|
||||
Padlock,
|
||||
Pclmulqdq,
|
||||
Pconfig,
|
||||
Pop,
|
||||
Prefetch,
|
||||
Ptwrite,
|
||||
Push,
|
||||
Rdpid,
|
||||
Rdrand,
|
||||
Rdseed,
|
||||
Rdwrfsgs,
|
||||
Ret,
|
||||
Rotate,
|
||||
Scatter,
|
||||
Segop,
|
||||
Semaphore,
|
||||
Sgx,
|
||||
Sha,
|
||||
Shift,
|
||||
Smap,
|
||||
Sse,
|
||||
Sse2,
|
||||
Stringop,
|
||||
Sttni,
|
||||
Syscall,
|
||||
Sysret,
|
||||
System,
|
||||
Tdx,
|
||||
Ud,
|
||||
Uintr,
|
||||
UncondBr,
|
||||
Unknown,
|
||||
Vaes,
|
||||
Vfma,
|
||||
Vfmaps,
|
||||
Vnni,
|
||||
Vnniw,
|
||||
Vpclmulqdq,
|
||||
Vpopcnt,
|
||||
Vtx,
|
||||
Waitpkg,
|
||||
Wbnoinvd,
|
||||
Widenop,
|
||||
WideKl,
|
||||
X87Alu,
|
||||
Xop,
|
||||
Xsave,
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl TryFrom<ffi::ND_INS_CATEGORY> for Category {
|
||||
type Error = decode_error::DecodeError;
|
||||
|
||||
fn try_from(value: ffi::ND_INS_CATEGORY) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
ffi::_ND_INS_TYPE::ND_CAT_INVALID => {
|
||||
Err(decode_error::DecodeError::UnknownInstruction(value as u32))
|
||||
}
|
||||
ffi::_ND_INS_TYPE::ND_CAT_3DNOW => Ok(Category::I3dnow),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_AES => Ok(Category::Aes),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_AESKL => Ok(Category::Aeskl),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_AMX => Ok(Category::Amx),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_ARITH => Ok(Category::Arith),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_AVX => Ok(Category::Avx),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_AVX2 => Ok(Category::Avx2),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_AVX2GATHER => Ok(Category::Avx2gather),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_AVX512 => Ok(Category::Avx512),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_AVX512BF16 => Ok(Category::Avx512bf16),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_AVX512FP16 => Ok(Category::Avx512fp16),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_AVX512VBMI => Ok(Category::Avx512vbmi),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_AVX512VP2INTERSECT => Ok(Category::Avx512vp2intersect),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_AVXVNNI => Ok(Category::Avxvnni),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_BITBYTE => Ok(Category::Bitbyte),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_BLEND => Ok(Category::Blend),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_BMI1 => Ok(Category::Bmi1),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_BMI2 => Ok(Category::Bmi2),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_BROADCAST => Ok(Category::Broadcast),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_CALL => Ok(Category::Call),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_CET => Ok(Category::Cet),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_CLDEMOTE => Ok(Category::Cldemote),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_CMOV => Ok(Category::Cmov),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_COMPRESS => Ok(Category::Compress),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_COND_BR => Ok(Category::CondBr),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_CONFLICT => Ok(Category::Conflict),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_CONVERT => Ok(Category::Convert),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_DATAXFER => Ok(Category::Dataxfer),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_DECIMAL => Ok(Category::Decimal),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_ENQCMD => Ok(Category::Enqcmd),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_EXPAND => Ok(Category::Expand),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_FLAGOP => Ok(Category::Flagop),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_FMA4 => Ok(Category::Fma4),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_FRED => Ok(Category::Fred),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_GATHER => Ok(Category::Gather),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_GFNI => Ok(Category::Gfni),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_HRESET => Ok(Category::Hreset),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_I386 => Ok(Category::I386),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_IFMA => Ok(Category::Ifma),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_INTERRUPT => Ok(Category::Interrupt),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_IO => Ok(Category::Io),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_IOSTRINGOP => Ok(Category::Iostringop),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_KL => Ok(Category::Kl),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_KMASK => Ok(Category::Kmask),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_KNL => Ok(Category::Knl),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_LKGS => Ok(Category::Lkgs),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_LOGIC => Ok(Category::Logic),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_LOGICAL => Ok(Category::Logical),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_LOGICAL_FP => Ok(Category::LogicalFp),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_LWP => Ok(Category::Lwp),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_LZCNT => Ok(Category::Lzcnt),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_MISC => Ok(Category::Misc),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_MMX => Ok(Category::Mmx),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_MOVDIR64B => Ok(Category::Movdir64b),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_MOVDIRI => Ok(Category::Movdiri),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_MPX => Ok(Category::Mpx),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_NOP => Ok(Category::Nop),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_PADLOCK => Ok(Category::Padlock),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_PCLMULQDQ => Ok(Category::Pclmulqdq),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_PCONFIG => Ok(Category::Pconfig),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_POP => Ok(Category::Pop),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_PREFETCH => Ok(Category::Prefetch),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_PTWRITE => Ok(Category::Ptwrite),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_PUSH => Ok(Category::Push),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_RDPID => Ok(Category::Rdpid),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_RDRAND => Ok(Category::Rdrand),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_RDSEED => Ok(Category::Rdseed),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_RDWRFSGS => Ok(Category::Rdwrfsgs),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_RET => Ok(Category::Ret),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_ROTATE => Ok(Category::Rotate),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_SCATTER => Ok(Category::Scatter),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_SEGOP => Ok(Category::Segop),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_SEMAPHORE => Ok(Category::Semaphore),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_SGX => Ok(Category::Sgx),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_SHA => Ok(Category::Sha),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_SHIFT => Ok(Category::Shift),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_SMAP => Ok(Category::Smap),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_SSE => Ok(Category::Sse),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_SSE2 => Ok(Category::Sse2),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_STRINGOP => Ok(Category::Stringop),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_STTNI => Ok(Category::Sttni),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_SYSCALL => Ok(Category::Syscall),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_SYSRET => Ok(Category::Sysret),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_SYSTEM => Ok(Category::System),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_TDX => Ok(Category::Tdx),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_UD => Ok(Category::Ud),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_UINTR => Ok(Category::Uintr),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_UNCOND_BR => Ok(Category::UncondBr),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_UNKNOWN => Ok(Category::Unknown),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_VAES => Ok(Category::Vaes),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_VFMA => Ok(Category::Vfma),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_VFMAPS => Ok(Category::Vfmaps),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_VNNI => Ok(Category::Vnni),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_VNNIW => Ok(Category::Vnniw),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_VPCLMULQDQ => Ok(Category::Vpclmulqdq),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_VPOPCNT => Ok(Category::Vpopcnt),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_VTX => Ok(Category::Vtx),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_WAITPKG => Ok(Category::Waitpkg),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_WBNOINVD => Ok(Category::Wbnoinvd),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_WIDENOP => Ok(Category::Widenop),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_WIDE_KL => Ok(Category::WideKl),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_X87_ALU => Ok(Category::X87Alu),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_XOP => Ok(Category::Xop),
|
||||
ffi::_ND_INS_TYPE::ND_CAT_XSAVE => Ok(Category::Xsave),
|
||||
}
|
||||
}
|
||||
}
|
262
rsbddisasm/bddisasm/src/isa_set.rs
Normal file
262
rsbddisasm/bddisasm/src/isa_set.rs
Normal file
@ -0,0 +1,262 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Bitdefender
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
//! Instruction sets.
|
||||
|
||||
extern crate bddisasm_sys as ffi;
|
||||
|
||||
use super::decode_error;
|
||||
use std::convert::TryFrom;
|
||||
|
||||
/// ISA set.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub enum IsaSet {
|
||||
I3dnow,
|
||||
Adx,
|
||||
Aes,
|
||||
Amd,
|
||||
Amxbf16,
|
||||
Amxint8,
|
||||
Amxtile,
|
||||
Avx,
|
||||
Avx2,
|
||||
Avx2gather,
|
||||
Avx5124fmaps,
|
||||
Avx5124vnniw,
|
||||
Avx512bf16,
|
||||
Avx512bitalg,
|
||||
Avx512bw,
|
||||
Avx512cd,
|
||||
Avx512dq,
|
||||
Avx512er,
|
||||
Avx512f,
|
||||
Avx512fp16,
|
||||
Avx512ifma,
|
||||
Avx512pf,
|
||||
Avx512vbmi,
|
||||
Avx512vbmi2,
|
||||
Avx512vnni,
|
||||
Avx512vp2intersect,
|
||||
Avx512vpopcntdq,
|
||||
Avxvnni,
|
||||
Bmi1,
|
||||
Bmi2,
|
||||
CetIbt,
|
||||
CetSs,
|
||||
Cldemote,
|
||||
Clfsh,
|
||||
Clfshopt,
|
||||
Clwb,
|
||||
Clzero,
|
||||
Cmpxchg16b,
|
||||
Cyrix,
|
||||
CyrixSmm,
|
||||
Enqcmd,
|
||||
F16c,
|
||||
Fma,
|
||||
Fma4,
|
||||
Fred,
|
||||
Fxsave,
|
||||
Gfni,
|
||||
Hreset,
|
||||
I186,
|
||||
I286prot,
|
||||
I286real,
|
||||
I386,
|
||||
I486,
|
||||
I486real,
|
||||
I64,
|
||||
I86,
|
||||
Invlpgb,
|
||||
Invpcid,
|
||||
Kl,
|
||||
Lkgs,
|
||||
Longmode,
|
||||
Lwp,
|
||||
Lzcnt,
|
||||
Mcommit,
|
||||
Mmx,
|
||||
Movbe,
|
||||
Movdir64b,
|
||||
Movdiri,
|
||||
Mpx,
|
||||
Mwaitt,
|
||||
Pause,
|
||||
Pclmulqdq,
|
||||
Pconfig,
|
||||
Pentiumreal,
|
||||
Pku,
|
||||
Popcnt,
|
||||
Ppro,
|
||||
PrefetchNop,
|
||||
Ptwrite,
|
||||
Rdpid,
|
||||
Rdpmc,
|
||||
Rdpru,
|
||||
Rdrand,
|
||||
Rdseed,
|
||||
Rdtscp,
|
||||
Rdwrfsgs,
|
||||
Serialize,
|
||||
Sgx,
|
||||
Sha,
|
||||
Smap,
|
||||
Smx,
|
||||
Snp,
|
||||
Sse,
|
||||
Sse2,
|
||||
Sse3,
|
||||
Sse4,
|
||||
Sse42,
|
||||
Sse4a,
|
||||
Ssse3,
|
||||
Svm,
|
||||
Tbm,
|
||||
Tdx,
|
||||
Tsx,
|
||||
Tsxldtrk,
|
||||
Ud,
|
||||
Uintr,
|
||||
Unknown,
|
||||
Vaes,
|
||||
Vpclmulqdq,
|
||||
Vtx,
|
||||
Waitpkg,
|
||||
Wbnoinvd,
|
||||
X87,
|
||||
Xop,
|
||||
Xsave,
|
||||
Xsavec,
|
||||
Xsaves,
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl TryFrom<ffi::ND_INS_SET> for IsaSet {
|
||||
type Error = decode_error::DecodeError;
|
||||
|
||||
fn try_from(value: ffi::ND_INS_SET) -> Result<Self, Self::Error> {
|
||||
match value {
|
||||
ffi::_ND_INS_SET::ND_SET_INVALID => {
|
||||
Err(decode_error::DecodeError::UnknownInstruction(value as u32))
|
||||
}
|
||||
ffi::_ND_INS_SET::ND_SET_3DNOW => Ok(IsaSet::I3dnow),
|
||||
ffi::_ND_INS_SET::ND_SET_ADX => Ok(IsaSet::Adx),
|
||||
ffi::_ND_INS_SET::ND_SET_AES => Ok(IsaSet::Aes),
|
||||
ffi::_ND_INS_SET::ND_SET_AMD => Ok(IsaSet::Amd),
|
||||
ffi::_ND_INS_SET::ND_SET_AMXBF16 => Ok(IsaSet::Amxbf16),
|
||||
ffi::_ND_INS_SET::ND_SET_AMXINT8 => Ok(IsaSet::Amxint8),
|
||||
ffi::_ND_INS_SET::ND_SET_AMXTILE => Ok(IsaSet::Amxtile),
|
||||
ffi::_ND_INS_SET::ND_SET_AVX => Ok(IsaSet::Avx),
|
||||
ffi::_ND_INS_SET::ND_SET_AVX2 => Ok(IsaSet::Avx2),
|
||||
ffi::_ND_INS_SET::ND_SET_AVX2GATHER => Ok(IsaSet::Avx2gather),
|
||||
ffi::_ND_INS_SET::ND_SET_AVX5124FMAPS => Ok(IsaSet::Avx5124fmaps),
|
||||
ffi::_ND_INS_SET::ND_SET_AVX5124VNNIW => Ok(IsaSet::Avx5124vnniw),
|
||||
ffi::_ND_INS_SET::ND_SET_AVX512BF16 => Ok(IsaSet::Avx512bf16),
|
||||
ffi::_ND_INS_SET::ND_SET_AVX512BITALG => Ok(IsaSet::Avx512bitalg),
|
||||
ffi::_ND_INS_SET::ND_SET_AVX512BW => Ok(IsaSet::Avx512bw),
|
||||
ffi::_ND_INS_SET::ND_SET_AVX512CD => Ok(IsaSet::Avx512cd),
|
||||
ffi::_ND_INS_SET::ND_SET_AVX512DQ => Ok(IsaSet::Avx512dq),
|
||||
ffi::_ND_INS_SET::ND_SET_AVX512ER => Ok(IsaSet::Avx512er),
|
||||
ffi::_ND_INS_SET::ND_SET_AVX512F => Ok(IsaSet::Avx512f),
|
||||
ffi::_ND_INS_SET::ND_SET_AVX512FP16 => Ok(IsaSet::Avx512fp16),
|
||||
ffi::_ND_INS_SET::ND_SET_AVX512IFMA => Ok(IsaSet::Avx512ifma),
|
||||
ffi::_ND_INS_SET::ND_SET_AVX512PF => Ok(IsaSet::Avx512pf),
|
||||
ffi::_ND_INS_SET::ND_SET_AVX512VBMI => Ok(IsaSet::Avx512vbmi),
|
||||
ffi::_ND_INS_SET::ND_SET_AVX512VBMI2 => Ok(IsaSet::Avx512vbmi2),
|
||||
ffi::_ND_INS_SET::ND_SET_AVX512VNNI => Ok(IsaSet::Avx512vnni),
|
||||
ffi::_ND_INS_SET::ND_SET_AVX512VP2INTERSECT => Ok(IsaSet::Avx512vp2intersect),
|
||||
ffi::_ND_INS_SET::ND_SET_AVX512VPOPCNTDQ => Ok(IsaSet::Avx512vpopcntdq),
|
||||
ffi::_ND_INS_SET::ND_SET_AVXVNNI => Ok(IsaSet::Avxvnni),
|
||||
ffi::_ND_INS_SET::ND_SET_BMI1 => Ok(IsaSet::Bmi1),
|
||||
ffi::_ND_INS_SET::ND_SET_BMI2 => Ok(IsaSet::Bmi2),
|
||||
ffi::_ND_INS_SET::ND_SET_CET_IBT => Ok(IsaSet::CetIbt),
|
||||
ffi::_ND_INS_SET::ND_SET_CET_SS => Ok(IsaSet::CetSs),
|
||||
ffi::_ND_INS_SET::ND_SET_CLDEMOTE => Ok(IsaSet::Cldemote),
|
||||
ffi::_ND_INS_SET::ND_SET_CLFSH => Ok(IsaSet::Clfsh),
|
||||
ffi::_ND_INS_SET::ND_SET_CLFSHOPT => Ok(IsaSet::Clfshopt),
|
||||
ffi::_ND_INS_SET::ND_SET_CLWB => Ok(IsaSet::Clwb),
|
||||
ffi::_ND_INS_SET::ND_SET_CLZERO => Ok(IsaSet::Clzero),
|
||||
ffi::_ND_INS_SET::ND_SET_CMPXCHG16B => Ok(IsaSet::Cmpxchg16b),
|
||||
ffi::_ND_INS_SET::ND_SET_CYRIX => Ok(IsaSet::Cyrix),
|
||||
ffi::_ND_INS_SET::ND_SET_CYRIX_SMM => Ok(IsaSet::CyrixSmm),
|
||||
ffi::_ND_INS_SET::ND_SET_ENQCMD => Ok(IsaSet::Enqcmd),
|
||||
ffi::_ND_INS_SET::ND_SET_F16C => Ok(IsaSet::F16c),
|
||||
ffi::_ND_INS_SET::ND_SET_FMA => Ok(IsaSet::Fma),
|
||||
ffi::_ND_INS_SET::ND_SET_FMA4 => Ok(IsaSet::Fma4),
|
||||
ffi::_ND_INS_SET::ND_SET_FRED => Ok(IsaSet::Fred),
|
||||
ffi::_ND_INS_SET::ND_SET_FXSAVE => Ok(IsaSet::Fxsave),
|
||||
ffi::_ND_INS_SET::ND_SET_GFNI => Ok(IsaSet::Gfni),
|
||||
ffi::_ND_INS_SET::ND_SET_HRESET => Ok(IsaSet::Hreset),
|
||||
ffi::_ND_INS_SET::ND_SET_I186 => Ok(IsaSet::I186),
|
||||
ffi::_ND_INS_SET::ND_SET_I286PROT => Ok(IsaSet::I286prot),
|
||||
ffi::_ND_INS_SET::ND_SET_I286REAL => Ok(IsaSet::I286real),
|
||||
ffi::_ND_INS_SET::ND_SET_I386 => Ok(IsaSet::I386),
|
||||
ffi::_ND_INS_SET::ND_SET_I486 => Ok(IsaSet::I486),
|
||||
ffi::_ND_INS_SET::ND_SET_I486REAL => Ok(IsaSet::I486real),
|
||||
ffi::_ND_INS_SET::ND_SET_I64 => Ok(IsaSet::I64),
|
||||
ffi::_ND_INS_SET::ND_SET_I86 => Ok(IsaSet::I86),
|
||||
ffi::_ND_INS_SET::ND_SET_INVLPGB => Ok(IsaSet::Invlpgb),
|
||||
ffi::_ND_INS_SET::ND_SET_INVPCID => Ok(IsaSet::Invpcid),
|
||||
ffi::_ND_INS_SET::ND_SET_KL => Ok(IsaSet::Kl),
|
||||
ffi::_ND_INS_SET::ND_SET_LKGS => Ok(IsaSet::Lkgs),
|
||||
ffi::_ND_INS_SET::ND_SET_LONGMODE => Ok(IsaSet::Longmode),
|
||||
ffi::_ND_INS_SET::ND_SET_LWP => Ok(IsaSet::Lwp),
|
||||
ffi::_ND_INS_SET::ND_SET_LZCNT => Ok(IsaSet::Lzcnt),
|
||||
ffi::_ND_INS_SET::ND_SET_MCOMMIT => Ok(IsaSet::Mcommit),
|
||||
ffi::_ND_INS_SET::ND_SET_MMX => Ok(IsaSet::Mmx),
|
||||
ffi::_ND_INS_SET::ND_SET_MOVBE => Ok(IsaSet::Movbe),
|
||||
ffi::_ND_INS_SET::ND_SET_MOVDIR64B => Ok(IsaSet::Movdir64b),
|
||||
ffi::_ND_INS_SET::ND_SET_MOVDIRI => Ok(IsaSet::Movdiri),
|
||||
ffi::_ND_INS_SET::ND_SET_MPX => Ok(IsaSet::Mpx),
|
||||
ffi::_ND_INS_SET::ND_SET_MWAITT => Ok(IsaSet::Mwaitt),
|
||||
ffi::_ND_INS_SET::ND_SET_PAUSE => Ok(IsaSet::Pause),
|
||||
ffi::_ND_INS_SET::ND_SET_PCLMULQDQ => Ok(IsaSet::Pclmulqdq),
|
||||
ffi::_ND_INS_SET::ND_SET_PCONFIG => Ok(IsaSet::Pconfig),
|
||||
ffi::_ND_INS_SET::ND_SET_PENTIUMREAL => Ok(IsaSet::Pentiumreal),
|
||||
ffi::_ND_INS_SET::ND_SET_PKU => Ok(IsaSet::Pku),
|
||||
ffi::_ND_INS_SET::ND_SET_POPCNT => Ok(IsaSet::Popcnt),
|
||||
ffi::_ND_INS_SET::ND_SET_PPRO => Ok(IsaSet::Ppro),
|
||||
ffi::_ND_INS_SET::ND_SET_PREFETCH_NOP => Ok(IsaSet::PrefetchNop),
|
||||
ffi::_ND_INS_SET::ND_SET_PTWRITE => Ok(IsaSet::Ptwrite),
|
||||
ffi::_ND_INS_SET::ND_SET_RDPID => Ok(IsaSet::Rdpid),
|
||||
ffi::_ND_INS_SET::ND_SET_RDPMC => Ok(IsaSet::Rdpmc),
|
||||
ffi::_ND_INS_SET::ND_SET_RDPRU => Ok(IsaSet::Rdpru),
|
||||
ffi::_ND_INS_SET::ND_SET_RDRAND => Ok(IsaSet::Rdrand),
|
||||
ffi::_ND_INS_SET::ND_SET_RDSEED => Ok(IsaSet::Rdseed),
|
||||
ffi::_ND_INS_SET::ND_SET_RDTSCP => Ok(IsaSet::Rdtscp),
|
||||
ffi::_ND_INS_SET::ND_SET_RDWRFSGS => Ok(IsaSet::Rdwrfsgs),
|
||||
ffi::_ND_INS_SET::ND_SET_SERIALIZE => Ok(IsaSet::Serialize),
|
||||
ffi::_ND_INS_SET::ND_SET_SGX => Ok(IsaSet::Sgx),
|
||||
ffi::_ND_INS_SET::ND_SET_SHA => Ok(IsaSet::Sha),
|
||||
ffi::_ND_INS_SET::ND_SET_SMAP => Ok(IsaSet::Smap),
|
||||
ffi::_ND_INS_SET::ND_SET_SMX => Ok(IsaSet::Smx),
|
||||
ffi::_ND_INS_SET::ND_SET_SNP => Ok(IsaSet::Snp),
|
||||
ffi::_ND_INS_SET::ND_SET_SSE => Ok(IsaSet::Sse),
|
||||
ffi::_ND_INS_SET::ND_SET_SSE2 => Ok(IsaSet::Sse2),
|
||||
ffi::_ND_INS_SET::ND_SET_SSE3 => Ok(IsaSet::Sse3),
|
||||
ffi::_ND_INS_SET::ND_SET_SSE4 => Ok(IsaSet::Sse4),
|
||||
ffi::_ND_INS_SET::ND_SET_SSE42 => Ok(IsaSet::Sse42),
|
||||
ffi::_ND_INS_SET::ND_SET_SSE4A => Ok(IsaSet::Sse4a),
|
||||
ffi::_ND_INS_SET::ND_SET_SSSE3 => Ok(IsaSet::Ssse3),
|
||||
ffi::_ND_INS_SET::ND_SET_SVM => Ok(IsaSet::Svm),
|
||||
ffi::_ND_INS_SET::ND_SET_TBM => Ok(IsaSet::Tbm),
|
||||
ffi::_ND_INS_SET::ND_SET_TDX => Ok(IsaSet::Tdx),
|
||||
ffi::_ND_INS_SET::ND_SET_TSX => Ok(IsaSet::Tsx),
|
||||
ffi::_ND_INS_SET::ND_SET_TSXLDTRK => Ok(IsaSet::Tsxldtrk),
|
||||
ffi::_ND_INS_SET::ND_SET_UD => Ok(IsaSet::Ud),
|
||||
ffi::_ND_INS_SET::ND_SET_UINTR => Ok(IsaSet::Uintr),
|
||||
ffi::_ND_INS_SET::ND_SET_UNKNOWN => Ok(IsaSet::Unknown),
|
||||
ffi::_ND_INS_SET::ND_SET_VAES => Ok(IsaSet::Vaes),
|
||||
ffi::_ND_INS_SET::ND_SET_VPCLMULQDQ => Ok(IsaSet::Vpclmulqdq),
|
||||
ffi::_ND_INS_SET::ND_SET_VTX => Ok(IsaSet::Vtx),
|
||||
ffi::_ND_INS_SET::ND_SET_WAITPKG => Ok(IsaSet::Waitpkg),
|
||||
ffi::_ND_INS_SET::ND_SET_WBNOINVD => Ok(IsaSet::Wbnoinvd),
|
||||
ffi::_ND_INS_SET::ND_SET_X87 => Ok(IsaSet::X87),
|
||||
ffi::_ND_INS_SET::ND_SET_XOP => Ok(IsaSet::Xop),
|
||||
ffi::_ND_INS_SET::ND_SET_XSAVE => Ok(IsaSet::Xsave),
|
||||
ffi::_ND_INS_SET::ND_SET_XSAVEC => Ok(IsaSet::Xsavec),
|
||||
ffi::_ND_INS_SET::ND_SET_XSAVES => Ok(IsaSet::Xsaves),
|
||||
}
|
||||
}
|
||||
}
|
189
rsbddisasm/bddisasm/src/lib.rs
Normal file
189
rsbddisasm/bddisasm/src/lib.rs
Normal file
@ -0,0 +1,189 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Bitdefender
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
//! bddisasm x86/x64 instruction decoder
|
||||
//!
|
||||
//! This crate contains bindings for the [bddisasm](https://github.com/bitdefender/bddisasm) x86/x64 decoder library.
|
||||
//! It supports all existing x86 instruction, offering a wide range of information about each one, including:
|
||||
//!
|
||||
//! - operands (implicit and explicit)
|
||||
//! - access mode for each operand
|
||||
//! - CPUID feature flags
|
||||
//! - CPU modes in which an instruction is valid
|
||||
//!
|
||||
//! # Usage
|
||||
//!
|
||||
//! Add `bddisasm` to your `Cargo.toml`:
|
||||
//!
|
||||
//! ```toml
|
||||
//! [dependencies]
|
||||
//! bddisasm = "0.1.0"
|
||||
//! ```
|
||||
//!
|
||||
//! # Examples
|
||||
//!
|
||||
//! ## Decoding one instruction
|
||||
//!
|
||||
//! Use [DecodedInstruction::decode](crate::decoded_instruction::DecodedInstruction::decode) to decode an instruction
|
||||
//! from a chunk of code.
|
||||
//!
|
||||
//! ```
|
||||
//! use bddisasm::decoded_instruction::{DecodedInstruction, DecodeMode, Mnemonic};
|
||||
//!
|
||||
//! let code = vec![0x31, 0xc0];
|
||||
//! match DecodedInstruction::decode(&code, DecodeMode::Bits32) {
|
||||
//! Ok(ins) => {
|
||||
//! assert_eq!(ins.mnemonic(), Mnemonic::Xor);
|
||||
//! println!("{}", ins);
|
||||
//! },
|
||||
//! Err(err) => println!("Unable to decode: {}", err),
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Decoding multiple instructions
|
||||
//!
|
||||
//! Use [Decoder](crate::decoder::Decoder) to decode multiple instructions from a chunk of code.
|
||||
//!
|
||||
//! ```
|
||||
//! use bddisasm::decoder::{Decoder, DecodeMode};
|
||||
//!
|
||||
//! let code = [
|
||||
//! // ENCLS
|
||||
//! 0x0f, 0x01, 0xcf,
|
||||
//! // MOV rax, qword ptr [rbx+rcx*4+0x1234]
|
||||
//! 0x48, 0x8b, 0x84, 0x8b, 0x34, 0x12, 0x00, 0x00,
|
||||
//! // Not a valid instruction
|
||||
//! 0x0f,
|
||||
//! // WRMSR
|
||||
//! 0x0f, 0x30,
|
||||
//! ];
|
||||
//! let decoder = Decoder::new(&code, DecodeMode::Bits64, 0x1234);
|
||||
//!
|
||||
//! for ins in decoder {
|
||||
//! match ins {
|
||||
//! Ok(ins) => println!("{}", ins),
|
||||
//! Err(e) => println!("{}", e),
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! This will print:
|
||||
//!
|
||||
//! ```text
|
||||
//! ENCLS
|
||||
//! MOV rax, qword ptr [rbx+rcx*4+0x1234]
|
||||
//! the provided input buffer is too small
|
||||
//! WRMSR
|
||||
//! ```
|
||||
//!
|
||||
//! Use [Decoder::decode_next_with_info](crate::decoder::Decoder::decode_next_with_info) to get information about the
|
||||
//! offset inside the code chunk at which an instruction was decoded from.
|
||||
//!
|
||||
//! ```
|
||||
//! use bddisasm::decoder::{Decoder, DecodeMode};
|
||||
//!
|
||||
//! let code = [
|
||||
//! // ENCLS
|
||||
//! 0x0f, 0x01, 0xcf,
|
||||
//! // MOV rax, qword ptr [rbx+rcx*4+0x1234]
|
||||
//! 0x48, 0x8b, 0x84, 0x8b, 0x34, 0x12, 0x00, 0x00,
|
||||
//! // Not a valid instruction
|
||||
//! 0x0f,
|
||||
//! // WRMSR
|
||||
//! 0x0f, 0x30,
|
||||
//! ];
|
||||
//! let mut decoder = Decoder::new(&code, DecodeMode::Bits64, 0x1234);
|
||||
//!
|
||||
//!
|
||||
//! // Keep decoding until there's nothing left to decode
|
||||
//! while let Some((result, offset, _)) = decoder.decode_next_with_info() {
|
||||
//! match result {
|
||||
//! Ok(ins) => println!("{:#x} {}", offset, ins),
|
||||
//! Err(e) => println!("Error: `{}` at offset {:#x}", e, offset),
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! This will print:
|
||||
//!
|
||||
//! ```text
|
||||
//! 0x0 ENCLS
|
||||
//! 0x3 MOV rax, qword ptr [rbx+rcx*4+0x1234]
|
||||
//! Error `the provided input buffer is too small` at offset 0xb
|
||||
//! 0xc WRMSR
|
||||
//! ```
|
||||
//!
|
||||
//! ## Working with instruction operands
|
||||
//!
|
||||
//! Instruction operands can be analyzed using the [operand](crate::operand) module. Rich informaion is offered for
|
||||
//! each type of operand. Bellow is a minimal example that looks at a memory operand.
|
||||
//!
|
||||
//! ```
|
||||
//! # use bddisasm::decode_error::DecodeError;
|
||||
//! # fn test() -> Result<(), DecodeError> {
|
||||
//! use bddisasm::decoded_instruction::{DecodedInstruction, DecodeMode};
|
||||
//! use bddisasm::operand::OpInfo;
|
||||
//!
|
||||
//! // ` MOV rax, qword ptr [rcx+r15*2]`
|
||||
//! let code = b"\x4a\x8b\x04\x79";
|
||||
//! let ins = DecodedInstruction::decode(code, DecodeMode::Bits64).unwrap();
|
||||
//! // Get the operands
|
||||
//! let operands = ins.operands();
|
||||
//! // Get the second operand which is the source (`[rcx+r15*2]`)
|
||||
//! let src = operands[1];
|
||||
//!
|
||||
//! println!("Source operand type: {}", src.info);
|
||||
//! match src.info {
|
||||
//! OpInfo::Mem(mem) => {
|
||||
//! if let Some(base) = mem.base {
|
||||
//! println!("Base register: {}", base);
|
||||
//! } else {
|
||||
//! println!("No base register");
|
||||
//! }
|
||||
//!
|
||||
//! if let Some(index) = mem.index {
|
||||
//! println!("Index register: {}", index);
|
||||
//! } else {
|
||||
//! println!("No index register");
|
||||
//! }
|
||||
//!
|
||||
//! if let Some(scale) = mem.scale {
|
||||
//! println!("Scale: {}", scale);
|
||||
//! } else {
|
||||
//! println!("No scale");
|
||||
//! }
|
||||
//!
|
||||
//! if let Some(displacement) = mem.disp {
|
||||
//! println!("Displacement: {}", displacement);
|
||||
//! } else {
|
||||
//! println!("No displacement");
|
||||
//! }
|
||||
//! },
|
||||
//! _ => unreachable!(),
|
||||
//! }
|
||||
//! # Ok(())
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! Will print:
|
||||
//!
|
||||
//! ```text
|
||||
//! Source operand type: memory
|
||||
//! Base register: 1
|
||||
//! Index register: 15
|
||||
//! Scale: 2
|
||||
//! No displacement
|
||||
//! ```
|
||||
pub mod cpu_modes;
|
||||
pub mod cpuid;
|
||||
pub mod decode_error;
|
||||
pub mod decoded_instruction;
|
||||
pub mod decoder;
|
||||
pub mod fpu_flags;
|
||||
pub mod instruction_category;
|
||||
pub mod isa_set;
|
||||
pub mod mnemonic;
|
||||
pub mod operand;
|
||||
pub mod rflags;
|
||||
pub mod tuple;
|
3198
rsbddisasm/bddisasm/src/mnemonic.rs
Normal file
3198
rsbddisasm/bddisasm/src/mnemonic.rs
Normal file
File diff suppressed because it is too large
Load Diff
767
rsbddisasm/bddisasm/src/operand.rs
Normal file
767
rsbddisasm/bddisasm/src/operand.rs
Normal file
@ -0,0 +1,767 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Bitdefender
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
//! Operand types and details.
|
||||
|
||||
extern crate bddisasm_sys as ffi;
|
||||
|
||||
use std::fmt;
|
||||
|
||||
/// Describes an address operand.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct OpAddr {
|
||||
/// The ID of the base segment selector.
|
||||
///
|
||||
/// `ES` = 0, `CS` = 1, `SS` = 2, `DS` = 3, `FS` = 4, `GS` = 5.
|
||||
pub base_seg: u16,
|
||||
|
||||
/// Offset inside the segment.
|
||||
pub offset: u64,
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl From<ffi::ND_OPDESC_ADDRESS> for OpAddr {
|
||||
fn from(op: ffi::ND_OPDESC_ADDRESS) -> OpAddr {
|
||||
OpAddr {
|
||||
base_seg: op.BaseSeg,
|
||||
offset: op.Offset,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl OpAddr {
|
||||
pub(crate) fn new(base_seg: u16, offset: u64) -> Self {
|
||||
Self { base_seg, offset }
|
||||
}
|
||||
}
|
||||
|
||||
/// The type of a register.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub enum OpRegType {
|
||||
/// The register is a 8/16/32/64 bit general purpose register.
|
||||
Gpr,
|
||||
|
||||
/// The register is a segment register.
|
||||
Seg,
|
||||
|
||||
/// The register is a 80-bit FPU register.
|
||||
Fpu,
|
||||
|
||||
/// The register is a 64-bit MMX register.
|
||||
Mmx,
|
||||
|
||||
/// The register is a 128/256/512 bit SSE vector register.
|
||||
Sse,
|
||||
|
||||
/// The register is a control register.
|
||||
Cr,
|
||||
|
||||
/// The register is a debug register.
|
||||
Dr,
|
||||
|
||||
/// The register is a test register.
|
||||
Tr,
|
||||
|
||||
/// The register is a bound register.
|
||||
Bnd,
|
||||
|
||||
/// The register is a mask register.
|
||||
Msk,
|
||||
|
||||
/// The register is a tile register.
|
||||
Tile,
|
||||
|
||||
/// The register is a model specific register.
|
||||
Msr,
|
||||
|
||||
/// The register is a extended control register.
|
||||
Xcr,
|
||||
|
||||
/// The register is a system register.
|
||||
Sys,
|
||||
|
||||
/// The register is a x87 status/control register.
|
||||
X87,
|
||||
|
||||
/// The register is the MXCSR register.
|
||||
Mxcsr,
|
||||
|
||||
/// The register is the PKRU register.
|
||||
Pkru,
|
||||
|
||||
/// The register is the SSP (Shadow Stack Pointer) register.
|
||||
Ssp,
|
||||
|
||||
/// The register is the FLAGS register.
|
||||
Flg,
|
||||
|
||||
/// The register is the instruction pointer register.
|
||||
Rip,
|
||||
|
||||
/// The register is the User Interrupt Flag.
|
||||
Uif,
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl From<ffi::ND_OPDESC_REGISTER> for OpRegType {
|
||||
fn from(op: ffi::ND_OPDESC_REGISTER) -> OpRegType {
|
||||
match op.Type {
|
||||
ffi::_ND_REG_TYPE::ND_REG_NOT_PRESENT => panic!("Unexpected ND_REG_NOT_PRESENT"),
|
||||
ffi::_ND_REG_TYPE::ND_REG_GPR => OpRegType::Gpr,
|
||||
ffi::_ND_REG_TYPE::ND_REG_SEG => OpRegType::Seg,
|
||||
ffi::_ND_REG_TYPE::ND_REG_FPU => OpRegType::Fpu,
|
||||
ffi::_ND_REG_TYPE::ND_REG_MMX => OpRegType::Mmx,
|
||||
ffi::_ND_REG_TYPE::ND_REG_SSE => OpRegType::Sse,
|
||||
ffi::_ND_REG_TYPE::ND_REG_CR => OpRegType::Cr,
|
||||
ffi::_ND_REG_TYPE::ND_REG_DR => OpRegType::Dr,
|
||||
ffi::_ND_REG_TYPE::ND_REG_TR => OpRegType::Tr,
|
||||
ffi::_ND_REG_TYPE::ND_REG_BND => OpRegType::Bnd,
|
||||
ffi::_ND_REG_TYPE::ND_REG_MSK => OpRegType::Msk,
|
||||
ffi::_ND_REG_TYPE::ND_REG_TILE => OpRegType::Tile,
|
||||
ffi::_ND_REG_TYPE::ND_REG_MSR => OpRegType::Msr,
|
||||
ffi::_ND_REG_TYPE::ND_REG_XCR => OpRegType::Xcr,
|
||||
ffi::_ND_REG_TYPE::ND_REG_SYS => OpRegType::Sys,
|
||||
ffi::_ND_REG_TYPE::ND_REG_X87 => OpRegType::X87,
|
||||
ffi::_ND_REG_TYPE::ND_REG_MXCSR => OpRegType::Mxcsr,
|
||||
ffi::_ND_REG_TYPE::ND_REG_PKRU => OpRegType::Pkru,
|
||||
ffi::_ND_REG_TYPE::ND_REG_SSP => OpRegType::Ssp,
|
||||
ffi::_ND_REG_TYPE::ND_REG_FLG => OpRegType::Flg,
|
||||
ffi::_ND_REG_TYPE::ND_REG_RIP => OpRegType::Rip,
|
||||
ffi::_ND_REG_TYPE::ND_REG_UIF => OpRegType::Uif,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for OpRegType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::Gpr => write!(f, "GPR"),
|
||||
Self::Seg => write!(f, "Segment"),
|
||||
Self::Fpu => write!(f, "FPU"),
|
||||
Self::Mmx => write!(f, "MMX"),
|
||||
Self::Sse => write!(f, "SSE"),
|
||||
Self::Cr => write!(f, "CR"),
|
||||
Self::Dr => write!(f, "DR"),
|
||||
Self::Tr => write!(f, "TR"),
|
||||
Self::Bnd => write!(f, "BND"),
|
||||
Self::Msk => write!(f, "MSK"),
|
||||
Self::Tile => write!(f, "Tile"),
|
||||
Self::Msr => write!(f, "MSR"),
|
||||
Self::Xcr => write!(f, "XCR"),
|
||||
Self::Sys => write!(f, "Sys"),
|
||||
Self::X87 => write!(f, "X87"),
|
||||
Self::Mxcsr => write!(f, "MXCSR"),
|
||||
Self::Pkru => write!(f, "PKRU"),
|
||||
Self::Ssp => write!(f, "SSP"),
|
||||
Self::Flg => write!(f, "Flg"),
|
||||
Self::Rip => write!(f, "RIP"),
|
||||
Self::Uif => write!(f, "UIF"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes a register operand.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct OpReg {
|
||||
/// The type of the register.
|
||||
pub kind: OpRegType,
|
||||
|
||||
/// Indicates the register size, in bytes.
|
||||
///
|
||||
/// This may not be equal to the [`Operand::size`] field, as a smaller amount of data may be processed from a
|
||||
/// register (especially if we have a SSE register or a mask register).
|
||||
pub size: u32,
|
||||
|
||||
// TODO: create an enum for each register, instead of using an ID.
|
||||
/// The register index/ID.
|
||||
///
|
||||
/// Registers are numbered as they are in the Intel documentation. Examples:
|
||||
///
|
||||
/// - `AL` = `AX` = `EAX` = `RAX` = 0
|
||||
/// - `R15B` = `R15W` = `R15D` = `R15` = 15
|
||||
/// - `XMM0` = 0
|
||||
/// - `ZMM31` = 31
|
||||
/// - `ES` = 0
|
||||
/// - `CS` = 1
|
||||
///
|
||||
/// # Remarks
|
||||
///
|
||||
/// If [kind](OpReg::kind) is [OpRegType::Gpr](OpRegType::Gpr), the high and low part of 16-bit registers will have
|
||||
/// the same index (for example, `AH` and `AL`). To differentiate between them use [is_high8](OpReg::is_high8).
|
||||
pub index: usize,
|
||||
|
||||
/// The number of registers accessed, starting with [id](OpReg::index).
|
||||
pub count: u32,
|
||||
|
||||
/// `true` if this is the high part of a 16-bit GPR: `AH`, `CH`, `DH`, or `BH`.
|
||||
pub is_high8: bool,
|
||||
|
||||
/// `true` if this is a block register addressing.
|
||||
pub is_block: bool,
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl From<ffi::ND_OPDESC_REGISTER> for OpReg {
|
||||
fn from(op: ffi::ND_OPDESC_REGISTER) -> OpReg {
|
||||
let kind = OpRegType::from(op);
|
||||
let is_high8 = op.IsHigh8();
|
||||
let index = match kind {
|
||||
OpRegType::Gpr => {
|
||||
if is_high8 {
|
||||
// See `ShemuGetGprValue` in `bdshemu.c`.
|
||||
op.Reg - 4
|
||||
} else {
|
||||
op.Reg
|
||||
}
|
||||
}
|
||||
_ => op.Reg,
|
||||
} as usize;
|
||||
|
||||
OpReg {
|
||||
kind,
|
||||
size: op.Size,
|
||||
index,
|
||||
count: op.Count,
|
||||
is_high8,
|
||||
is_block: op.IsBlock(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Holds extra information for instruction that use VSIB addressing.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct Vsib {
|
||||
/// VSIB element size.
|
||||
pub vsib_element_size: u8,
|
||||
|
||||
/// Number of elements scattered/gathered/prefetched.
|
||||
pub vsib_element_count: u8,
|
||||
}
|
||||
|
||||
/// Shadow stack access types.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub enum ShadowStackAccess {
|
||||
None,
|
||||
|
||||
/// Explicit memory operand accessed as shadow stack.
|
||||
Explicit,
|
||||
|
||||
/// Shadow Stack Pointer (`SSP`) used as base for addressing using conventional load/store.
|
||||
SspLdSt,
|
||||
|
||||
/// Shadow Stack Pointer (`SSP`) used as base for addressing using `push`/`pop`.
|
||||
SspPushPop,
|
||||
|
||||
/// Privilege 0 `SSP` (`IA32_PL0_SSP`) used (`SETSSBSY`).
|
||||
Pl0Ssp,
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl From<ffi::ND_SHSTK_ACCESS> for ShadowStackAccess {
|
||||
fn from(acc: ffi::ND_SHSTK_ACCESS) -> ShadowStackAccess {
|
||||
match acc {
|
||||
ffi::_ND_SHSTK_ACCESS::ND_SHSTK_NONE => ShadowStackAccess::None,
|
||||
ffi::_ND_SHSTK_ACCESS::ND_SHSTK_EXPLICIT => ShadowStackAccess::Explicit,
|
||||
ffi::_ND_SHSTK_ACCESS::ND_SHSTK_SSP_LD_ST => ShadowStackAccess::SspLdSt,
|
||||
ffi::_ND_SHSTK_ACCESS::ND_SHSTK_SSP_PUSH_POP => ShadowStackAccess::SspPushPop,
|
||||
ffi::_ND_SHSTK_ACCESS::ND_SHSTK_PL0_SSP => ShadowStackAccess::Pl0Ssp,
|
||||
// NOTE: when updating this take care to also update the `From<u8>` implementation!
|
||||
// TODO: any way of keeping these in sync automagically?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u8> for ShadowStackAccess {
|
||||
fn from(acc: u8) -> ShadowStackAccess {
|
||||
if acc == ffi::_ND_SHSTK_ACCESS::ND_SHSTK_NONE as u8 {
|
||||
ShadowStackAccess::None
|
||||
} else if acc == ffi::_ND_SHSTK_ACCESS::ND_SHSTK_EXPLICIT as u8 {
|
||||
ShadowStackAccess::Explicit
|
||||
} else if acc == ffi::_ND_SHSTK_ACCESS::ND_SHSTK_SSP_LD_ST as u8 {
|
||||
ShadowStackAccess::SspLdSt
|
||||
} else if acc == ffi::_ND_SHSTK_ACCESS::ND_SHSTK_SSP_PUSH_POP as u8 {
|
||||
ShadowStackAccess::SspPushPop
|
||||
} else if acc == ffi::_ND_SHSTK_ACCESS::ND_SHSTK_PL0_SSP as u8 {
|
||||
ShadowStackAccess::Pl0Ssp
|
||||
} else {
|
||||
panic!("Unexpected shadow stack access type: {}", acc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes a memory operand.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct OpMem {
|
||||
/// `true` if the memory operand is a broadcast operand.
|
||||
pub has_broadcast: bool,
|
||||
|
||||
/// `true` if this is a rip-relative addressing. Base, Index, Scale are all ignored.
|
||||
pub is_rip_rel: bool,
|
||||
|
||||
/// `true` if this is a stack op.
|
||||
///
|
||||
/// # Note
|
||||
///
|
||||
/// Explicit stack accesses are not included (eg: `mov eax, [rsp]`).
|
||||
pub is_stack: bool,
|
||||
|
||||
/// `true` for `[RSI]` and `[RDI]` operands inside string operations.
|
||||
pub is_string: bool,
|
||||
|
||||
/// `true` if direct addressing (`MOV [...], EAX, 0xA3`).
|
||||
pub is_direct: bool,
|
||||
/// `true` if this is a bit base. Used for `BT*` instructions.
|
||||
///
|
||||
/// The bitbase stored in the second operand must be added to the linear address.
|
||||
pub is_bitbase: bool,
|
||||
|
||||
/// `true` if the memory operand is address generation and no memory access is made.
|
||||
pub is_ag: bool,
|
||||
|
||||
/// `true` MIB addressing is used (MPX instructions).
|
||||
pub is_mib: bool,
|
||||
|
||||
/// `true` the addressing uses sibmem (AMX instructions).
|
||||
pub is_sib_mem: bool,
|
||||
|
||||
/// Base segment used to address the memory.
|
||||
pub seg: Option<u8>,
|
||||
|
||||
/// Base register.
|
||||
pub base: Option<u8>,
|
||||
|
||||
/// Base register size, in bytes. Max 8 bytes.
|
||||
pub base_size: Option<u32>,
|
||||
|
||||
/// Index register. Can be a vector reg (ZMM0-ZMM31).
|
||||
pub index: Option<u8>,
|
||||
|
||||
/// Index register size, in bytes. Max 8 bytes.
|
||||
pub index_size: Option<u32>,
|
||||
|
||||
/// The `index` register selects a vector register.
|
||||
pub vsib: Option<Vsib>,
|
||||
|
||||
/// Scale: 1, 2, 4 or 8. Always present if `index` is present.
|
||||
pub scale: Option<u8>,
|
||||
|
||||
/// Sign extended displacement.
|
||||
pub disp: Option<u64>,
|
||||
|
||||
/// Displacement size. Max 4 bytes.
|
||||
pub disp_size: Option<u8>,
|
||||
|
||||
/// Compressed displacement size - 1, 2, 4, 8, 16, 32, 64.
|
||||
pub comp_disp_size: Option<u8>,
|
||||
|
||||
/// Shadow stack access type.
|
||||
///
|
||||
/// Will be `None` if the shadow stack is not accessed.
|
||||
pub shadow_stack_access: Option<ShadowStackAccess>,
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl From<ffi::ND_OPDESC_MEMORY> for OpMem {
|
||||
fn from(op: ffi::ND_OPDESC_MEMORY) -> OpMem {
|
||||
let seg = if op.HasSeg() { Some(op.Seg) } else { None };
|
||||
|
||||
let (base, base_size) = if op.HasBase() {
|
||||
(Some(op.Base), Some(op.BaseSize))
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
let (index, index_size, scale) = if op.HasIndex() {
|
||||
(Some(op.Index), Some(op.IndexSize), Some(op.Scale))
|
||||
} else {
|
||||
(None, None, None)
|
||||
};
|
||||
|
||||
let (disp, disp_size) = if op.HasDisp() {
|
||||
(Some(op.Disp), Some(op.DispSize))
|
||||
} else {
|
||||
(None, None)
|
||||
};
|
||||
|
||||
let comp_disp_size = if op.HasCompDisp() {
|
||||
Some(op.CompDispSize)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let (vsib, index_size) = if op.IsVsib() {
|
||||
(
|
||||
Some(Vsib {
|
||||
vsib_element_size: op.Vsib.ElemSize,
|
||||
vsib_element_count: op.Vsib.ElemCount,
|
||||
}),
|
||||
Some(op.Vsib.IndexSize as u32),
|
||||
)
|
||||
} else {
|
||||
(None, index_size)
|
||||
};
|
||||
|
||||
let shadow_stack_access = if op.IsShadowStack() {
|
||||
Some(ShadowStackAccess::from(op.ShStkType))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
OpMem {
|
||||
has_broadcast: op.HasBroadcast(),
|
||||
is_rip_rel: op.IsRipRel(),
|
||||
is_stack: op.IsStack(),
|
||||
is_string: op.IsString(),
|
||||
is_direct: op.IsDirect(),
|
||||
is_bitbase: op.IsBitbase(),
|
||||
is_ag: op.IsAG(),
|
||||
is_mib: op.IsMib(),
|
||||
is_sib_mem: op.IsSibMem(),
|
||||
seg,
|
||||
base,
|
||||
base_size,
|
||||
index,
|
||||
index_size,
|
||||
vsib,
|
||||
scale,
|
||||
disp,
|
||||
disp_size,
|
||||
comp_disp_size,
|
||||
shadow_stack_access,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Extended operand information, based on the actual type of the operand.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub enum OpInfo {
|
||||
None,
|
||||
|
||||
/// The operand is a register.
|
||||
Reg(OpReg),
|
||||
|
||||
/// The operand is located in memory.
|
||||
Mem(OpMem),
|
||||
|
||||
/// The operand is an immediate.
|
||||
Imm(u64),
|
||||
|
||||
/// The operand is a relative offset.
|
||||
Offs(u64),
|
||||
|
||||
/// The operand is an absolute address, in the form `seg:offset`.
|
||||
Addr(OpAddr),
|
||||
|
||||
/// The operand is an implicit constant.
|
||||
Const(u64),
|
||||
|
||||
/// An entire bank/set of registers are being accessed. Used in `PUSHA`/`POPA`/`XSAVE`/`LOADALL`.
|
||||
Bank,
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl From<ffi::ND_OPERAND> for OpInfo {
|
||||
fn from(op: ffi::ND_OPERAND) -> OpInfo {
|
||||
match op.Type {
|
||||
ffi::_ND_OPERAND_TYPE::ND_OP_NOT_PRESENT => OpInfo::None,
|
||||
ffi::_ND_OPERAND_TYPE::ND_OP_REG => {
|
||||
OpInfo::Reg(OpReg::from(unsafe { op.Info.Register }))
|
||||
}
|
||||
ffi::_ND_OPERAND_TYPE::ND_OP_MEM => OpInfo::Mem(OpMem::from(unsafe { op.Info.Memory })),
|
||||
ffi::_ND_OPERAND_TYPE::ND_OP_IMM => OpInfo::Imm(unsafe { op.Info.Immediate }.Imm),
|
||||
ffi::_ND_OPERAND_TYPE::ND_OP_OFFS => {
|
||||
OpInfo::Offs(unsafe { op.Info.RelativeOffset }.Rel)
|
||||
}
|
||||
ffi::_ND_OPERAND_TYPE::ND_OP_ADDR => {
|
||||
OpInfo::Addr(OpAddr::from(unsafe { op.Info.Address }))
|
||||
}
|
||||
ffi::_ND_OPERAND_TYPE::ND_OP_CONST => OpInfo::Const(unsafe { op.Info.Constant }.Const),
|
||||
ffi::_ND_OPERAND_TYPE::ND_OP_BANK => OpInfo::Bank,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for OpInfo {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::None => write!(f, "None"),
|
||||
Self::Reg(_) => write!(f, "register"),
|
||||
Self::Mem(_) => write!(f, "memory"),
|
||||
Self::Imm(_) => write!(f, "immediate"),
|
||||
Self::Offs(_) => write!(f, "relative offset"),
|
||||
Self::Addr(_) => write!(f, "absolute address"),
|
||||
Self::Const(_) => write!(f, "implicit constant"),
|
||||
Self::Bank => write!(f, "register bank"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Operand size.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub enum OpSize {
|
||||
/// The inner value contains the operand size in bytes.
|
||||
Bytes(usize),
|
||||
|
||||
/// The size of a cache line.
|
||||
CacheLine,
|
||||
|
||||
/// Unknown size.
|
||||
Unknown,
|
||||
}
|
||||
|
||||
impl fmt::Display for OpSize {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
Self::Bytes(size) => write!(f, "{} bytes", size),
|
||||
Self::CacheLine => write!(f, "cache line"),
|
||||
Self::Unknown => write!(f, "unknown"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl From<ffi::ND_OPERAND_SIZE> for OpSize {
|
||||
fn from(sz: ffi::ND_OPERAND_SIZE) -> OpSize {
|
||||
match sz {
|
||||
ffi::ND_SIZE_8BIT => OpSize::Bytes(1),
|
||||
ffi::ND_SIZE_16BIT => OpSize::Bytes(2),
|
||||
ffi::ND_SIZE_32BIT => OpSize::Bytes(4),
|
||||
ffi::ND_SIZE_48BIT => OpSize::Bytes(6),
|
||||
ffi::ND_SIZE_64BIT => OpSize::Bytes(8),
|
||||
ffi::ND_SIZE_80BIT => OpSize::Bytes(10),
|
||||
ffi::ND_SIZE_112BIT => OpSize::Bytes(14),
|
||||
ffi::ND_SIZE_128BIT => OpSize::Bytes(16),
|
||||
ffi::ND_SIZE_224BIT => OpSize::Bytes(28),
|
||||
ffi::ND_SIZE_256BIT => OpSize::Bytes(32),
|
||||
ffi::ND_SIZE_384BIT => OpSize::Bytes(48),
|
||||
ffi::ND_SIZE_512BIT => OpSize::Bytes(64),
|
||||
ffi::ND_SIZE_752BIT => OpSize::Bytes(94),
|
||||
ffi::ND_SIZE_864BIT => OpSize::Bytes(108),
|
||||
ffi::ND_SIZE_4096BIT => OpSize::Bytes(512),
|
||||
ffi::ND_SIZE_1KB => OpSize::Bytes(1024),
|
||||
ffi::ND_SIZE_CACHE_LINE => OpSize::CacheLine,
|
||||
ffi::ND_SIZE_UNKNOWN => OpSize::Unknown,
|
||||
_ => panic!("Unespected operand size: {}", sz),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Operand access mode.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct OpAccess {
|
||||
/// The operand is read.
|
||||
pub read: bool,
|
||||
|
||||
/// The operand is written.
|
||||
pub write: bool,
|
||||
|
||||
/// The operand is read only under some conditions.
|
||||
pub cond_read: bool,
|
||||
|
||||
/// The operand is written only under some conditions.
|
||||
pub cond_write: bool,
|
||||
|
||||
/// The operand is prefetched.
|
||||
pub prefetch: bool,
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl From<ffi::ND_OPERAND_ACCESS> for OpAccess {
|
||||
fn from(acc: ffi::ND_OPERAND_ACCESS) -> OpAccess {
|
||||
let acc = unsafe { acc.__bindgen_anon_1 };
|
||||
|
||||
OpAccess {
|
||||
read: acc.Read() != 0,
|
||||
write: acc.Write() != 0,
|
||||
cond_read: acc.CondRead() != 0,
|
||||
cond_write: acc.CondWrite() != 0,
|
||||
prefetch: acc.Prefetch() != 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Broadcast specifier.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct Broadcast {
|
||||
/// Number of times to broadcast the element.
|
||||
pub count: u8,
|
||||
|
||||
/// Size of one element.
|
||||
pub size: u8,
|
||||
}
|
||||
|
||||
/// Decorator information.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct Decorator {
|
||||
/// If mask is present, holds the ID of the mask register (`K0` - `K7`) used.
|
||||
pub mask_register: Option<u8>,
|
||||
|
||||
/// `true` if zeroing will be made, `false` if merging will be made.
|
||||
pub has_zero: bool,
|
||||
|
||||
/// If broadcasting is being made, holds the broadcast specifier.
|
||||
pub broadcast: Option<Broadcast>,
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl From<ffi::ND_OPERAND_DECORATOR> for Decorator {
|
||||
fn from(decorator: ffi::ND_OPERAND_DECORATOR) -> Decorator {
|
||||
let mask_register = if decorator.HasMask() {
|
||||
Some(decorator.Mask.Msk)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let broadcast = if decorator.HasBroadcast() {
|
||||
Some(Broadcast {
|
||||
count: decorator.Broadcast.Count,
|
||||
size: decorator.Broadcast.Size,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Decorator {
|
||||
mask_register,
|
||||
has_zero: decorator.HasZero(),
|
||||
broadcast,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes an instruction operand.
|
||||
///
|
||||
/// Each operand type encodes different information. See [`OpInfo`] for details.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// # use std::error::Error;
|
||||
/// #
|
||||
/// # fn main() -> Result<(), Box<dyn Error>> {
|
||||
/// use bddisasm::decoder::{DecodedInstruction, DecodeMode};
|
||||
/// use bddisasm::operand::*;
|
||||
///
|
||||
/// // `MOV ah, byte ptr [rcx+rdx*2+0x8]`
|
||||
/// let code = vec![0x8a, 0x64, 0x51, 0x08];
|
||||
/// let ins = DecodedInstruction::decode(&code, DecodeMode::Bits64)?;
|
||||
///
|
||||
/// let operands = ins.operands();
|
||||
/// let dst = operands[0];
|
||||
/// let src = operands[1];
|
||||
///
|
||||
/// // Get the size of each operand
|
||||
/// println!("Destination size: {}", dst.size);
|
||||
/// println!("Source size: {}", src.size);
|
||||
///
|
||||
/// // Get the type of the destination operand
|
||||
/// match dst.info {
|
||||
/// OpInfo::Reg(reg) => {
|
||||
/// println!("Register kind: {} Size: {} Index: {}", reg.kind, reg.size, reg.index)
|
||||
/// },
|
||||
/// // In this case we know that the destination operand is a register
|
||||
/// _ => println!("Unexpected operand info type"),
|
||||
/// }
|
||||
///
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub struct Operand {
|
||||
/// Extended operand information.
|
||||
pub info: OpInfo,
|
||||
|
||||
// Operand size in bytes.
|
||||
//
|
||||
// This should be used when operating with the operand. It includes sign-extension or zero-extensions.
|
||||
//
|
||||
// # Note
|
||||
///
|
||||
/// It indicates the actual amount of data used for processing. If the operand is a [register](OpInfo::Reg), it
|
||||
/// MAY NOT indicate the register size. Use the [info](Operand::info) field to get the actual register size in these
|
||||
/// cases.
|
||||
pub size: OpSize,
|
||||
|
||||
/// Raw size inside the instruction.
|
||||
///
|
||||
/// This will usually be identical to [size](Operand::size), however, some instructions force the actual size of
|
||||
/// their operands to 64 bit (`PUSH`/`POP` or branches are good examples).
|
||||
///
|
||||
/// Although the raw size of the relative offset or the immediate will be [raw_size](Operand::raw_size), internally,
|
||||
/// the CPU will use [size](Operand::size) (usually sign-extended).
|
||||
pub raw_size: OpSize,
|
||||
|
||||
/// Access mode.
|
||||
pub access: OpAccess,
|
||||
|
||||
/// `true` if the operand is default. This also applies to implicit operands.
|
||||
pub is_default: bool,
|
||||
|
||||
/// Decorator information.
|
||||
pub decorator: Decorator,
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl From<ffi::ND_OPERAND> for Operand {
|
||||
fn from(op: ffi::ND_OPERAND) -> Operand {
|
||||
Operand {
|
||||
info: OpInfo::from(op),
|
||||
size: OpSize::from(op.Size),
|
||||
raw_size: OpSize::from(op.RawSize),
|
||||
access: OpAccess::from(op.Access),
|
||||
is_default: unsafe { op.Flags.__bindgen_anon_1 }.IsDefault() != 0,
|
||||
decorator: Decorator::from(op.Decorator),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::decoded_instruction::{DecodeMode, DecodedInstruction};
|
||||
|
||||
#[test]
|
||||
fn ops() {
|
||||
let code = vec![0x8a, 0x64, 0x51, 0x08];
|
||||
let ins = DecodedInstruction::decode(&code, DecodeMode::Bits64).unwrap();
|
||||
|
||||
let operands = ins.operands();
|
||||
assert_eq!(operands.len(), 2);
|
||||
|
||||
let dest = operands[0];
|
||||
assert_eq!(dest.size, OpSize::Bytes(1));
|
||||
assert_eq!(dest.raw_size, OpSize::Bytes(1));
|
||||
assert_eq!(dest.is_default, false);
|
||||
assert!(dest.access.write);
|
||||
|
||||
if let OpInfo::Reg(dst_reg) = dest.info {
|
||||
assert_eq!(dst_reg.kind, OpRegType::Gpr);
|
||||
assert_eq!(dst_reg.size, 1);
|
||||
assert_eq!(dst_reg.index, 0);
|
||||
assert_eq!(dst_reg.is_high8, true);
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
let src = operands[1];
|
||||
assert_eq!(src.size, OpSize::Bytes(1));
|
||||
assert_eq!(src.raw_size, OpSize::Bytes(1));
|
||||
assert_eq!(src.is_default, false);
|
||||
assert!(src.access.read);
|
||||
|
||||
if let OpInfo::Mem(src_mem) = src.info {
|
||||
assert_eq!(src_mem.seg, Some(3));
|
||||
assert_eq!(src_mem.base, Some(1));
|
||||
assert_eq!(src_mem.base_size, Some(8));
|
||||
assert_eq!(src_mem.index, Some(2));
|
||||
assert_eq!(src_mem.index_size, Some(8));
|
||||
assert_eq!(src_mem.scale, Some(2));
|
||||
assert_eq!(src_mem.disp, Some(8));
|
||||
assert_eq!(src_mem.disp_size, Some(1));
|
||||
} else {
|
||||
unreachable!();
|
||||
}
|
||||
}
|
||||
}
|
46
rsbddisasm/bddisasm/src/rflags.rs
Normal file
46
rsbddisasm/bddisasm/src/rflags.rs
Normal file
@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Bitdefender
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
//! Describes the bits in the FLAGS register.
|
||||
|
||||
extern crate bddisasm_sys as ffi;
|
||||
|
||||
/// Carry flag.
|
||||
pub const CF: u32 = ffi::NDR_RFLAG_CF;
|
||||
/// Parity flag.
|
||||
pub const PF: u32 = ffi::NDR_RFLAG_PF;
|
||||
/// Auxiliary flag.
|
||||
pub const AF: u32 = ffi::NDR_RFLAG_AF;
|
||||
/// Zero flag.
|
||||
pub const ZF: u32 = ffi::NDR_RFLAG_ZF;
|
||||
/// Sign flag.
|
||||
pub const SF: u32 = ffi::NDR_RFLAG_SF;
|
||||
/// Trap flag.
|
||||
pub const TF: u32 = ffi::NDR_RFLAG_TF;
|
||||
/// Interrupt flag.
|
||||
pub const IF: u32 = ffi::NDR_RFLAG_IF;
|
||||
/// Direction flag.
|
||||
pub const DF: u32 = ffi::NDR_RFLAG_DF;
|
||||
/// Overflow flag.
|
||||
pub const OF: u32 = ffi::NDR_RFLAG_OF;
|
||||
/// I/O privilege level flag.
|
||||
pub const IOPL: u32 = ffi::NDR_RFLAG_IOPL;
|
||||
/// Nested task flag.
|
||||
pub const NT: u32 = ffi::NDR_RFLAG_NT;
|
||||
/// Resume flag.
|
||||
pub const RF: u32 = ffi::NDR_RFLAG_RF;
|
||||
/// Virtual mode flag.
|
||||
pub const VM: u32 = ffi::NDR_RFLAG_VM;
|
||||
/// Alignment check flag.
|
||||
pub const AC: u32 = ffi::NDR_RFLAG_AC;
|
||||
/// Virtual interrupts flag.
|
||||
pub const VIF: u32 = ffi::NDR_RFLAG_VIF;
|
||||
/// Virtual interrupt pending flag.
|
||||
pub const VIP: u32 = ffi::NDR_RFLAG_VIP;
|
||||
/// CPUID identification flag.
|
||||
pub const ID: u32 = ffi::NDR_RFLAG_ID;
|
||||
|
||||
pub(crate) fn flags_raw(flags: ffi::ND_RFLAGS) -> u32 {
|
||||
unsafe { flags.Raw }
|
||||
}
|
122
rsbddisasm/bddisasm/src/tuple.rs
Normal file
122
rsbddisasm/bddisasm/src/tuple.rs
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (c) 2021 Bitdefender
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
//! Instruction tuple type.
|
||||
|
||||
extern crate bddisasm_sys as ffi;
|
||||
|
||||
/// Instruction tuple type.
|
||||
///
|
||||
/// Used to determine compressed displacement size for `disp8 EVEX` instructions. Note that most of the `EVEX` encoded
|
||||
/// instructions use the compressed displacement addressing scheme.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug)]
|
||||
pub enum Tuple {
|
||||
None,
|
||||
/// Full Vector.
|
||||
Fv,
|
||||
/// Half Vector.
|
||||
Hv,
|
||||
/// Quarter Vector.
|
||||
Qv,
|
||||
/// Tuple1 scalar, size 8 bit.
|
||||
T1S8,
|
||||
/// Tuple1 scalar, size 16 bit.
|
||||
T1S16,
|
||||
/// Tuple1 scalar, size 32/64 bit.
|
||||
T1S,
|
||||
/// Tuple1 float, size 32/64 bit.
|
||||
T1F,
|
||||
/// Tuple2, 64/128 bit.
|
||||
T2,
|
||||
/// Tuple4, 128/256 bit.
|
||||
T4,
|
||||
/// Tuple8, 256 bit.
|
||||
T8,
|
||||
/// Full Vector Memory.
|
||||
Fvm,
|
||||
/// Half Vector Memory.
|
||||
Hvm,
|
||||
/// Quarter Vector Memory.
|
||||
Qvm,
|
||||
/// Oct Vector Memory.
|
||||
OVm,
|
||||
/// M128, 128 bit.
|
||||
M128,
|
||||
/// DUP (VMOVDDUP).
|
||||
Dup,
|
||||
/// 4 x 32 bit Memory Elements are referenced.
|
||||
T14X,
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
impl From<ffi::ND_TUPLE> for Tuple {
|
||||
fn from(value: ffi::ND_TUPLE) -> Tuple {
|
||||
match value {
|
||||
ffi::_ND_TUPLE::ND_TUPLE_None => Tuple::None,
|
||||
ffi::_ND_TUPLE::ND_TUPLE_FV => Tuple::Fv,
|
||||
ffi::_ND_TUPLE::ND_TUPLE_HV => Tuple::Hv,
|
||||
ffi::_ND_TUPLE::ND_TUPLE_QV => Tuple::Qv,
|
||||
ffi::_ND_TUPLE::ND_TUPLE_T1S8 => Tuple::T1S8,
|
||||
ffi::_ND_TUPLE::ND_TUPLE_T1S16 => Tuple::T1S16,
|
||||
ffi::_ND_TUPLE::ND_TUPLE_T1S => Tuple::T1S,
|
||||
ffi::_ND_TUPLE::ND_TUPLE_T1F => Tuple::T1F,
|
||||
ffi::_ND_TUPLE::ND_TUPLE_T2 => Tuple::T2,
|
||||
ffi::_ND_TUPLE::ND_TUPLE_T4 => Tuple::T4,
|
||||
ffi::_ND_TUPLE::ND_TUPLE_T8 => Tuple::T8,
|
||||
ffi::_ND_TUPLE::ND_TUPLE_FVM => Tuple::Fvm,
|
||||
ffi::_ND_TUPLE::ND_TUPLE_HVM => Tuple::Hvm,
|
||||
ffi::_ND_TUPLE::ND_TUPLE_QVM => Tuple::Qvm,
|
||||
ffi::_ND_TUPLE::ND_TUPLE_OVM => Tuple::OVm,
|
||||
ffi::_ND_TUPLE::ND_TUPLE_M128 => Tuple::M128,
|
||||
ffi::_ND_TUPLE::ND_TUPLE_DUP => Tuple::Dup,
|
||||
ffi::_ND_TUPLE::ND_TUPLE_T1_4X => Tuple::T14X,
|
||||
// NOTE: when updating this take care to also update the `From<u32>` implementation!
|
||||
// TODO: any way of keeping these in sync automagically?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<u32> for Tuple {
|
||||
fn from(value: u32) -> Tuple {
|
||||
if value == ffi::_ND_TUPLE::ND_TUPLE_None as u32 {
|
||||
Tuple::None
|
||||
} else if value == ffi::_ND_TUPLE::ND_TUPLE_FV as u32 {
|
||||
Tuple::Fv
|
||||
} else if value == ffi::_ND_TUPLE::ND_TUPLE_HV as u32 {
|
||||
Tuple::Hv
|
||||
} else if value == ffi::_ND_TUPLE::ND_TUPLE_QV as u32 {
|
||||
Tuple::Qv
|
||||
} else if value == ffi::_ND_TUPLE::ND_TUPLE_T1S8 as u32 {
|
||||
Tuple::T1S8
|
||||
} else if value == ffi::_ND_TUPLE::ND_TUPLE_T1S16 as u32 {
|
||||
Tuple::T1S16
|
||||
} else if value == ffi::_ND_TUPLE::ND_TUPLE_T1S as u32 {
|
||||
Tuple::T1S
|
||||
} else if value == ffi::_ND_TUPLE::ND_TUPLE_T1F as u32 {
|
||||
Tuple::T1F
|
||||
} else if value == ffi::_ND_TUPLE::ND_TUPLE_T2 as u32 {
|
||||
Tuple::T2
|
||||
} else if value == ffi::_ND_TUPLE::ND_TUPLE_T4 as u32 {
|
||||
Tuple::T4
|
||||
} else if value == ffi::_ND_TUPLE::ND_TUPLE_T8 as u32 {
|
||||
Tuple::T8
|
||||
} else if value == ffi::_ND_TUPLE::ND_TUPLE_FVM as u32 {
|
||||
Tuple::Fvm
|
||||
} else if value == ffi::_ND_TUPLE::ND_TUPLE_HVM as u32 {
|
||||
Tuple::Hvm
|
||||
} else if value == ffi::_ND_TUPLE::ND_TUPLE_QVM as u32 {
|
||||
Tuple::Qvm
|
||||
} else if value == ffi::_ND_TUPLE::ND_TUPLE_OVM as u32 {
|
||||
Tuple::OVm
|
||||
} else if value == ffi::_ND_TUPLE::ND_TUPLE_M128 as u32 {
|
||||
Tuple::M128
|
||||
} else if value == ffi::_ND_TUPLE::ND_TUPLE_DUP as u32 {
|
||||
Tuple::Dup
|
||||
} else if value == ffi::_ND_TUPLE::ND_TUPLE_T1_4X as u32 {
|
||||
Tuple::T14X
|
||||
} else {
|
||||
panic!("Unknown tuple: {}", value)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user