aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2026-02-25 09:22:14 +0000
committeriximeow <me@iximeow.net>2026-02-25 09:22:14 +0000
commita7bfd957879388e6d2d55cb188bfdc47f01ffb96 (patch)
treea3ebe03ec56ece85f9eb8b8a6d1c1b74ee4bb68d
parent953fb84fca06f34f1ae70481f6449c39f734f24b (diff)
hey that's useful
-rw-r--r--Cargo.toml3
-rw-r--r--ffi/long_mode/Cargo.toml4
-rw-r--r--src/long_mode/mod.rs65
3 files changed, 66 insertions, 6 deletions
diff --git a/Cargo.toml b/Cargo.toml
index a8d2c86..7369627 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -65,3 +65,6 @@ capstone_bench = []
# this disables a lot of inlining to make it easier for me to measure
# likelihood of codepaths for typical instruction sequences
profiling = []
+
+# do not use this. it is for development and library debugging only.
+_debug_internal_disasm_stats = []
diff --git a/ffi/long_mode/Cargo.toml b/ffi/long_mode/Cargo.toml
index 0b6a529..3b4363d 100644
--- a/ffi/long_mode/Cargo.toml
+++ b/ffi/long_mode/Cargo.toml
@@ -6,7 +6,7 @@ edition = "2018"
[dependencies]
yaxpeax-x86 = { path = "../../", default-features = false }
-yaxpeax-arch = { version = "0.3.1", default-features = false }
+yaxpeax-arch = { version = "0.3.1", default-features = false, features = [] }
[lib]
name = "yaxpeax_x86_ffi_long_mode"
@@ -14,6 +14,6 @@ path = "src/lib.rs"
crate-type = ["staticlib", "cdylib"]
[features]
-default = ["fmt"]
+default = [] # "fmt"]
fmt = ["yaxpeax-x86/fmt"]
diff --git a/src/long_mode/mod.rs b/src/long_mode/mod.rs
index 233fb15..4168864 100644
--- a/src/long_mode/mod.rs
+++ b/src/long_mode/mod.rs
@@ -1103,9 +1103,6 @@ const XSAVE: [Opcode; 10] = [
Opcode::XSETBV,
];
-//struct OpcodeBuilder {
-// variant_specifier: OpcodeVariantInfo
-
/// an `x86_64` opcode. there sure are a lot of these.
#[allow(non_camel_case_types)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
@@ -2789,6 +2786,49 @@ impl LengthedInstruction for Instruction {
}
}
+#[cfg(feature="_debug_internal_disasm_stats")]
+mod disasm_stats {
+}
+#[cfg(not(feature="_debug_internal_disasm_stats"))]
+mod disasm_stats {
+ macro_rules! shim_impl {
+ ($name:ident) => {
+ #[inline(always)]
+ pub fn $name() { }
+ }
+ }
+
+ #[inline(always)]
+ pub fn record_opcode(_opc: super::Opcode) {
+ }
+
+ #[inline(always)]
+ pub(crate) fn record_operand_case(_oper: super::OperandCase) {
+ }
+
+ shim_impl!(record_opc_0f);
+ shim_impl!(record_opc_rep_0f);
+ shim_impl!(record_opc_repnz_0f);
+ shim_impl!(record_opc_op_size_0f);
+ shim_impl!(record_opc_0f38);
+ shim_impl!(record_opc_0f3a);
+
+ shim_impl!(check_lock);
+ shim_impl!(read_operands);
+ shim_impl!(is_only_modrm_operands);
+ shim_impl!(instr_done);
+
+ shim_impl!(prefix_rex);
+ shim_impl!(read_opc_hotpath_hit);
+ shim_impl!(read_opc_hotpath_miss);
+ shim_impl!(indirect_branch_prefixes_slow);
+}
+
+#[cfg(feature="_debug_internal_disasm_stats")]
+pub use disasm_stats::DisasmStats;
+#[cfg(feature="_debug_internal_disasm_stats")]
+pub use disasm_stats::DISASM_STATS;
+
/// an `x86_64` instruction decoder.
///
/// fundamentally this is one or two primitives with no additional state kept during decoding. it
@@ -3822,7 +3862,7 @@ pub struct OperandCodeWrapper { code: OperandCode }
#[allow(non_camel_case_types)]
#[derive(Debug, PartialEq, Copy, Clone)]
#[repr(u8)]
-enum OperandCase {
+pub enum OperandCase {
Internal = 0, // handled internally and completely by embedded rules.
Gv_M = 1, // "internal", but must be distinguished from Gv_Ev
Ibs = 2,
@@ -5261,6 +5301,7 @@ fn record_opcode_record_found<
T: Reader<Address<Arch>, Word<Arch>>,
S: DescriptionSink<FieldDescription>,
>(words: &mut T, sink: &mut S, opc: Opcode, code: OperandCode, opc_length: u32) {
+ disasm_stats::record_opcode(opc);
let offset = words.offset() as u32;
let opcode_start_bit = (offset - opc_length) * 8;
let opcode_end_bit = offset * 8 - 1;
@@ -5387,12 +5428,15 @@ fn read_with_annotations<
instruction.regs[2] = RegSpec::rax();
let record: OperandCode = if self.read_opc_hotpath(nextb, &mut nextb, &mut next_rec, words, instruction, sink)? {
+ disasm_stats::read_opc_hotpath_hit();
next_rec.operand()
} else {
+ disasm_stats::read_opc_hotpath_miss();
let prefixes = &mut instruction.prefixes;
let record = loop {
let mut record = next_rec;
if nextb >= 0x40 && nextb < 0x50 {
+ disasm_stats::prefix_rex();
let b = nextb;
sink.record((words.offset() - 1) as u32 * 8, (words.offset() - 1) as u32 * 8 + 7, FieldDescription {
desc: InnerDescription::RexPrefix(b),
@@ -5456,6 +5500,7 @@ fn read_with_annotations<
});
prefixes.set_rep();
} else {
+ disasm_stats::indirect_branch_prefixes_slow();
match b {
0x26 |
0x2e |
@@ -5525,11 +5570,14 @@ fn read_with_annotations<
self.read_operands(decoder, words, instruction, record, sink)?;
if self.check_lock {
+ disasm_stats::check_lock();
if !instruction.opcode.can_lock() || !instruction.operands[0].is_memory() {
return Err(DecodeError::InvalidPrefixes);
}
}
+ disasm_stats::instr_done();
+
Ok(())
}
@@ -5599,11 +5647,13 @@ fn read_operands<
InnerDescription::Boundary("opcode ends/operands begin (typically)")
.with_id(words.offset() as u32 * 8 - 1)
);
+ disasm_stats::read_operands();
let operand_code = OperandCodeBuilder::from_bits(operand_code as u16);
let modrm_start = words.offset() as u32 * 8;
let opcode_start = modrm_start - 8;
if operand_code.is_only_modrm_operands() {
+ disasm_stats::is_only_modrm_operands();
let bank;
let modrm = read_modrm(words)?;
let rrr = (modrm >> 3) & 7;
@@ -5943,6 +5993,7 @@ fn read_operands<
}
instruction.operand_count = 2;
+ disasm_stats::record_operand_case(operand_code.operand_case_handler_index());
// match operand_code {
match operand_code.operand_case_handler_index() {
// these operand cases are all `only_*`, and are unreachable here..
@@ -9024,18 +9075,23 @@ fn read_0f_opcode(&mut self, opcode: u8, prefixes: &mut Prefixes) -> OpcodeRecor
// invalid prefix is in fact an invalid instruction. so just duplicate for the four kinds of
// opcode lists.
if prefixes.repnz() {
+ disasm_stats::record_opc_repnz_0f();
REPNZ_0F_CODES[opcode as usize]
} else if prefixes.rep() {
+ disasm_stats::record_opc_rep_0f();
REP_0F_CODES[opcode as usize]
} else if prefixes.operand_size() {
+ disasm_stats::record_opc_op_size_0f();
OPERAND_SIZE_0F_CODES[opcode as usize]
} else {
+ disasm_stats::record_opc_0f();
NORMAL_0F_CODES[opcode as usize]
}
}
#[inline(always)]
fn read_0f38_opcode(&mut self, opcode: u8, prefixes: &mut Prefixes) -> Result<OpcodeRecord, DecodeError> {
+ disasm_stats::record_opc_0f38();
if prefixes.rep() {
const TBL: [OpcodeRecord; 0x28] = [
OpcodeRecord::new(Interpretation::Instruction(Opcode::Invalid), OperandCode::ModRM_0xf30f38d8),
@@ -9156,6 +9212,7 @@ fn read_0f38_opcode(&mut self, opcode: u8, prefixes: &mut Prefixes) -> Result<Op
#[inline(always)]
fn read_0f3a_opcode(&mut self, opcode: u8, prefixes: &mut Prefixes) -> Result<OpcodeRecord, DecodeError> {
+ disasm_stats::record_opc_0f3a();
if prefixes.rep() {
if prefixes != &Prefixes::new(0x10) {
return Err(DecodeError::InvalidOpcode);