mirror of
https://github.com/bitdefender/bddisasm.git
synced 2025-01-18 19:10:57 +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
|
bdshemu_fuzz/out-64
|
||||||
docs/build
|
docs/build
|
||||||
libbddisasm.pc
|
libbddisasm.pc
|
||||||
build*
|
build/
|
||||||
.vscode
|
.vscode
|
||||||
disasmtool_lix/_build
|
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