aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2026-05-25 19:21:31 +0000
committeriximeow <me@iximeow.net>2026-05-25 19:24:01 +0000
commitfe6b2b898aee944ba3490c35f4aed4d155485c0e (patch)
tree984496e7ec908c43272d591db12a566c49ea07a2 /src
parentb43f483a50f650dbf385d2d55d0d38171f08e32b (diff)
push/pop width in 16/32-bit modes are receptive to operand width prefix
Diffstat (limited to 'src')
-rw-r--r--src/protected_mode/behavior.rs72
-rw-r--r--src/protected_mode/mod.rs3
-rw-r--r--src/real_mode/behavior.rs68
-rw-r--r--src/real_mode/mod.rs51
4 files changed, 180 insertions, 14 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] =