diff options
| author | iximeow <me@iximeow.net> | 2026-05-25 19:21:31 +0000 |
|---|---|---|
| committer | iximeow <me@iximeow.net> | 2026-05-25 19:24:01 +0000 |
| commit | fe6b2b898aee944ba3490c35f4aed4d155485c0e (patch) | |
| tree | 984496e7ec908c43272d591db12a566c49ea07a2 | |
| parent | b43f483a50f650dbf385d2d55d0d38171f08e32b (diff) | |
push/pop width in 16/32-bit modes are receptive to operand width prefix
| -rw-r--r-- | src/protected_mode/behavior.rs | 72 | ||||
| -rw-r--r-- | src/protected_mode/mod.rs | 3 | ||||
| -rw-r--r-- | src/real_mode/behavior.rs | 68 | ||||
| -rw-r--r-- | src/real_mode/mod.rs | 51 | ||||
| -rw-r--r-- | test/protected_mode/operand.rs | 4 | ||||
| -rw-r--r-- | test/real_mode/operand.rs | 4 |
6 files changed, 184 insertions, 18 deletions
diff --git a/src/protected_mode/behavior.rs b/src/protected_mode/behavior.rs index 22d1f9e..48b700b 100644 --- a/src/protected_mode/behavior.rs +++ b/src/protected_mode/behavior.rs @@ -129,6 +129,16 @@ impl Instruction { .set_operand(1, Access::Read) .set_operand(2, Access::Read); } + } else if self.opcode == Opcode::PUSH { + if self.prefixes.operand_size() { + behavior = behavior + .set_implicit_ops(PUSHS_OPS_IDX); + } + } else if self.opcode == Opcode::PUSHF { + if self.prefixes.operand_size() { + behavior = behavior + .set_implicit_ops(PUSHFS_OPS_IDX); + } } else if self.opcode == Opcode::DIV || self.opcode == Opcode::IDIV { let op_width = if self.operands[0] == OperandSpec::RegMMM { self.regs[1].width() @@ -1914,7 +1924,29 @@ static PUSH_OPS: &'static [ImplicitOperand] = &[ ImplicitOperand { spec: OperandSpec::Disp, reg: RegSpec::esp(), - disp: -8i32, + disp: -4i32, + write: true, + }, + // push.. pushes the value (above), then does a RMW on rsp. + ImplicitOperand { + spec: OperandSpec::RegRRR, + reg: RegSpec::esp(), + disp: 0, + write: false, + }, + ImplicitOperand { + spec: OperandSpec::RegRRR, + reg: RegSpec::esp(), + disp: 0, + write: true, + } +]; + +static PUSHS_OPS: &'static [ImplicitOperand] = &[ + ImplicitOperand { + spec: OperandSpec::Disp, + reg: RegSpec::esp(), + disp: -2i32, write: true, }, // push.. pushes the value (above), then does a RMW on rsp. @@ -2081,7 +2113,35 @@ static PUSHF_OPS: &'static [ImplicitOperand] = &[ ImplicitOperand { spec: OperandSpec::Disp, reg: RegSpec::esp(), - disp: -8i32, + disp: -4i32, + write: true, + }, + // push.. pushes the value (above), then does a RMW on rsp. + ImplicitOperand { + spec: OperandSpec::RegRRR, + reg: RegSpec::esp(), + disp: 0, + write: false, + }, + ImplicitOperand { + spec: OperandSpec::RegRRR, + reg: RegSpec::esp(), + disp: 0, + write: true, + } +]; + +static PUSHFS_OPS: &'static [ImplicitOperand] = &[ + ImplicitOperand { + spec: OperandSpec::RegRRR, + reg: RegSpec::eflags(), + disp: 0, + write: false, + }, + ImplicitOperand { + spec: OperandSpec::Disp, + reg: RegSpec::esp(), + disp: -2i32, write: true, }, // push.. pushes the value (above), then does a RMW on rsp. @@ -3726,8 +3786,10 @@ const PUSHA_IDX: u16 = 74; const POPA_IDX: u16 = 75; const PUSHAD_IDX: u16 = 76; const POPAD_IDX: u16 = 77; +const PUSHS_OPS_IDX: u16 = 78; +const PUSHFS_OPS_IDX: u16 = 79; -static IMPLICIT_OPS_LIST: [&[ImplicitOperand]; 78] = [ +static IMPLICIT_OPS_LIST: [&[ImplicitOperand]; 80] = [ &[], // implicit ops list 0 is not used PUSH_OPS, POP_OPS, @@ -3806,6 +3868,8 @@ static IMPLICIT_OPS_LIST: [&[ImplicitOperand]; 78] = [ POPA_OPS, PUSHAD_OPS, POPAD_OPS, + PUSHS_OPS, + PUSHFS_OPS, ]; fn opcode2behavior(opc: &Opcode) -> BehaviorDigest { @@ -3942,6 +4006,7 @@ static TABLE: [BehaviorDigest; 1428] = [ .set_operand(0, Access::Read), /* PUSH => */ BehaviorDigest::empty() .set_implicit_ops(PUSH_OPS_IDX) + .set_nontrivial(true) .set_pl_any() .set_operand(0, Access::Read), /* POP => */ BehaviorDigest::empty() @@ -3989,6 +4054,7 @@ static TABLE: [BehaviorDigest; 1428] = [ .set_pl_any(), /* PUSHF => */ BehaviorDigest::empty() .set_implicit_ops(PUSHF_IDX) + .set_nontrivial(true) .set_pl_any(), /* WAIT => */ GENERAL, /* CBW => */ BehaviorDigest::empty() diff --git a/src/protected_mode/mod.rs b/src/protected_mode/mod.rs index 17fd883..3036620 100644 --- a/src/protected_mode/mod.rs +++ b/src/protected_mode/mod.rs @@ -5774,13 +5774,14 @@ fn read_operands< 0 => { // these are Zv_R let bank = if !instruction.prefixes.operand_size() { + instruction.mem_size = 4; RegisterBank::D } else { + instruction.mem_size = 2; RegisterBank::W }; instruction.regs[0] = RegSpec::from_parts(reg, bank); - instruction.mem_size = 4; sink.record( opcode_start + 0, opcode_start + 2, diff --git a/src/real_mode/behavior.rs b/src/real_mode/behavior.rs index 8941ee5..7724a0a 100644 --- a/src/real_mode/behavior.rs +++ b/src/real_mode/behavior.rs @@ -140,6 +140,16 @@ impl Instruction { .set_operand(1, Access::Read) .set_operand(2, Access::Read); } + } else if self.opcode == Opcode::PUSH { + if self.prefixes.operand_size() { + behavior = behavior + .set_implicit_ops(PUSHW_OPS_IDX); + } + } else if self.opcode == Opcode::PUSHF { + if self.prefixes.operand_size() { + behavior = behavior + .set_implicit_ops(PUSHFW_OPS_IDX); + } } else if self.opcode == Opcode::DIV || self.opcode == Opcode::IDIV { let op_width = if self.operands[0] == OperandSpec::RegMMM { self.regs[1].width() @@ -1960,6 +1970,28 @@ static PUSH_OPS: &'static [ImplicitOperand] = &[ } ]; +static PUSHW_OPS: &'static [ImplicitOperand] = &[ + ImplicitOperand { + spec: OperandSpec::Disp, + reg: RegSpec::sp(), + disp: -4i32, + write: true, + }, + // push.. pushes the value (above), then does a RMW on rsp. + ImplicitOperand { + spec: OperandSpec::RegRRR, + reg: RegSpec::sp(), + disp: 0, + write: false, + }, + ImplicitOperand { + spec: OperandSpec::RegRRR, + reg: RegSpec::sp(), + disp: 0, + write: true, + } +]; + static POP_OPS: &'static [ImplicitOperand] = &[ ImplicitOperand { spec: OperandSpec::Deref, @@ -2127,6 +2159,34 @@ static PUSHF_OPS: &'static [ImplicitOperand] = &[ } ]; +static PUSHFW_OPS: &'static [ImplicitOperand] = &[ + ImplicitOperand { + spec: OperandSpec::RegRRR, + reg: RegSpec::eflags(), + disp: 0, + write: false, + }, + ImplicitOperand { + spec: OperandSpec::Disp, + reg: RegSpec::sp(), + disp: -4i32, + write: true, + }, + // push.. pushes the value (above), then does a RMW on rsp. + ImplicitOperand { + spec: OperandSpec::RegRRR, + reg: RegSpec::sp(), + disp: 0, + write: false, + }, + ImplicitOperand { + spec: OperandSpec::RegRRR, + reg: RegSpec::sp(), + disp: 0, + write: true, + } +]; + static POPF_OPS: &'static [ImplicitOperand] = &[ ImplicitOperand { spec: OperandSpec::Deref, @@ -3689,8 +3749,10 @@ const PUSHA_IDX: u16 = 72; const POPA_IDX: u16 = 73; const PUSHAD_IDX: u16 = 74; const POPAD_IDX: u16 = 75; +const PUSHW_OPS_IDX: u16 = 76; +const PUSHFW_OPS_IDX: u16 = 77; -static IMPLICIT_OPS_LIST: [&[ImplicitOperand]; 76] = [ +static IMPLICIT_OPS_LIST: [&[ImplicitOperand]; 78] = [ &[], // implicit ops list 0 is not used PUSH_OPS, POP_OPS, @@ -3767,6 +3829,8 @@ static IMPLICIT_OPS_LIST: [&[ImplicitOperand]; 76] = [ POPA_OPS, PUSHAD_OPS, POPAD_OPS, + PUSHW_OPS, + PUSHFW_OPS, ]; fn opcode2behavior(opc: &Opcode) -> BehaviorDigest { @@ -3910,6 +3974,7 @@ static TABLE: [BehaviorDigest; 1428] = [ .set_operand(0, Access::Read), /* PUSH => */ BehaviorDigest::empty() .set_implicit_ops(PUSH_OPS_IDX) + .set_nontrivial(true) .set_pl_any() .set_operand(0, Access::Read), /* POP => */ BehaviorDigest::empty() @@ -3957,6 +4022,7 @@ static TABLE: [BehaviorDigest; 1428] = [ .set_pl_any(), /* PUSHF => */ BehaviorDigest::empty() .set_implicit_ops(PUSHF_IDX) + .set_nontrivial(true) .set_pl_any(), /* WAIT => */ GENERAL, /* CBW => */ BehaviorDigest::empty() diff --git a/src/real_mode/mod.rs b/src/real_mode/mod.rs index cc67de7..aa98846 100644 --- a/src/real_mode/mod.rs +++ b/src/real_mode/mod.rs @@ -5800,13 +5800,14 @@ fn read_operands< 0 => { // these are Zv_R let bank = if instruction.prefixes.operand_size() { + instruction.mem_size = 4; RegisterBank::D } else { + instruction.mem_size = 2; RegisterBank::W }; instruction.regs[0] = RegSpec::from_parts(reg, bank); - instruction.mem_size = 2; sink.record( opcode_start + 0, opcode_start + 2, @@ -6517,9 +6518,17 @@ fn read_operands< } else if instruction.opcode == Opcode::RETF { instruction.mem_size = 4; } else if instruction.opcode == Opcode::POPF { - instruction.mem_size = 2; + if !instruction.prefixes.operand_size() { + instruction.mem_size = 2; + } else { + instruction.mem_size = 4; + } } else if instruction.opcode == Opcode::PUSHF { - instruction.mem_size = 2; + if !instruction.prefixes.operand_size() { + instruction.mem_size = 2; + } else { + instruction.mem_size = 4; + } } else if instruction.opcode == Opcode::LEAVE { instruction.mem_size = 2; } else if instruction.opcode == Opcode::XLAT { @@ -8801,7 +8810,11 @@ fn read_operands< instruction.operand_count = 1; // see the comment on mem size as written in OperandCase::FS - instruction.mem_size = 2; + if !instruction.prefixes.operand_size() { + instruction.mem_size = 2; + } else { + instruction.mem_size = 4; + } } OperandCase::DS => { instruction.regs[0] = RegSpec::ds(); @@ -8809,7 +8822,11 @@ fn read_operands< instruction.operand_count = 1; // see the comment on mem size as written in OperandCase::FS - instruction.mem_size = 2; + if !instruction.prefixes.operand_size() { + instruction.mem_size = 2; + } else { + instruction.mem_size = 4; + } } OperandCase::ES => { instruction.regs[0] = RegSpec::es(); @@ -8817,7 +8834,11 @@ fn read_operands< instruction.operand_count = 1; // see the comment on mem size as written in OperandCase::FS - instruction.mem_size = 2; + if !instruction.prefixes.operand_size() { + instruction.mem_size = 2; + } else { + instruction.mem_size = 4; + } } OperandCase::SS => { instruction.regs[0] = RegSpec::ss(); @@ -8825,7 +8846,11 @@ fn read_operands< instruction.operand_count = 1; // see the comment on mem size as written in OperandCase::FS - instruction.mem_size = 2; + if !instruction.prefixes.operand_size() { + instruction.mem_size = 2; + } else { + instruction.mem_size = 4; + } } OperandCase::FS => { instruction.regs[0] = RegSpec::fs(); @@ -8842,7 +8867,11 @@ fn read_operands< // the memory size here really depends on the `Default` bit in the CS descriptor. if // the default operand size is 64 bits (basically all long-mode code), the acccess is // 8 bytes (zero extended). if the default operand size is 32 bits, this is more complex. - instruction.mem_size = 2; + if !instruction.prefixes.operand_size() { + instruction.mem_size = 2; + } else { + instruction.mem_size = 4; + } } OperandCase::GS => { instruction.regs[0] = RegSpec::gs(); @@ -8850,7 +8879,11 @@ fn read_operands< instruction.operand_count = 1; // see the comment on mem size as written in OperandCase::FS - instruction.mem_size = 2; + if !instruction.prefixes.operand_size() { + instruction.mem_size = 2; + } else { + instruction.mem_size = 4; + } } OperandCase::AL_Ib => { instruction.regs[0] = diff --git a/test/protected_mode/operand.rs b/test/protected_mode/operand.rs index 0c970de..8a752f7 100644 --- a/test/protected_mode/operand.rs +++ b/test/protected_mode/operand.rs @@ -51,8 +51,8 @@ fn test_implied_memory_width() { assert_eq!(mem_size_of(&[0xe8, 0x11, 0x22, 0x33, 0x44]), Some(4)); assert_eq!(mem_size_of(&[0x50]), Some(4)); assert_eq!(mem_size_of(&[0x58]), Some(4)); - assert_eq!(mem_size_of(&[0x66, 0x50]), Some(4)); - assert_eq!(mem_size_of(&[0x66, 0x58]), Some(4)); + assert_eq!(mem_size_of(&[0x66, 0x50]), Some(2)); + assert_eq!(mem_size_of(&[0x66, 0x58]), Some(2)); assert_eq!(mem_size_of(&[0xff, 0xf0]), Some(4)); assert_eq!(mem_size_of(&[0x66, 0xff, 0xf0]), Some(2)); // unlike 64-bit mode, operand-size prefixed call and jump do have a different size: they read diff --git a/test/real_mode/operand.rs b/test/real_mode/operand.rs index fb2ce39..d5a3b83 100644 --- a/test/real_mode/operand.rs +++ b/test/real_mode/operand.rs @@ -12,8 +12,8 @@ fn test_implied_memory_width() { assert_eq!(mem_size_of(&[0xe8, 0x11, 0x22, 0x33, 0x44]), Some(2)); assert_eq!(mem_size_of(&[0x50]), Some(2)); assert_eq!(mem_size_of(&[0x58]), Some(2)); - assert_eq!(mem_size_of(&[0x66, 0x50]), Some(2)); - assert_eq!(mem_size_of(&[0x66, 0x58]), Some(2)); + assert_eq!(mem_size_of(&[0x66, 0x50]), Some(4)); + assert_eq!(mem_size_of(&[0x66, 0x58]), Some(4)); assert_eq!(mem_size_of(&[0xff, 0xf0]), Some(2)); assert_eq!(mem_size_of(&[0x66, 0xff, 0xf0]), Some(4)); // unlike 64-bit mode, operand-size prefixed call and jump do have a different size: they read |
