diff options
author | iximeow <me@iximeow.net> | 2022-01-03 14:06:57 -0800 |
---|---|---|
committer | iximeow <me@iximeow.net> | 2022-01-03 14:06:57 -0800 |
commit | 0759de116479c8a6319450e1e116af39e8c844c5 (patch) | |
tree | 38b806c8c3035faf99c88ada34bb7eab4309c4e3 | |
parent | c9a266cd62713f2ff7f5cf637adafd685ee17f16 (diff) | |
parent | fe9c41db5e4f2916439dd268a1b5e65447396ce3 (diff) |
architecture-generic bit layouts of instructions
-rw-r--r-- | Cargo.lock | 238 | ||||
-rw-r--r-- | Cargo.toml | 10 | ||||
-rw-r--r-- | src/main.rs | 396 |
3 files changed, 434 insertions, 210 deletions
@@ -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", ] @@ -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 } |