diff options
-rw-r--r-- | CHANGELOG | 7 | ||||
-rw-r--r-- | differential-tests/tests/capstone-differential.rs | 73 | ||||
-rw-r--r-- | src/armv8/a64.rs | 20 | ||||
-rw-r--r-- | tests/armv8/a64.rs | 34 |
4 files changed, 119 insertions, 15 deletions
@@ -1,3 +1,10 @@ +## 0.3.3 + +* fix some issues with tag instruction decoding + - the raw tag offset was reported, rather than the shifted-by-four value that + corresponds to a real address + - ldg was displayed as "ldm" (thank you @martin-fink!) + ## 0.3.2 * fully support PAC instructions diff --git a/differential-tests/tests/capstone-differential.rs b/differential-tests/tests/capstone-differential.rs index 3602ac5..9f85e83 100644 --- a/differential-tests/tests/capstone-differential.rs +++ b/differential-tests/tests/capstone-differential.rs @@ -523,16 +523,6 @@ fn capstone_differential() { panic!("text: {}, bytes: {:?}", yax_text, bytes); } - if cs_text.contains("stz2g ") || cs_text.contains("stzg ") || cs_text.contains("st2g ") || cs_text.contains("stg ") || cs_text.contains("irg ") || cs_text.contains("ssbb") { - // TODO yax might not scale the offset right? - continue; - } - - if cs_text.contains("ldg ") { - // TODO: yax says ldm, cs says ldg - continue; - } - if cs_text.contains("dfb ") { // TODO: yax and cs disagree? continue; @@ -683,9 +673,9 @@ fn capstone_differential() { if cs_text.starts_with("sev ") { return true; } -// if yax_text.starts_with("hint ") { -// return true; -// } + if yax_text.starts_with("hint ") { + return true; + } // fmlal v0.2s, v0.4h, v0.h[0] != fmlal v0.2s, v0.2h, v0.h[0]. bytes: [0, // 0, 80, f] @@ -694,13 +684,68 @@ fn capstone_differential() { return true; } + if cs_text.starts_with("irg") && yax_text.starts_with(cs_text) && yax_text.ends_with(", xzr") { + // a trailing xzr is implicit in irg if omitted in the instruction + // text. yax includes xzr in this case, capstone does not. + return true; + } + + const TAG_INSTRUCTIONS: [&'static str; 8] = [ + "stzgm", "stg", "ldg", "stzg", + "stgm", "st2g", "ldgm", "stz2g" + ]; + + for mnemonic in TAG_INSTRUCTIONS { + if parsed_yax.opcode != mnemonic || parsed_cs.opcode != mnemonic { + continue; + } + + for (yax_op, cs_op) in parsed_yax.operands.iter().zip(parsed_cs.operands.iter()) { + if yax_op == cs_op { + continue; + } + match (yax_op, cs_op) { + ( + Some(ParsedOperand::MemoryWithOffset { + base: cs_base, offset: Some(cs_offset), writeback: cs_writeback + }), + Some(ParsedOperand::MemoryWithOffset { + base: yax_base, offset: Some(yax_offset), writeback: yax_writeback + }) + ) => { + if cs_base != yax_base || cs_writeback != yax_writeback { + // a pretty fundamental decode mismatch.. + return false; + } + + if *cs_offset != *yax_offset && *yax_offset < 0 { + // capstone decodes offsets as an unsigned integer, not + // clamping to the signed range -4096 to 4080. yaxpeax + // decodes the operand into this range. + if (!*yax_offset + 1) & 0x1fff != *cs_offset { + return false; + } + } + } + _ => { + // some operands differ + return false; + } + } + } + + // operand matching above looks OK, opcodes are the same, we're good + // here. + return true; + } + return false; } // eprintln!("{}", yax_text); if !acceptable_match(&yax_text, &cs_text) { eprintln!("disassembly mismatch: {} != {}. bytes: {:x?}", yax_text, cs_text, bytes); -// std::process::abort(); + std::process::abort(); } else { stats.good.fetch_add(1, Ordering::Relaxed); } diff --git a/src/armv8/a64.rs b/src/armv8/a64.rs index 56d9ef2..8775d77 100644 --- a/src/armv8/a64.rs +++ b/src/armv8/a64.rs @@ -1263,7 +1263,10 @@ pub enum Opcode { DSB(u8), DMB(u8), SB, + #[deprecated(since = "0.4.0", note = "i spelled `ssbb` incorrectly.")] SSSB, + PSSBB, + SSBB, HINT, CLREX, CSEL, @@ -1867,6 +1870,10 @@ impl Display for Opcode { Opcode::MRS => "mrs", Opcode::ISB => "isb", Opcode::SB => "sb", + Opcode::SSBB => "ssbb", + Opcode::PSSBB => "pssbb", + // this arm should never, never, never be hit + #[allow(deprecated)] Opcode::SSSB => "sssb", Opcode::CLREX => "clrex", Opcode::CSEL => "csel", @@ -8669,6 +8676,9 @@ impl Decoder<ARMv8> for InstDecoder { let Rt = ((word >> 0) & 0b11111) as u16; let simm = (((imm9 as i16) << 7) >> 7) as i32; + // tag granularity is 16 bytes, so tags are encoded with the low four + // (`LOG2_TAG_GRANULE `) zeroes shifted out for all tag instructions. + let simm = simm << 4; let opcode = &[ Opcode::STZGM, Opcode::STG, Opcode::STG, Opcode::STG, @@ -10597,7 +10607,15 @@ impl Decoder<ARMv8> for InstDecoder { }, 0b100 => { if CRm == 0b0000 { - inst.opcode = Opcode::SSSB; + inst.opcode = Opcode::SSBB; + inst.operands = [ + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + Operand::Nothing, + ]; + } else if CRm == 0b0100 { + inst.opcode = Opcode::PSSBB; inst.operands = [ Operand::Nothing, Operand::Nothing, diff --git a/tests/armv8/a64.rs b/tests/armv8/a64.rs index baf841b..81d4a2e 100644 --- a/tests/armv8/a64.rs +++ b/tests/armv8/a64.rs @@ -83,6 +83,21 @@ fn test_barrier() { test_display([0xbf, 0x3f, 0x03, 0xd5], "dmb sy"); // only with FEAT_SB test_display([0xff, 0x30, 0x03, 0xd5], "sb"); + + test_display([0x9f, 0x34, 0x03, 0xd5], "pssbb"); + + // when printing the instruction the third operand defaults to xzr if omitted, so yax probably + // could/should omit it. but it's not *wrong*.. + test_display([0x00, 0x10, 0xdf, 0x9a], "irg x0, x0, xzr"); + test_display([0x90, 0x10, 0xdf, 0x9a], "irg x16, x4, xzr"); + test_display([0x90, 0x10, 0xcf, 0x9a], "irg x16, x4, x15"); + + test_display([0x00, 0x10, 0x60, 0xd9], "ldg x0, [x0, #0x10]"); + test_display([0x90, 0x10, 0x60, 0xd9], "ldg x16, [x4, #0x10]"); + test_display([0x90, 0x90, 0x60, 0xd9], "ldg x16, [x4, #0x90]"); + // the immediate offfset in tag instructions is a signed offset in the range of -4096 to 4096. + // yax decodes it as signed in this range, capstone does not. + test_display([0x90, 0x90, 0x7f, 0xd9], "ldg x16, [x4, #-0x70]"); } #[test] @@ -5026,3 +5041,22 @@ fn test_bitfield() { assert!(errs.is_empty()); } + +#[test] +fn test_tags() { + const TESTS: &[([u8; 4], &'static str)] = &[ + ([0x00, 0x10, 0x60, 0xd9], "ldg x0, [x0, #0x10]"), + ([0x00, 0x20, 0x60, 0xd9], "ldg x0, [x0, #0x20]"), + ([0x00, 0x21, 0x60, 0xd9], "ldg x0, [x8, #0x20]"), + ([0x03, 0x21, 0x60, 0xd9], "ldg x3, [x8, #0x20]"), + ([0x03, 0x21, 0x7f, 0xd9], "ldg x3, [x8, #-0xe0]"), + ]; + + let errs = run_tests(TESTS); + + for err in errs.iter() { + println!("{}", err); + } + + assert!(errs.is_empty()); +} |