aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2022-01-03 14:06:57 -0800
committeriximeow <me@iximeow.net>2022-01-03 14:06:57 -0800
commit0759de116479c8a6319450e1e116af39e8c844c5 (patch)
tree38b806c8c3035faf99c88ada34bb7eab4309c4e3
parentc9a266cd62713f2ff7f5cf637adafd685ee17f16 (diff)
parentfe9c41db5e4f2916439dd268a1b5e65447396ce3 (diff)
architecture-generic bit layouts of instructions
-rw-r--r--Cargo.lock238
-rw-r--r--Cargo.toml10
-rw-r--r--src/main.rs396
3 files changed, 434 insertions, 210 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 3522baf..bed9a70 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -4,9 +4,9 @@ version = 3
[[package]]
name = "ansi_term"
-version = "0.11.0"
+version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
+checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2"
dependencies = [
"winapi",
]
@@ -30,15 +30,15 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "bitflags"
-version = "1.2.1"
+version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bitvec"
-version = "0.19.5"
+version = "0.19.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8942c8d352ae1838c9dda0b0ca2ab657696ef2232a20147cf1b30ae1a9cb4321"
+checksum = "55f93d0ef3363c364d5976646a38f04cf67cfe1d4c8d160cdea02cab2c116b33"
dependencies = [
"funty",
"radium",
@@ -47,16 +47,10 @@ dependencies = [
]
[[package]]
-name = "cfg-if"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
-
-[[package]]
name = "clap"
-version = "2.33.3"
+version = "2.34.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
+checksum = "a0610544180c38b88101fecf2dd634b174a62eef6946f84dfc6a7127512b381c"
dependencies = [
"ansi_term",
"atty",
@@ -68,29 +62,10 @@ dependencies = [
]
[[package]]
-name = "crossterm"
-version = "0.19.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c36c10130df424b2f3552fcc2ddcd9b28a27b1e54b358b45874f88d1ca6888c"
-dependencies = [
- "bitflags",
- "crossterm_winapi",
- "lazy_static",
- "libc",
- "mio",
- "parking_lot",
- "signal-hook",
- "winapi",
-]
-
-[[package]]
-name = "crossterm_winapi"
-version = "0.7.0"
+name = "either"
+version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0da8964ace4d3e4a044fd027919b2237000b24315a37c916f61809f1ff2140b9"
-dependencies = [
- "winapi",
-]
+checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "funty"
@@ -100,9 +75,9 @@ checksum = "fed34cd105917e91daa4da6b3728c47b068749d6a62c59811f06ed2ac71d9da7"
[[package]]
name = "hermit-abi"
-version = "0.1.18"
+version = "0.1.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
+checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33"
dependencies = [
"libc",
]
@@ -114,74 +89,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
[[package]]
-name = "instant"
-version = "0.1.9"
+name = "itertools"
+version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
+checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
dependencies = [
- "cfg-if",
+ "either",
]
[[package]]
-name = "lazy_static"
-version = "1.4.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
-
-[[package]]
name = "libc"
-version = "0.2.94"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "18794a8ad5b29321f790b55d93dfba91e125cb1a9edbd4f8e3150acc771c1a5e"
-
-[[package]]
-name = "lock_api"
-version = "0.4.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0382880606dff6d15c9476c416d18690b72742aa7b605bb6dd6ec9030fbf07eb"
-dependencies = [
- "scopeguard",
-]
-
-[[package]]
-name = "log"
-version = "0.4.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
-dependencies = [
- "cfg-if",
-]
-
-[[package]]
-name = "mio"
-version = "0.7.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf80d3e903b34e0bd7282b218398aec54e082c840d9baf8339e0080a0c542956"
-dependencies = [
- "libc",
- "log",
- "miow",
- "ntapi",
- "winapi",
-]
-
-[[package]]
-name = "miow"
-version = "0.3.7"
+version = "0.2.112"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21"
-dependencies = [
- "winapi",
-]
-
-[[package]]
-name = "ntapi"
-version = "0.3.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44"
-dependencies = [
- "winapi",
-]
+checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
[[package]]
name = "num-traits"
@@ -204,31 +124,6 @@ dependencies = [
]
[[package]]
-name = "parking_lot"
-version = "0.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
-dependencies = [
- "instant",
- "lock_api",
- "parking_lot_core",
-]
-
-[[package]]
-name = "parking_lot_core"
-version = "0.8.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018"
-dependencies = [
- "cfg-if",
- "instant",
- "libc",
- "redox_syscall",
- "smallvec",
- "winapi",
-]
-
-[[package]]
name = "proc-macro2"
version = "0.4.30"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -239,9 +134,9 @@ dependencies = [
[[package]]
name = "proc-macro2"
-version = "1.0.26"
+version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a152013215dca273577e18d2bf00fa862b89b24169fb78c4c95aeb07992c9cec"
+checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
dependencies = [
"unicode-xid 0.2.2",
]
@@ -257,11 +152,11 @@ dependencies = [
[[package]]
name = "quote"
-version = "1.0.9"
+version = "1.0.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
+checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d"
dependencies = [
- "proc-macro2 1.0.26",
+ "proc-macro2 1.0.36",
]
[[package]]
@@ -271,64 +166,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "941ba9d78d8e2f7ce474c015eea4d9c6d25b6a3327f9832ee29a4de27f91bbb8"
[[package]]
-name = "redox_syscall"
-version = "0.2.8"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "742739e41cd49414de871ea5e549afb7e2a3ac77b589bcbebe8c82fab37147fc"
-dependencies = [
- "bitflags",
-]
-
-[[package]]
-name = "scopeguard"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
-
-[[package]]
name = "serde"
-version = "1.0.125"
+version = "1.0.133"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "558dc50e1a5a5fa7112ca2ce4effcb321b0300c0d4ccf0776a9f60cd89031171"
+checksum = "97565067517b60e2d1ea8b268e59ce036de907ac523ad83a0475da04e818989a"
[[package]]
name = "serde_derive"
-version = "1.0.125"
+version = "1.0.133"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b093b7a2bb58203b5da3056c05b4ec1fed827dcfdb37347a8841695263b3d06d"
+checksum = "ed201699328568d8d08208fdd080e3ff594e6c422e438b6705905da01005d537"
dependencies = [
- "proc-macro2 1.0.26",
- "quote 1.0.9",
- "syn 1.0.72",
+ "proc-macro2 1.0.36",
+ "quote 1.0.14",
+ "syn 1.0.84",
]
[[package]]
-name = "signal-hook"
-version = "0.1.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e31d442c16f047a671b5a71e2161d6e68814012b7f5379d269ebd915fac2729"
-dependencies = [
- "libc",
- "mio",
- "signal-hook-registry",
-]
-
-[[package]]
-name = "signal-hook-registry"
-version = "1.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16f1d0fef1604ba8f7a073c7e701f213e056707210e9020af4528e0101ce11a6"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "smallvec"
-version = "1.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
-
-[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -347,12 +201,12 @@ dependencies = [
[[package]]
name = "syn"
-version = "1.0.72"
+version = "1.0.84"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a1e8cdbefb79a9a5a65e0db8b47b723ee907b7c7f8496c76a1770b5c310bab82"
+checksum = "ecb2e6da8ee5eb9a61068762a32fa9619cc591ceb055b3687f4cd4051ec2e06b"
dependencies = [
- "proc-macro2 1.0.26",
- "quote 1.0.9",
+ "proc-macro2 1.0.36",
+ "quote 1.0.14",
"unicode-xid 0.2.2",
]
@@ -373,9 +227,9 @@ dependencies = [
[[package]]
name = "unicode-width"
-version = "0.1.8"
+version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
+checksum = "3ed742d4ea2bd1176e236172c8429aaf54486e7ac098db29ffe6529e0ce50973"
[[package]]
name = "unicode-xid"
@@ -435,10 +289,7 @@ dependencies = [
[[package]]
name = "yaxpeax-arch"
version = "0.2.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f1ba5c2f163fa2f866c36750c6c931566c6d93231ae9410083b0738953b609d5"
dependencies = [
- "crossterm",
"num-traits",
"serde",
"serde_derive",
@@ -471,6 +322,7 @@ version = "0.2.9"
dependencies = [
"clap",
"hex",
+ "itertools",
"num-traits",
"yaxpeax-6502",
"yaxpeax-arch",
@@ -530,27 +382,25 @@ dependencies = [
[[package]]
name = "yaxpeax-msp430"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "63962bd68624664aa3a3391785f95388001e9d6107cdf8557e962153e4a854c5"
+version = "0.1.1"
dependencies = [
"yaxpeax-arch",
]
[[package]]
name = "yaxpeax-pic17"
-version = "0.1.0"
+version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cb717c23e96ce83de1569c512ab5508a6e39a194a73662e618608f0f1c41ada4"
+checksum = "15ca63b323712143e563e146af6a3df21fa3c75976985ee7b157470616c15c1e"
dependencies = [
"yaxpeax-arch",
]
[[package]]
name = "yaxpeax-pic18"
-version = "0.1.0"
+version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cf1fb0d482f0f7d23394e31e50bce78d8320760c790d86e324c1a1e49710097a"
+checksum = "f64bb0d36fc65721e5919e9febe316ae62cf0ba2510cddb01d1f182ddcc97f84"
dependencies = [
"yaxpeax-arch",
]
@@ -571,7 +421,5 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "725dc639f981b8e4c222e96d64f577e2ccb46e908c807418d29b66578f6b34ba"
dependencies = [
"num-traits",
- "serde",
- "serde_derive",
"yaxpeax-arch",
]
diff --git a/Cargo.toml b/Cargo.toml
index ee63e0f..2cc050c 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,23 +13,27 @@ readme = "README.md"
name = "yaxdis"
path = "src/main.rs"
+[patch.crates-io]
+yaxpeax-arch-02 = { package = "yaxpeax-arch", path = "/toy/yaxpeax/yaxpeax-arch/" }
+
[dependencies]
clap = "2.33"
hex = "0.4.0"
num-traits = "0.2.10"
+itertools = "0.10.1"
# common interfaces for all yaxpeax decoders
-yaxpeax-arch-02 = { package = "yaxpeax-arch", version = "0.2.4" }
+yaxpeax-arch-02 = { package = "yaxpeax-arch", version = "0.2.4" , default-features = false, features = ["std"] }
yaxpeax-arm = { version = "0.2.3" }
yaxpeax-avr = { version = "0.1.0" }
yaxpeax-m16c = { version = "0.1.0" }
yaxpeax-mips = { version = "0.1.0" }
-yaxpeax-msp430 = { version = "0.1.0" }
+yaxpeax-msp430 = { version = "0.1.0", path = "/toy/yaxpeax/arch/msp430" }
yaxpeax-lc87 = { version = "1.0.0" }
yaxpeax-pic17 = { version = "0.1.0" }
yaxpeax-pic18 = { version = "0.1.0" }
-yaxpeax-x86 = { version = "1.1.3" }
+yaxpeax-x86 = { version = "1.1.3", default-features = false, features = ["fmt", "std"] }
yaxpeax-ia64 = { version = "0.2.1" }
yaxpeax-superh = { version = "0.3.0" }
yaxpeax-6502 = { version = "0.0.2", features = ["std"] }
diff --git a/src/main.rs b/src/main.rs
index d834785..19660b3 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -85,18 +85,18 @@ fn main() {
match arch_str {
"x86_64" |
- "x86:64" => arch_02::decode_input::<yaxpeax_x86::long_mode::Arch>(&buf, &printer),
+ "x86:64" => arch_02::decode_input_and_annotate::<yaxpeax_x86::long_mode::Arch>(&buf, &printer),
"x86_32" |
- "x86:32" => arch_02::decode_input::<yaxpeax_x86::protected_mode::Arch>(&buf, &printer),
+ "x86:32" => arch_02::decode_input_and_annotate::<yaxpeax_x86::protected_mode::Arch>(&buf, &printer),
"x86_16" |
- "x86:16" => arch_02::decode_input::<yaxpeax_x86::real_mode::Arch>(&buf, &printer),
+ "x86:16" => arch_02::decode_input_and_annotate::<yaxpeax_x86::real_mode::Arch>(&buf, &printer),
"ia64" => arch_02::decode_input::<yaxpeax_ia64::IA64>(&buf, &printer),
"avr" => arch_02::decode_input::<yaxpeax_avr::AVR>(&buf, &printer),
"armv7" => arch_02::decode_input::<yaxpeax_arm::armv7::ARMv7>(&buf, &printer),
"armv7-t" => arch_02::decode_armv7_thumb(&buf, &printer),
"armv8" => arch_02::decode_input::<yaxpeax_arm::armv8::a64::ARMv8>(&buf, &printer),
"mips" => arch_02::decode_input::<yaxpeax_mips::MIPS>(&buf, &printer),
- "msp430" => arch_02::decode_input::<yaxpeax_msp430::MSP430>(&buf, &printer),
+ "msp430" => arch_02::decode_input_and_annotate::<yaxpeax_msp430::MSP430>(&buf, &printer),
"pic17" => arch_02::decode_input::<yaxpeax_pic17::PIC17>(&buf, &printer),
"pic18" => arch_02::decode_input::<yaxpeax_pic18::PIC18>(&buf, &printer),
"m16c" => arch_02::decode_input::<yaxpeax_m16c::M16C>(&buf, &printer),
@@ -161,12 +161,6 @@ struct Printer {
verbose: bool,
}
-struct InstDetails<I: fmt::Display + fmt::Debug> {
- pub inst_len: usize,
- pub well_defined: bool,
- pub inst: I,
-}
-
impl Printer {
// shared generic function to keep display logic consistent regardless of yaxpeax-arch version
fn print_instr<I, E>(&self, rest: &[u8], addr: usize, inst_res: Result<InstDetails<I>, E>)
@@ -178,14 +172,26 @@ impl Printer {
let mut stdout = self.stdout.lock();
write!(stdout, "{:#010x}: ", addr).unwrap();
match inst_res {
- Ok(InstDetails { inst_len, well_defined, inst }) => {
+ Ok(InstDetails { inst_len, well_defined, inst, field_descriptions }) => {
writeln!(stdout, "{:14}: {}", hex::encode(&rest[..inst_len]), inst)
.unwrap();
if self.verbose {
- writeln!(stdout, " {:?}", inst).unwrap();
if !well_defined {
writeln!(stdout, " not well-defined").unwrap();
}
+
+ // if we can show detailed information about the instruction's interpretation,
+ // do that. otherwise, debug impl of the instruction and hope for the best.
+ if let Some((mapper, fields)) = field_descriptions {
+ let bits_layout = fmt_field_descriptions(
+ &mapper,
+ &fields,
+ &rest[..inst_len]
+ );
+ write!(stdout, "{}", bits_layout).unwrap();
+ } else {
+ writeln!(stdout, " {:?}", inst).unwrap();
+ }
}
}
Err(e) => {
@@ -195,6 +201,13 @@ impl Printer {
}
}
+struct InstDetails<I: fmt::Debug + fmt::Display> {
+ inst_len: usize,
+ well_defined: bool,
+ inst: I,
+ field_descriptions: Option<(BitPosition, Vec<FieldRecord>)>,
+}
+
// yaxpeax-arch, implemented by all decoders here, may be required at incompatible versions by
// different decoders if/when a new version releases. implement the actual decode-and-print
// behavior independent of yaxpeax-arch so decoders using different version can exist in parallel.
@@ -205,6 +218,10 @@ mod arch_02 {
use yaxpeax_arch_02::{
AddressBase, Arch, Decoder, Instruction, LengthedInstruction, Reader, U8Reader,
};
+ use yaxpeax_arch_02::annotation::{AnnotatingDecoder, FieldDescription, VecSink};
+
+ use crate::{FieldRecord, ItemDescription};
+
pub(crate) fn decode_input<A: Arch>(buf: &[u8], printer: &Printer)
where
@@ -214,6 +231,15 @@ mod arch_02 {
decode_input_with_decoder::<A>(A::Decoder::default(), buf, printer);
}
+ pub(crate) fn decode_input_and_annotate<A: Arch + crate::ArchBitMapper>(buf: &[u8], printer: &Printer)
+ where
+ A::Instruction: fmt::Display,
+ A::Decoder: AnnotatingDecoder<A>,
+ for<'data> U8Reader<'data>: Reader<A::Address, A::Word>,
+ {
+ decode_input_with_annotation::<A>(A::Decoder::default(), buf, printer);
+ }
+
pub(crate) fn decode_armv7_thumb(buf: &[u8], printer: &Printer) {
let decoder = yaxpeax_arm::armv7::InstDecoder::default_thumb();
decode_input_with_decoder::<yaxpeax_arm::armv7::ARMv7>(decoder, buf, printer);
@@ -240,10 +266,356 @@ mod arch_02 {
inst_len: A::Address::zero().wrapping_offset(inst.len()).to_linear(),
well_defined: inst.well_defined(),
inst,
+ field_descriptions: None,
}
});
printer.print_instr(rest, addr.to_linear(), generic_res);
addr += advance_addr;
}
}
+
+ fn field_descs_to_record<A: Arch + crate::ArchBitMapper>(sink: VecSink<<A::Decoder as AnnotatingDecoder<A>>::FieldDescription>) -> Vec<FieldRecord> where A::Decoder: AnnotatingDecoder<A> {
+ let mut fields: Vec<FieldRecord> = Vec::new();
+ let bit_mapper = A::mapper();
+
+ use itertools::Itertools;
+ let mut vs = sink.records;
+ vs.sort_by_key(|rec| rec.2.id());
+ for (id, group) in &vs.iter().group_by(|x| x.2.id()) {
+ let mut field = FieldRecord {
+ elements: Vec::new(),
+ id: id,
+ };
+
+ for (desc, spans) in &group.group_by(|x| x.2.to_owned()) {
+ let mut item = ItemDescription {
+ ranges: Vec::new(),
+ description: desc.to_string(),
+ separator: desc.is_separator(),
+ };
+
+ for span in spans {
+ item.ranges.push(crate::BitRange::across(bit_mapper, span.0, span.1));
+ }
+ field.elements.push(item);
+ }
+ fields.push(field);
+ }
+
+ fields
+ }
+
+ pub(crate) fn decode_input_with_annotation<A: Arch + crate::ArchBitMapper>(
+ decoder: A::Decoder,
+ buf: &[u8],
+ printer: &Printer,
+ ) where
+ A::Instruction: fmt::Display,
+ A::Decoder: AnnotatingDecoder<A>,
+ for<'data> U8Reader<'data>: Reader<A::Address, A::Word>,
+ {
+ let mut addr = A::Address::zero();
+ while let Some(rest) = buf.get(addr.to_linear()..).filter(|v| !v.is_empty()) {
+ let mut sink: VecSink<<A::Decoder as AnnotatingDecoder<A>>::FieldDescription> = VecSink::new();
+ let mut reader = U8Reader::new(rest);
+ let mut inst = A::Instruction::default();
+ let res = decoder.decode_with_annotation(&mut inst, &mut reader, &mut sink);
+ let advance_addr = match &res {
+ Ok(_) => inst.len(),
+ Err(_) => A::Instruction::min_size(),
+ };
+ let generic_res = res.map(|_| {
+ let records = field_descs_to_record::<A>(sink);
+ crate::InstDetails {
+ inst_len: A::Address::zero().wrapping_offset(inst.len()).to_linear(),
+ well_defined: inst.well_defined(),
+ inst,
+ field_descriptions: Some((A::mapper(), records)),
+ }
+ });
+ printer.print_instr(rest, addr.to_linear(), generic_res);
+ addr += advance_addr;
+ }
+ }
+}
+
+/// any architecture with an `AnnotatingDecoder` implementation will have annotations reported at
+/// positions of bits in the instruction. `yaxpeax-dis` requires some description of how to convert
+/// between a column and a bit for a given architecture.
+#[derive(Copy, Clone, Debug)]
+struct BitPosition {
+ word_size: usize,
+}
+
+impl BitPosition {
+ fn col2bit(&self, col: usize) -> usize {
+ let word = col / self.word_size;
+ let bit = (self.word_size - 1) - (col % self.word_size);
+ let bit = word * self.word_size + bit;
+ bit
+ }
+
+ fn bit2col(&self, bit: usize) -> usize {
+ let word = bit / self.word_size;
+ let col = (self.word_size - 1) - (bit % self.word_size);
+ let col = word * self.word_size + col;
+ col
+ }
+}
+
+const IA64_POSITIONS: BitPosition = BitPosition {
+ word_size: 128
+};
+
+const WORD_POSITIONS: BitPosition = BitPosition {
+ word_size: 16
+};
+
+const BYTE_POSITIONS: BitPosition = BitPosition {
+ word_size: 8
+};
+
+trait ArchBitMapper {
+ fn mapper() -> BitPosition;
+}
+
+impl ArchBitMapper for yaxpeax_x86::real_mode::Arch {
+ fn mapper() -> BitPosition {
+ BYTE_POSITIONS
+ }
+}
+
+impl ArchBitMapper for yaxpeax_x86::protected_mode::Arch {
+ fn mapper() -> BitPosition {
+ BYTE_POSITIONS
+ }
+}
+
+impl ArchBitMapper for yaxpeax_x86::long_mode::Arch {
+ fn mapper() -> BitPosition {
+ BYTE_POSITIONS
+ }
+}
+
+impl ArchBitMapper for yaxpeax_msp430::MSP430 {
+ fn mapper() -> BitPosition {
+ WORD_POSITIONS
+ }
+}
+
+impl ArchBitMapper for yaxpeax_ia64::IA64 {
+ fn mapper() -> BitPosition {
+ IA64_POSITIONS
+ }
+}
+
+#[derive(Debug)]
+struct BitRange {
+ start: u32,
+ end: u32,
+ lhs: u32,
+ rhs: u32,
+}
+
+impl BitRange {
+ fn across(bit_mapper: BitPosition, start: u32, end: u32) -> BitRange {
+ let mut lhs = bit_mapper.bit2col(start as usize) as u32;
+ let mut rhs = bit_mapper.bit2col(start as usize) as u32;
+ for bit in start..=end {
+ lhs = std::cmp::min(lhs, bit_mapper.bit2col(bit as usize) as u32);
+ rhs = std::cmp::max(rhs, bit_mapper.bit2col(bit as usize) as u32);
+ }
+ BitRange { start, end, lhs, rhs }
+ }
+}
+
+/// a representation of a decoder's `Annotation` type that does not actually reference
+/// `yaxpeax_arch`. this is important so we can have a whole shared display routine reused across
+/// `yaxpeax_arch` versions - there may be more than one in use at a time in `yaxpeax-dis`.
+struct ItemDescription {
+ ranges: Vec<BitRange>,
+ description: String,
+ separator: bool,
+}
+
+impl fmt::Debug for ItemDescription {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{{ ranges: {:?}, description: {}, separator: {} }}", &self.ranges, &self.description, self.separator)
+ }
+}
+
+// spans grouped together in some decoder-specified logical structure by
+// `id`. `id` is a hint that data should be considered related for display
+// purposes.
+struct FieldRecord {
+ // spans grouped together by `FieldDescription` - one field may be
+ // described by multiple distinct spans, so those spans are recorded
+ // here. elements are ordered by the lowest bit of spans describing an
+ // element.
+ elements: Vec<ItemDescription>,
+ id: u32,
+}
+
+impl fmt::Debug for FieldRecord {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{{ elements: {:?}, id: {} }}", &self.elements, &self.id)
+ }
+}
+
+fn fmt_field_descriptions(bit_mapper: &BitPosition, fields: &[FieldRecord], data: &[u8]) -> String {
+ let mut boundaries = [false; 256];
+ let mut separators = [false; 256];
+ let mut bits = [false; 256];
+ let mut rhs = [false; 256];
+ let mut lhs = [false; 256];
+ let mut field_order: Vec<(usize, usize)> = Vec::new();
+ let mut boundary_order: Vec<(usize, usize)> = Vec::new();
+
+ for (fi, field) in fields.iter().enumerate() {
+ for (ei, element) in field.elements.iter().enumerate() {
+ if element.separator {
+ for (_ri, range) in element.ranges.iter().enumerate() {
+ boundaries[range.start as usize + 1] = true;
+ boundary_order.push((fi, range.start as usize + 1));
+ }
+ continue;
+ }
+ field_order.push((fi, ei));
+ for (_ri, range) in element.ranges.iter().enumerate() {
+ for i in range.start..=range.end {
+ bits[i as usize] = true;
+ }
+ separators[range.start as usize] = true;
+ lhs[range.lhs as usize] = true;
+ rhs[range.rhs as usize] = true;
+ }
+ }
+ }
+ boundary_order.sort_by(|l, r| r.1.cmp(&l.1));
+
+ // regardless of sections, the left-hand side of the terminal is a free boundary
+ lhs[0] = false;
+
+ let mut res = String::new();
+ res.push_str(" \n");
+
+ let mut fudge_bits = [false; 160];
+
+ for i in 0..160 {
+ if (i >> 3) >= data.len() {
+ continue;
+ }
+
+ let mut fudge = false;
+
+ if lhs[i] {
+ fudge = true;
+ }
+
+ if i > 0 && rhs[i - 1] {
+ fudge = true;
+ }
+
+ if fudge {
+ fudge_bits[i] = true;
+ }
+ }
+
+ let mut fudge = 0;
+ let mut col = [b' '; 160];
+
+ for i in 0..160 {
+ if (i >> 3) >= data.len() {
+ continue;
+ }
+
+ let bit = bit_mapper.col2bit(i);
+
+ if fudge_bits[i] {
+ fudge += 1;
+ }
+
+ if data[(bit >> 3) as usize] & (1 << (bit as u8 & 7)) != 0 {
+ col[i + fudge] = b'1';
+ } else {
+ col[i + fudge] = b'0';
+ }
+ }
+ res.push_str(unsafe { std::str::from_utf8_unchecked(&col) });
+ res.push_str("\n");
+
+ for (fi, ei) in field_order.iter() {
+ let mut col = [b' '; 160];
+
+ for range in &fields[*fi as usize].elements[*ei as usize].ranges {
+ let mut fudge = 0;
+
+ for c in 0..128 {
+ let bit = bit_mapper.col2bit(c as usize);
+
+ if boundaries[c] {
+ col[c + fudge] = b'|';
+ }
+ if fudge_bits[c as usize] {
+ fudge += 1;
+ }
+
+ if bit >= range.start as usize && bit <= range.end as usize {
+ let data_bit = data[(bit >> 3) as usize] & (1 << (bit as u8 & 7)) != 0;
+ col[c as usize + fudge] = if data_bit { b'1' } else { b'0' };
+ }
+ }
+ }
+
+ res.push_str(unsafe { std::str::from_utf8_unchecked(&col[..(data.len() * 8 + 30)]) });
+ res.push_str(" ");
+ res.push_str(&fields[*fi as usize].elements[*ei as usize].description);
+ res.push_str("\n");
+ }
+
+ let mut fudge = 0;
+ let mut col = [b' '; 160];
+
+ let mut line_end = 0;
+ for i in 0..160 {
+ if (i >> 3) > data.len() {
+ continue;
+ }
+
+ if boundaries[i] {
+ col[i + fudge] = b'|';
+ line_end = i + fudge + 1;
+ }
+ if fudge_bits[i] {
+ fudge += 1;
+ }
+ }
+ res.push_str(unsafe { std::str::from_utf8_unchecked(&col[..line_end]) });
+ res.push_str("\n");
+
+ for (field_index, bit) in boundary_order {
+ let mut fudge = 0;
+ let mut col = [b' '; 160];
+
+ for i in 0..160 {
+ if (i >> 3) > data.len() {
+ continue;
+ }
+
+ if i == bit {
+ res.push_str(unsafe { std::str::from_utf8_unchecked(&col[..i + fudge]) });
+ break;
+ }
+
+ if boundaries[i] {
+ col[i + fudge] = b'|';
+ }
+ if fudge_bits[i] {
+ fudge += 1;
+ }
+ }
+ res.push_str("\n");
+ }
+
+ res
}