diff options
| author | iximeow <me@iximeow.net> | 2026-05-25 17:32:57 +0000 |
|---|---|---|
| committer | iximeow <me@iximeow.net> | 2026-05-25 18:00:30 +0000 |
| commit | f2a2a09688421f2c532ab6f02527bf68f095407a (patch) | |
| tree | e7b5c3c5302c4c1a4ef89c1a5852b50059850986 /src/long_mode | |
| parent | 94f39674e0aee55ff6f6bb59f7d7c4873d2d1a6c (diff) | |
j*cxz/pusha/popa alternate size forms
these all existed since forever but the library did not distinguish them
and did not provide prefix information for users to tell which had been
decoded.
Diffstat (limited to 'src/long_mode')
| -rw-r--r-- | src/long_mode/behavior.rs | 12 | ||||
| -rw-r--r-- | src/long_mode/display.rs | 12 | ||||
| -rw-r--r-- | src/long_mode/mod.rs | 22 |
3 files changed, 39 insertions, 7 deletions
diff --git a/src/long_mode/behavior.rs b/src/long_mode/behavior.rs index 3e1b4a7..478c07a 100644 --- a/src/long_mode/behavior.rs +++ b/src/long_mode/behavior.rs @@ -229,8 +229,7 @@ impl Instruction { } } else if self.opcode() == Opcode::LOOPNZ || self.opcode() == Opcode::LOOPZ - || self.opcode() == Opcode::LOOP - || self.opcode() == Opcode::JRCXZ { + || self.opcode() == Opcode::LOOP { if self.prefixes.rex_unchecked().w() { behavior = behavior .set_implicit_ops(RW_RCX_IDX); @@ -3559,7 +3558,7 @@ fn behavior_table_size_is_right() { } /// this table MUST line up with Opcode declaration order in `mod.rs`. -static TABLE: [BehaviorDigest; 1413] = [ +static TABLE: [BehaviorDigest; 1414] = [ /* ADD => */ GENERAL_RW_R_FLAGWRITE, /* OR => */ GENERAL_RW_R_FLAGWRITE, /* ADC => */ GENERAL_RW_R_FLAGRW, @@ -5167,7 +5166,7 @@ static TABLE: [BehaviorDigest; 1413] = [ /* JRCXZ => */ BehaviorDigest::empty() .set_pl_any() .set_operand(0, Access::Read) - .set_nontrivial(true), + .set_implicit_ops(RW_RCX_IDX), // started shipping in Tremont, 2020 sept 23 // while this instruction is marked "write, read", the written first operand is a register @@ -5871,4 +5870,9 @@ static TABLE: [BehaviorDigest; 1413] = [ .set_pl0() .set_flags_access(Access::Write) .set_complex(true), + + /* JECXZ => */ BehaviorDigest::empty() + .set_pl_any() + .set_operand(0, Access::Read) + .set_implicit_ops(RW_ECX_IDX), ]; diff --git a/src/long_mode/display.rs b/src/long_mode/display.rs index f215d07..a00dd22 100644 --- a/src/long_mode/display.rs +++ b/src/long_mode/display.rs @@ -2100,6 +2100,8 @@ const MNEMONICS: &[&'static str] = &[ "pvalidate", "rmpadjust", "rmpupdate", + + "jecxz", ]; impl Opcode { @@ -2699,6 +2701,7 @@ impl <T: fmt::Write, Y: YaxColors> Colorize<T, Y> for Opcode { Opcode::LOOPZ | Opcode::LOOP | Opcode::JRCXZ | + Opcode::JECXZ | Opcode::CALL | Opcode::CALLF | Opcode::JMP | @@ -4018,6 +4021,10 @@ pub(crate) fn contextualize_c<T: DisplaySink>(instr: &Instruction, out: &mut T) out.write_str("if rcx == 0 then jmp ")?; write_jmp_operand(instr.operand(0), out)?; }, + Opcode::JECXZ => { + out.write_str("if ecx == 0 then jmp ")?; + write_jmp_operand(instr.operand(0), out)?; + }, Opcode::LOOP => { out.write_str("rcx--; if rcx != 0 then jmp ")?; write_jmp_operand(instr.operand(0), out)?; @@ -4216,8 +4223,9 @@ impl <T: fmt::Write, Y: YaxColors> ShowContextual<u64, [Option<alloc::string::St } } -static RELATIVE_BRANCHES: [Opcode; 22] = [ - Opcode::JMP, Opcode::CALL, Opcode::JRCXZ, +static RELATIVE_BRANCHES: [Opcode; 23] = [ + Opcode::JMP, Opcode::CALL, + Opcode::JRCXZ, Opcode::JECXZ, Opcode::LOOP, Opcode::LOOPZ, Opcode::LOOPNZ, Opcode::JO, Opcode::JNO, Opcode::JB, Opcode::JNB, diff --git a/src/long_mode/mod.rs b/src/long_mode/mod.rs index c477b16..f2fac5b 100644 --- a/src/long_mode/mod.rs +++ b/src/long_mode/mod.rs @@ -2593,6 +2593,9 @@ pub enum Opcode { PVALIDATE, RMPADJUST, RMPUPDATE, + + // this was present since initial x86-64 but the library missed the prefix handling.. + JECXZ, } impl PartialEq for Instruction { @@ -4047,6 +4050,7 @@ enum OperandCase { ModRM_0xf30f38fa, ModRM_0xf30f38fb, ModRM_0xf30f3af0, + CXZ, } #[allow(non_camel_case_types)] @@ -4381,6 +4385,7 @@ enum OperandCode { Ev_G_xmm_Ib = OperandCodeBuilder::new().read_E().operand_case(OperandCase::Ev_G_xmm_Ib).bits(), G_E_mm_Ib = OperandCodeBuilder::new().read_E().operand_case(OperandCase::G_E_mm_Ib).bits(), MASKMOVDQU = OperandCodeBuilder::new().read_E().reg_mem().operand_case(OperandCase::MASKMOVDQU).bits(), + CXZ = OperandCodeBuilder::new().operand_case(OperandCase::CXZ).bits(), } fn base_opcode_map(v: u8) -> Opcode { @@ -4684,7 +4689,7 @@ const OPCODES: [OpcodeRecord; 256] = [ OpcodeRecord::new(Interpretation::Instruction(Opcode::LOOPNZ), OperandCode::Ibs), OpcodeRecord::new(Interpretation::Instruction(Opcode::LOOPZ), OperandCode::Ibs), OpcodeRecord::new(Interpretation::Instruction(Opcode::LOOP), OperandCode::Ibs), - OpcodeRecord::new(Interpretation::Instruction(Opcode::JRCXZ), OperandCode::Ibs), + OpcodeRecord::new(Interpretation::Instruction(Opcode::JRCXZ), OperandCode::CXZ), OpcodeRecord::new(Interpretation::Instruction(Opcode::IN), OperandCode::AL_Ib), OpcodeRecord::new(Interpretation::Instruction(Opcode::IN), OperandCode::AX_Ib), OpcodeRecord::new(Interpretation::Instruction(Opcode::OUT), OperandCode::Ib_AL), @@ -9117,6 +9122,21 @@ fn read_operands< instruction.regs[0].bank = RegisterBank::Q; }; } + OperandCase::CXZ => { + if instruction.prefixes.address_size() { + // address-size overridden from 64-bit to 32-bit + instruction.opcode = Opcode::JECXZ; + } + instruction.imm = + read_imm_signed(words, 1)? as u64; + sink.record( + words.offset() as u32 * 8 - 8, + words.offset() as u32 * 8 - 1, + InnerDescription::Number("1-byte immediate", instruction.imm as i64) + .with_id(words.offset() as u32 * 8), + ); + instruction.operands[0] = OperandSpec::ImmI8; + }, }; Ok(()) } |
