aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authoriximeow <me@iximeow.net>2026-04-12 01:03:47 +0000
committeriximeow <me@iximeow.net>2026-05-25 00:59:27 +0000
commita049351c5d512710f557ffb45ee6391fc86a3dc6 (patch)
tree17040c4c95f9361de271fb1ad874c3a71d2b0e9d
parent6c32405ca9930f393d8ca45d22df1b5a1c7c8653 (diff)
fix table management instructions' ({l,s}{g,i,l}dt) mem_size
these instructions, it turns out, have fixed operand size based on CPU execution mode and regardless of prefixes. good to know!
-rw-r--r--CHANGELOG2
-rw-r--r--src/long_mode/mod.rs11
-rw-r--r--src/protected_mode/mod.rs15
-rw-r--r--src/real_mode/mod.rs12
-rw-r--r--test/long_mode/mod.rs10
-rw-r--r--test/long_mode/operand.rs8
-rw-r--r--test/protected_mode/mod.rs10
-rw-r--r--test/real_mode/mod.rs10
8 files changed, 51 insertions, 27 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 8d88ec7..540a5bf 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -10,6 +10,8 @@
* push-immediate, pushf, popf, enter, leave, and xlat now all report a correct memory
access size, fixing the prior behavior of reporting no memory access size at
all
+* table load/store instructions (lgdt, lidt, lldt, sgdt, sidt, sldt) have correct (mode-dependent)
+ memory access sizes, rather than incorrectly varying on operand-size overrides.
* 64-bit mode: mov seg-to-reg uses 32-bit GPRs for the destination rather than 16-bit.
* this is more accurate to the semantic of the instruction, which is why other disassemblers
report it this way; for register destinations specifically the segment selector is
diff --git a/src/long_mode/mod.rs b/src/long_mode/mod.rs
index 2971313..4288eb9 100644
--- a/src/long_mode/mod.rs
+++ b/src/long_mode/mod.rs
@@ -8243,7 +8243,7 @@ fn read_operands<
} else {
instruction.opcode = Opcode::SGDT;
instruction.operand_count = 1;
- instruction.mem_size = 63;
+ instruction.mem_size = 10;
instruction.operands[0] = read_E(words, instruction, modrm, bank, sink)?;
}
} else if r == 1 {
@@ -8298,7 +8298,7 @@ fn read_operands<
} else {
instruction.opcode = Opcode::SIDT;
instruction.operand_count = 1;
- instruction.mem_size = 63;
+ instruction.mem_size = 10;
instruction.operands[0] = read_E(words, instruction, modrm, bank, sink)?;
}
} else if r == 2 {
@@ -8337,7 +8337,10 @@ fn read_operands<
} else {
instruction.opcode = Opcode::LGDT;
instruction.operand_count = 1;
- instruction.mem_size = 63;
+ // quoth SDM:
+ // > In 64-bit mode, the operand size is fixed at 8+2 bytes. The instruction
+ // > stores an 8-byte base and a 2-byte limit.
+ instruction.mem_size = 10;
instruction.operands[0] = read_E(words, instruction, modrm, bank, sink)?;
}
} else if r == 3 {
@@ -8401,7 +8404,7 @@ fn read_operands<
} else {
instruction.opcode = Opcode::LIDT;
instruction.operand_count = 1;
- instruction.mem_size = 63;
+ instruction.mem_size = 10;
instruction.operands[0] = read_E(words, instruction, modrm, bank, sink)?;
}
} else if r == 4 {
diff --git a/src/protected_mode/mod.rs b/src/protected_mode/mod.rs
index 6701071..89c5dfd 100644
--- a/src/protected_mode/mod.rs
+++ b/src/protected_mode/mod.rs
@@ -8042,7 +8042,7 @@ fn read_operands<
} else {
instruction.opcode = Opcode::SGDT;
instruction.operand_count = 1;
- instruction.mem_size = 63;
+ instruction.mem_size = 6;
instruction.operands[0] = read_E(words, instruction, modrm, bank, sink)?;
}
} else if r == 1 {
@@ -8097,7 +8097,11 @@ fn read_operands<
} else {
instruction.opcode = Opcode::SIDT;
instruction.operand_count = 1;
- instruction.mem_size = 63;
+ // quoth SDM:
+ // > In non-64-bit modes, the 16-bit limit field of the register is stored in
+ // > the low 2 bytes of the memory location and the 32-bit base address is
+ // > stored in the high 4 bytes.
+ instruction.mem_size = 6;
instruction.operands[0] = read_E(words, instruction, modrm, bank, sink)?;
}
} else if r == 2 {
@@ -8136,7 +8140,10 @@ fn read_operands<
} else {
instruction.opcode = Opcode::LGDT;
instruction.operand_count = 1;
- instruction.mem_size = 63;
+ // quoth SDM:
+ // > In legacy or compatibility mode, the destination operand is a 6-byte
+ // > memory location.
+ instruction.mem_size = 6;
instruction.operands[0] = read_E(words, instruction, modrm, bank, sink)?;
}
} else if r == 3 {
@@ -8200,7 +8207,7 @@ fn read_operands<
} else {
instruction.opcode = Opcode::LIDT;
instruction.operand_count = 1;
- instruction.mem_size = 63;
+ instruction.mem_size = 6;
instruction.operands[0] = read_E(words, instruction, modrm, bank, sink)?;
}
} else if r == 4 {
diff --git a/src/real_mode/mod.rs b/src/real_mode/mod.rs
index 7ac4c2e..559cac3 100644
--- a/src/real_mode/mod.rs
+++ b/src/real_mode/mod.rs
@@ -8087,7 +8087,7 @@ fn read_operands<
} else {
instruction.opcode = Opcode::SGDT;
instruction.operand_count = 1;
- instruction.mem_size = 63;
+ instruction.mem_size = 6;
instruction.operands[0] = read_E(words, instruction, modrm, bank, sink)?;
}
} else if r == 1 {
@@ -8142,7 +8142,11 @@ fn read_operands<
} else {
instruction.opcode = Opcode::SIDT;
instruction.operand_count = 1;
- instruction.mem_size = 63;
+ // quoth SDM:
+ // > In non-64-bit modes, the 16-bit limit field of the register is stored in
+ // > the low 2 bytes of the memory location and the 32-bit base address is
+ // > stored in the high 4 bytes.
+ instruction.mem_size = 6;
instruction.operands[0] = read_E(words, instruction, modrm, bank, sink)?;
}
} else if r == 2 {
@@ -8181,7 +8185,7 @@ fn read_operands<
} else {
instruction.opcode = Opcode::LGDT;
instruction.operand_count = 1;
- instruction.mem_size = 63;
+ instruction.mem_size = 6;
instruction.operands[0] = read_E(words, instruction, modrm, bank, sink)?;
}
} else if r == 3 {
@@ -8245,7 +8249,7 @@ fn read_operands<
} else {
instruction.opcode = Opcode::LIDT;
instruction.operand_count = 1;
- instruction.mem_size = 63;
+ instruction.mem_size = 6;
instruction.operands[0] = read_E(words, instruction, modrm, bank, sink)?;
}
} else if r == 4 {
diff --git a/test/long_mode/mod.rs b/test/long_mode/mod.rs
index dc82cd4..0b6dcc6 100644
--- a/test/long_mode/mod.rs
+++ b/test/long_mode/mod.rs
@@ -955,11 +955,11 @@ fn test_0f01() {
}
test_display(&[0x0f, 0x01, 0x38], "invlpg byte [rax]");
test_display(&[0x0f, 0x01, 0x3f], "invlpg byte [rdi]");
- test_display(&[0x0f, 0x01, 0x40, 0xff], "sgdt ptr [rax - 0x1]");
- test_display(&[0x0f, 0x01, 0x41, 0xff], "sgdt ptr [rcx - 0x1]");
- test_display(&[0x0f, 0x01, 0x49, 0xff], "sidt ptr [rcx - 0x1]");
- test_display(&[0x0f, 0x01, 0x51, 0xff], "lgdt ptr [rcx - 0x1]");
- test_display(&[0x0f, 0x01, 0x59, 0xff], "lidt ptr [rcx - 0x1]");
+ test_display(&[0x0f, 0x01, 0x40, 0xff], "sgdt mword [rax - 0x1]");
+ test_display(&[0x0f, 0x01, 0x41, 0xff], "sgdt mword [rcx - 0x1]");
+ test_display(&[0x0f, 0x01, 0x49, 0xff], "sidt mword [rcx - 0x1]");
+ test_display(&[0x0f, 0x01, 0x51, 0xff], "lgdt mword [rcx - 0x1]");
+ test_display(&[0x0f, 0x01, 0x59, 0xff], "lidt mword [rcx - 0x1]");
test_display(&[0x0f, 0x01, 0x61, 0xff], "smsw word [rcx - 0x1]");
test_invalid(&[0x0f, 0x01, 0x69, 0xff]);
test_display(&[0x0f, 0x01, 0x71, 0xff], "lmsw word [rcx - 0x1]");
diff --git a/test/long_mode/operand.rs b/test/long_mode/operand.rs
index 09ac5a2..9f594b5 100644
--- a/test/long_mode/operand.rs
+++ b/test/long_mode/operand.rs
@@ -69,4 +69,12 @@ fn test_implied_memory_width() {
assert_eq!(mem_size_of(&[0x0f, 0xa8]), Some(8));
// pop gs
assert_eq!(mem_size_of(&[0x0f, 0xa9]), Some(8));
+ // lidt
+ assert_eq!(mem_size_of(&[0x0f, 0x01, 0x18]), Some(10));
+ // sidt
+ assert_eq!(mem_size_of(&[0x0f, 0x01, 0x08]), Some(10));
+ // lgdt
+ assert_eq!(mem_size_of(&[0x0f, 0x01, 0x10]), Some(10));
+ // sgdt
+ assert_eq!(mem_size_of(&[0x0f, 0x01, 0x00]), Some(10));
}
diff --git a/test/protected_mode/mod.rs b/test/protected_mode/mod.rs
index 78a061a..d5533cc 100644
--- a/test/protected_mode/mod.rs
+++ b/test/protected_mode/mod.rs
@@ -811,11 +811,11 @@ fn test_0f01() {
// Number"
test_display(&[0x0f, 0x01, 0x38], "invlpg byte [eax]");
test_display(&[0x0f, 0x01, 0x3f], "invlpg byte [edi]");
- test_display(&[0x0f, 0x01, 0x40, 0xff], "sgdt ptr [eax - 0x1]");
- test_display(&[0x0f, 0x01, 0x41, 0xff], "sgdt ptr [ecx - 0x1]");
- test_display(&[0x0f, 0x01, 0x49, 0xff], "sidt ptr [ecx - 0x1]");
- test_display(&[0x0f, 0x01, 0x51, 0xff], "lgdt ptr [ecx - 0x1]");
- test_display(&[0x0f, 0x01, 0x59, 0xff], "lidt ptr [ecx - 0x1]");
+ test_display(&[0x0f, 0x01, 0x40, 0xff], "sgdt far [eax - 0x1]");
+ test_display(&[0x0f, 0x01, 0x41, 0xff], "sgdt far [ecx - 0x1]");
+ test_display(&[0x0f, 0x01, 0x49, 0xff], "sidt far [ecx - 0x1]");
+ test_display(&[0x0f, 0x01, 0x51, 0xff], "lgdt far [ecx - 0x1]");
+ test_display(&[0x0f, 0x01, 0x59, 0xff], "lidt far [ecx - 0x1]");
test_display(&[0x0f, 0x01, 0x61, 0xff], "smsw word [ecx - 0x1]");
test_invalid(&[0x0f, 0x01, 0x69, 0xff]);
test_display(&[0x0f, 0x01, 0x71, 0xff], "lmsw word [ecx - 0x1]");
diff --git a/test/real_mode/mod.rs b/test/real_mode/mod.rs
index 5fb109f..af8bfaf 100644
--- a/test/real_mode/mod.rs
+++ b/test/real_mode/mod.rs
@@ -104,11 +104,11 @@ fn test_real_mode() {
test_display(&[0x0e], "push cs");
test_display(&[0x0f, 0x01, 0x38], "invlpg byte [bx + si * 1]");
test_display(&[0x0f, 0x01, 0x3f], "invlpg byte [bx]");
- test_display(&[0x0f, 0x01, 0x40, 0xff], "sgdt ptr [bx + si * 1 - 0x1]");
- test_display(&[0x0f, 0x01, 0x41, 0xff], "sgdt ptr [bx + di * 1 - 0x1]");
- test_display(&[0x0f, 0x01, 0x49, 0xff], "sidt ptr [bx + di * 1 - 0x1]");
- test_display(&[0x0f, 0x01, 0x51, 0xff], "lgdt ptr [bx + di * 1 - 0x1]");
- test_display(&[0x0f, 0x01, 0x59, 0xff], "lidt ptr [bx + di * 1 - 0x1]");
+ test_display(&[0x0f, 0x01, 0x40, 0xff], "sgdt far [bx + si * 1 - 0x1]");
+ test_display(&[0x0f, 0x01, 0x41, 0xff], "sgdt far [bx + di * 1 - 0x1]");
+ test_display(&[0x0f, 0x01, 0x49, 0xff], "sidt far [bx + di * 1 - 0x1]");
+ test_display(&[0x0f, 0x01, 0x51, 0xff], "lgdt far [bx + di * 1 - 0x1]");
+ test_display(&[0x0f, 0x01, 0x59, 0xff], "lidt far [bx + di * 1 - 0x1]");
test_display(&[0x0f, 0x01, 0x61, 0xff], "smsw word [bx + di * 1 - 0x1]");
test_display(&[0x0f, 0x01, 0x71, 0xff], "lmsw word [bx + di * 1 - 0x1]");
test_display(&[0x0f, 0x01, 0x79, 0xff], "invlpg byte [bx + di * 1 - 0x1]");