aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/long_mode/display.rs2
-rw-r--r--src/long_mode/mod.rs60
-rw-r--r--test/long_mode/mod.rs37
3 files changed, 97 insertions, 2 deletions
diff --git a/src/long_mode/display.rs b/src/long_mode/display.rs
index 694bc75..17f2c86 100644
--- a/src/long_mode/display.rs
+++ b/src/long_mode/display.rs
@@ -1138,6 +1138,7 @@ const MNEMONICS: &[&'static str] = &[
"rdgsbase",
"wrfsbase",
"wrgsbase",
+ "crc32",
];
impl Opcode {
@@ -1989,6 +1990,7 @@ impl <T: fmt::Write, Color: fmt::Display, Y: YaxColors<Color>> Colorize<T, Color
Opcode::WRPKRU |
Opcode::LAR => { write!(out, "{}", colors.platform_op(self)) }
+ Opcode::CRC32 |
Opcode::RDSEED |
Opcode::RDRAND |
Opcode::SHA1RNDS4 |
diff --git a/src/long_mode/mod.rs b/src/long_mode/mod.rs
index 7deab18..065d1ae 100644
--- a/src/long_mode/mod.rs
+++ b/src/long_mode/mod.rs
@@ -1433,6 +1433,8 @@ pub enum Opcode {
RDGSBASE,
WRFSBASE,
WRGSBASE,
+
+ CRC32,
}
#[derive(Debug)]
@@ -2338,6 +2340,7 @@ impl InstDecoder {
return Err(DecodeError::InvalidOpcode);
}
}
+ Opcode::CRC32 |
Opcode::PCMPESTRI |
Opcode::PCMPESTRM |
Opcode::PCMPISTRI |
@@ -3419,11 +3422,11 @@ pub enum OperandCode {
ModRM_0x0f0d = OperandCodeBuilder::new().read_modrm().special_case(2).bits(),
ModRM_0x0fae = OperandCodeBuilder::new().read_modrm().special_case(3).bits(),
ModRM_0x0fba = OperandCodeBuilder::new().read_modrm().special_case(4).bits(),
- ModRM_0xf238 = OperandCodeBuilder::new().read_modrm().special_case(5).bits(),
ModRM_0xf30fae = OperandCodeBuilder::new().read_modrm().special_case(6).bits(),
ModRM_0x660fae = OperandCodeBuilder::new().read_modrm().special_case(7).bits(),
ModRM_0xf30fc7 = OperandCodeBuilder::new().read_modrm().special_case(8).bits(),
ModRM_0x660f38 = OperandCodeBuilder::new().read_modrm().special_case(9).bits(),
+ ModRM_0xf20f38 = OperandCodeBuilder::new().read_modrm().special_case(24).bits(),
ModRM_0xf30f38 = OperandCodeBuilder::new().read_modrm().special_case(10).bits(),
ModRM_0x660f3a = OperandCodeBuilder::new().read_modrm().special_case(11).bits(),
ModRM_0x0f38 = OperandCodeBuilder::new().read_modrm().special_case(12).bits(),
@@ -3617,6 +3620,7 @@ pub enum OperandCode {
Gv_Ew = OperandCodeBuilder::new().op0_is_rrr_and_embedded_instructions().operand_case(5).bits(),
Gv_Ew_LSL = OperandCodeBuilder::new().op0_is_rrr_and_embedded_instructions().operand_case(7).bits(),
Gdq_Ed = OperandCodeBuilder::new().op0_is_rrr_and_embedded_instructions().operand_case(6).bits(),
+ Gdq_Ev = OperandCodeBuilder::new().op0_is_rrr_and_embedded_instructions().operand_case(10).bits(),
G_E_mm_Ib = OperandCodeBuilder::new().op0_is_rrr_and_embedded_instructions().read_E().reg_mem().operand_case(2).bits(),
G_E_xmm_Ib = OperandCodeBuilder::new().op0_is_rrr_and_embedded_instructions().operand_case(8).bits(),
AL_Ob = OperandCodeBuilder::new().op0_is_rrr_and_embedded_instructions().operand_case(0).bits(),
@@ -4029,7 +4033,7 @@ const OPCODE_F20F_MAP: [OpcodeRecord; 256] = [
OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing),
OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing),
OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing),
- OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::ModRM_0xf238),
+ OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::ModRM_0xf20f38),
OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing),
OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing),
OpcodeRecord(Interpretation::Instruction(Opcode::Invalid), OperandCode::Nothing),
@@ -5958,6 +5962,24 @@ fn read_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter: T,
RegSpec::gp_from_parts((modrm >> 3) & 7, instruction.prefixes.rex().r(), opwidth, instruction.prefixes.rex().present());
instruction.operand_count = 2;
},
+ OperandCode::Gdq_Ev => {
+ let opwidth = imm_width_from_prefixes_64(SizeCode::vqp, instruction.prefixes);
+ let modrm = read_modrm(&mut bytes_iter, length)?;
+
+// println!("mod_bits: {:2b}, r: {:3b}, m: {:3b}", mod_bits, r, m);
+ instruction.operands[1] = read_E(&mut bytes_iter, instruction, modrm, opwidth, length)?;
+ // `opwidth` can be 2, 4, or 8 here. if opwidth is 2, the first operand is a dword.
+ // if opwidth is 4, both registers are dwords. and if opwidth is 8, both registers are
+ // qword.
+ let regwidth = if opwidth == 2 {
+ 4
+ } else {
+ opwidth
+ };
+ instruction.modrm_rrr =
+ RegSpec::gp_from_parts((modrm >> 3) & 7, instruction.prefixes.rex().r(), regwidth, instruction.prefixes.rex().present());
+ instruction.operand_count = 2;
+ },
OperandCode::Ev => {
instruction.operands[0] = mem_oper;
instruction.operand_count = 1;
@@ -6474,6 +6496,23 @@ fn unlikely_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter
instruction.operands[1] = read_E_xmm(&mut bytes_iter, instruction, modrm, length)?;
instruction.operand_count = 2;
}
+ OperandCode::ModRM_0xf20f38 => {
+ let op = bytes_iter.next().ok_or(DecodeError::ExhaustedInput).map(|b| { *length += 1; b })?;
+ match op {
+ 0xf0 => {
+ instruction.opcode = Opcode::CRC32;
+ return read_operands(decoder, bytes_iter, instruction, OperandCode::Gv_Eb, length);
+ }
+ 0xf1 => {
+ instruction.opcode = Opcode::CRC32;
+ return read_operands(decoder, bytes_iter, instruction, OperandCode::Gdq_Ev, length);
+ }
+ _ => {
+ instruction.opcode = Opcode::Invalid;
+ return Err(DecodeError::InvalidOpcode);
+ }
+ };
+ }
OperandCode::ModRM_0xf30f38 => {
let op = bytes_iter.next().ok_or(DecodeError::ExhaustedInput).map(|b| { *length += 1; b })?;
match op {
@@ -6535,6 +6574,7 @@ fn unlikely_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter
0x34 => { instruction.opcode = Opcode::PMOVZXWQ; }
0x35 => { instruction.opcode = Opcode::PMOVZXDQ; }
+ 0x37 => { instruction.opcode = Opcode::PCMPGTQ; }
0x38 => { instruction.opcode = Opcode::PMINSB; }
0x39 => { instruction.opcode = Opcode::PMINSD; }
0x3a => { instruction.opcode = Opcode::PMINUW; }
@@ -6644,6 +6684,22 @@ fn unlikely_operands<T: Iterator<Item=u8>>(decoder: &InstDecoder, mut bytes_iter
return read_operands(decoder, bytes_iter, instruction, OperandCode::G_E_xmm_Ib, length);
}
+ 0x60 => {
+ instruction.opcode = Opcode::PCMPESTRM;
+ return read_operands(decoder, bytes_iter, instruction, OperandCode::G_E_xmm_Ib, length);
+ }
+ 0x61 => {
+ instruction.opcode = Opcode::PCMPESTRI;
+ return read_operands(decoder, bytes_iter, instruction, OperandCode::G_E_xmm_Ib, length);
+ }
+ 0x62 => {
+ instruction.opcode = Opcode::PCMPISTRM;
+ return read_operands(decoder, bytes_iter, instruction, OperandCode::G_E_xmm_Ib, length);
+ }
+ 0x63 => {
+ instruction.opcode = Opcode::PCMPISTRI;
+ return read_operands(decoder, bytes_iter, instruction, OperandCode::G_E_xmm_Ib, length);
+ }
0xcc => {
instruction.opcode = Opcode::SHA1RNDS4;
diff --git a/test/long_mode/mod.rs b/test/long_mode/mod.rs
index 9ce4019..a9a894a 100644
--- a/test/long_mode/mod.rs
+++ b/test/long_mode/mod.rs
@@ -586,6 +586,41 @@ fn test_sse3() {
}
#[test]
+fn test_sse4_2() {
+ fn test_instr(bytes: &[u8], text: &'static str) {
+ test_display_under(&InstDecoder::minimal().with_sse4_2(), bytes, text);
+ test_invalid_under(&InstDecoder::minimal(), bytes);
+ // avx doesn't imply older instructions are necessarily valid
+ test_invalid_under(&InstDecoder::minimal().with_avx(), bytes);
+ }
+
+ #[allow(unused)]
+ fn test_instr_invalid(bytes: &[u8]) {
+ test_invalid_under(&InstDecoder::minimal().with_sse4_2(), bytes);
+ test_invalid_under(&InstDecoder::default(), bytes);
+ }
+
+ test_instr(&[0x66, 0x0f, 0x38, 0x37, 0x03], "pcmpgtq xmm0, [rbx]");
+ test_instr(&[0x66, 0x0f, 0x38, 0x37, 0xc3], "pcmpgtq xmm0, xmm3");
+
+ test_instr(&[0xf2, 0x0f, 0x38, 0xf0, 0x06], "crc32 eax, [rsi]");
+ test_instr(&[0xf2, 0x0f, 0x38, 0xf0, 0xc6], "crc32 eax, dh");
+ test_instr(&[0xf2, 0x0f, 0x38, 0xf1, 0x06], "crc32 eax, [rsi]");
+ test_instr(&[0xf2, 0x0f, 0x38, 0xf1, 0xc6], "crc32 eax, esi");
+ test_instr(&[0x66, 0xf2, 0x0f, 0x38, 0xf1, 0xc6], "crc32 eax, si");
+ test_instr(&[0x66, 0xf2, 0x48, 0x0f, 0x38, 0xf1, 0xc6], "crc32 rax, rsi");
+
+ test_instr(&[0x66, 0x0f, 0x3a, 0x60, 0xc6, 0x54], "pcmpestrm xmm0, xmm6, 0x54");
+ test_instr(&[0x66, 0x0f, 0x3a, 0x60, 0x06, 0x54], "pcmpestrm xmm0, [rsi], 0x54");
+ test_instr(&[0x66, 0x0f, 0x3a, 0x61, 0xc6, 0x54], "pcmpestri xmm0, xmm6, 0x54");
+ test_instr(&[0x66, 0x0f, 0x3a, 0x61, 0x06, 0x54], "pcmpestri xmm0, [rsi], 0x54");
+ test_instr(&[0x66, 0x0f, 0x3a, 0x62, 0xc6, 0x54], "pcmpistrm xmm0, xmm6, 0x54");
+ test_instr(&[0x66, 0x0f, 0x3a, 0x62, 0x06, 0x54], "pcmpistrm xmm0, [rsi], 0x54");
+ test_instr(&[0x66, 0x0f, 0x3a, 0x63, 0xc6, 0x54], "pcmpistri xmm0, xmm6, 0x54");
+ test_instr(&[0x66, 0x0f, 0x3a, 0x63, 0x06, 0x54], "pcmpistri xmm0, [rsi], 0x54");
+}
+
+#[test]
fn test_sse4_1() {
fn test_instr(bytes: &[u8], text: &'static str) {
test_display_under(&InstDecoder::minimal().with_sse4_1(), bytes, text);
@@ -690,6 +725,8 @@ fn test_sse4_1() {
test_instr(&[0x66, 0x0f, 0x3a, 0x14, 0x06, 0x31], "pextrb xmm0, [rsi], 0x31");
test_invalid(&[0x0f, 0x3a, 0x14, 0x06]);
+ test_instr(&[0x66, 0x0f, 0x3a, 0x15, 0x06, 0x31], "pextrw xmm0, [rsi], 0x31");
+ test_invalid(&[0x0f, 0x3a, 0x15, 0x06]);
test_instr(&[0x66, 0x0f, 0x3a, 0x16, 0x06, 0x31], "pextrd xmm0, [rsi], 0x31");
test_invalid(&[0x0f, 0x3a, 0x16, 0x06]);
test_instr(&[0x66, 0x48, 0x0f, 0x3a, 0x16, 0x06, 0x31], "pextrq xmm0, [rsi], 0x31");