diff --git a/decode.c b/decode.c index 4e3ec90..ba6b72d 100644 --- a/decode.c +++ b/decode.c @@ -61,8 +61,7 @@ enum PREFIX_REXR = 0x04, PREFIX_REXW = 0x08, PREFIX_REX = 0x40, - PREFIX_VEXL = 0x10, - PREFIX_REXRR = 0x80, + PREFIX_REXRR = 0x10, }; struct InstrDesc @@ -129,6 +128,7 @@ fd_decode(const uint8_t* buffer, size_t len_sz, int mode_int, uintptr_t address, uint8_t addr_size = mode == DECODE_64 ? 3 : 2; unsigned prefix_rex = 0; int rex_off = -1; + unsigned vexl = 0; unsigned prefix_evex = 0; instr->segment = FD_REG_NONE; @@ -192,6 +192,7 @@ prefix_end: return FD_ERR_PARTIAL; unsigned opcode_escape = 0; + uint8_t mandatory_prefix = 0; // without escape/VEX/EVEX, this is ignored. if (buffer[off] == 0x0f) { if (UNLIKELY(off + 1 >= len)) @@ -203,8 +204,12 @@ prefix_end: else opcode_escape = 1; off += opcode_escape >= 2 ? 2 : 1; + + // If there is no REP/REPNZ prefix offer 66h as mandatory prefix. If + // there is a REP prefix, then the 66h prefix is ignored here. + mandatory_prefix = prefix_rep ? prefix_rep : !!prefix_66; } - else if (UNLIKELY((buffer[off] & 0xfe) == 0xc4 || buffer[off] == 0x62)) + else if (UNLIKELY((unsigned) buffer[off] - 0xc4 < 2 || buffer[off] == 0x62)) { unsigned vex_prefix = buffer[off]; // VEX (C4/C5) or EVEX (62) @@ -220,22 +225,24 @@ prefix_end: return FD_ERR_UD; uint8_t byte = buffer[off + 1]; - prefix_rex |= byte & 0x80 ? 0 : PREFIX_REXR; if (vex_prefix == 0xc5) // 2-byte VEX { opcode_escape = 1 | 4; // 4 is table index with VEX, 0f escape + prefix_rex = byte & 0x80 ? 0 : PREFIX_REXR; } else // 3-byte VEX or EVEX { - prefix_rex |= byte & 0x40 ? 0 : PREFIX_REXX; // SDM Vol 2A 2-15 (Dec. 2016): Ignored in 32-bit mode - prefix_rex |= mode != DECODE_64 || (byte & 0x20) ? 0 : PREFIX_REXB; + if (mode == DECODE_64) + prefix_rex = byte >> 5 ^ 0x7; if (vex_prefix == 0x62) // EVEX { if (byte & 0x08) // Bit 3 of opcode_escape must be clear. return FD_ERR_UD; opcode_escape = (byte & 0x07) | 8; // 8 is table index with EVEX - prefix_rex |= mode != DECODE_64 || (byte & 0x10) ? 0 : PREFIX_REXRR; + _Static_assert(PREFIX_REXRR == 0x10, "wrong REXRR value"); + if (mode == DECODE_64) + prefix_rex |= (byte & PREFIX_REXRR) ^ PREFIX_REXRR; } else // 3-byte VEX { @@ -251,8 +258,7 @@ prefix_end: prefix_rex |= byte & 0x80 ? PREFIX_REXW : 0; } - prefix_rep = (byte & 2) ? (byte & 3) : 0; - prefix_66 = (byte & 3) == 1; + mandatory_prefix = byte & 3; vex_operand = ((byte & 0x78) >> 3) ^ 0xf; if (vex_prefix == 0x62) // EVEX @@ -263,7 +269,8 @@ prefix_end: return FD_ERR_PARTIAL; byte = buffer[off + 3]; // prefix_evex is z:L'L/RC:b:V':aaa - prefix_evex = byte | 0x08; // Ensure that prefix_evex is non-zero. + vexl = (byte >> 5) & 3; + prefix_evex = byte | 0x100; // Ensure that prefix_evex is non-zero. if (mode == DECODE_64) // V' causes UD in 32-bit mode vex_operand |= byte & 0x08 ? 0 : 0x10; // V' else if (!(byte & 0x08)) @@ -272,8 +279,8 @@ prefix_end: } else // VEX { - prefix_rex |= byte & 0x04 ? PREFIX_VEXL : 0; - off += vex_prefix == 0xc4 ? 3 : 2; + vexl = byte & 0x04 ? 1 : 0; + off += 0xc7 - vex_prefix; // 3 for c4, 2 for c5 } skipvex:; @@ -286,9 +293,6 @@ prefix_end: // Handle mandatory prefixes (which behave like an opcode ext.). if (kind == ENTRY_TABLE_PREFIX) { - // If there is no REP/REPNZ prefix offer 66h as mandatory prefix. If - // there is a REP prefix, then the 66h prefix is ignored here. - uint8_t mandatory_prefix = prefix_rep ? prefix_rep : !!prefix_66; table_idx = table_walk(table_idx, mandatory_prefix, &kind); } @@ -306,10 +310,9 @@ prefix_end: { uint8_t index = 0; index |= prefix_rex & PREFIX_REXW ? (1 << 0) : 0; - index |= prefix_rex & PREFIX_VEXL ? (1 << 1) : 0; // When EVEX.L'L is the rounding mode, the instruction must not have // L'L constraints. - index |= (prefix_evex >> 4) & 6; + index |= vexl << 1; table_idx = table_walk(table_idx, index, &kind); } @@ -338,7 +341,6 @@ prefix_end: return FD_ERR_PARTIAL; unsigned op_byte = buffer[off - 1] | (!DESC_MODRM(desc) ? 0xc0 : 0); - unsigned vexl = !!(prefix_rex & PREFIX_VEXL); if (UNLIKELY(prefix_evex)) { // VSIB inst (gather/scatter) without mask register or w/EVEX.z is UD if (DESC_VSIB(desc) && (!(prefix_evex & 0x07) || (prefix_evex & 0x80))) @@ -351,7 +353,6 @@ prefix_end: if ((prefix_evex & 0x87) == 0x80) return FD_ERR_UD; - vexl = (prefix_evex >> 5) & 3; // Cases for SAE/RC (reg operands only): // - ER supported -> all ok // - SAE supported -> assume L'L is RC, but ignored (undocumented) @@ -369,6 +370,9 @@ prefix_end: return FD_ERR_UD; instr->evex = prefix_evex & 0x87; // clear RC, clear B } + + if (DESC_VSIB(desc)) + vex_operand &= 0xf; // EVEX.V' is used as index extension instead. } else { instr->evex = 0; } @@ -420,17 +424,15 @@ prefix_end: { FdOp* op_modreg = &instr->operands[DESC_MODREG_IDX(desc)]; unsigned reg_idx = (op_byte & 0x38) >> 3; - unsigned reg_ty = DESC_REGTY_MODREG(desc); // GPL VEC MSK - MMX SEG - op_modreg->misc = (0350761 >> (3 * reg_ty)) & 0x7; - if (LIKELY(!(reg_ty & 4))) + unsigned reg_ty = DESC_REGTY_MODREG(desc); + op_modreg->misc = reg_ty; + if (LIKELY(reg_ty < 2)) reg_idx += prefix_rex & PREFIX_REXR ? 8 : 0; - if (reg_ty == 2 && reg_idx >= 8) // REXR can't be set in 32-bit mode - return FD_ERR_UD; - if (reg_ty == 2 && (prefix_evex & 0x80)) // EVEX.z with mask as dest - return FD_ERR_UD; - if (reg_ty == 1) // PREFIX_REXRR ignored above in 32-bit mode + else if (reg_ty == 7 && (prefix_rex & PREFIX_REXR || prefix_evex & 0x80)) + return FD_ERR_UD; // REXR in 64-bit mode or EVEX.z with mask as dest + if (UNLIKELY(reg_ty == FD_RT_VEC)) // REXRR ignored above in 32-bit mode reg_idx += prefix_rex & PREFIX_REXRR ? 16 : 0; - else if (prefix_rex & PREFIX_REXRR) + else if (UNLIKELY(prefix_rex & PREFIX_REXRR)) return FD_ERR_UD; op_modreg->type = FD_OT_REG; op_modreg->size = operand_sizes[(desc->operand_sizes >> 2) & 3]; @@ -442,22 +444,22 @@ prefix_end: FdOp* op_modrm = &instr->operands[DESC_MODRM_IDX(desc)]; op_modrm->size = operand_sizes[(desc->operand_sizes >> 0) & 3]; - unsigned mod = op_byte & 0xc0; unsigned rm = op_byte & 0x07; - if (mod == 0xc0) + if (op_byte >= 0xc0) { uint8_t reg_idx = rm; - unsigned reg_ty = DESC_REGTY_MODRM(desc); // GPL VEC - - MMX FPU MSK - op_modrm->misc = (07450061 >> (3 * reg_ty)) & 0x7; - if (LIKELY(!(reg_ty & 4))) + unsigned reg_ty = DESC_REGTY_MODRM(desc); + op_modrm->misc = reg_ty; + if (LIKELY(reg_ty < 2)) reg_idx += prefix_rex & PREFIX_REXB ? 8 : 0; - if (prefix_evex && reg_ty == 1) // vector registers only + if (prefix_evex && reg_ty == 0) // vector registers only reg_idx += prefix_rex & PREFIX_REXX ? 16 : 0; op_modrm->type = FD_OT_REG; op_modrm->reg = reg_idx; } else { + unsigned mod = op_byte & 0xc0; bool vsib = UNLIKELY(DESC_VSIB(desc)); // SIB byte @@ -473,9 +475,9 @@ prefix_end: base = sib & 0x07; if (!vsib && idx == 4) idx = FD_REG_NONE; - if (vsib) { - idx |= vex_operand & 0x10; - vex_operand &= 0xf; + if (vsib && prefix_evex) { + // EVEX.V':EVEX.X:SIB.idx + idx |= prefix_evex & 0x8 ? 0 : 0x10; } op_modrm->misc = scale | idx; } @@ -488,11 +490,19 @@ prefix_end: } // EVEX.z for memory destination operand is UD. - if (DESC_MODRM_IDX(desc) == 0 && (prefix_evex & 0x80)) + if (UNLIKELY(prefix_evex & 0x80) && DESC_MODRM_IDX(desc) == 0) return FD_ERR_UD; + // RIP-relative addressing only if SIB-byte is absent + if (mod == 0 && rm == 5 && mode == DECODE_64) + op_modrm->reg = FD_REG_IP; + else if (mod == 0 && base == 5) + op_modrm->reg = FD_REG_NONE; + else + op_modrm->reg = base + (prefix_rex & PREFIX_REXB ? 8 : 0); + // EVEX.b for memory-operand without broadcast support is UD. - unsigned scale = op_modrm->size - 1; + unsigned scale = 0; if (UNLIKELY(prefix_evex & 0x10)) { if (UNLIKELY(!DESC_EVEX_BCST(desc))) return FD_ERR_UD; @@ -503,27 +513,19 @@ prefix_end: instr->segment |= scale << 6; // Store broadcast size op_modrm->type = FD_OT_MEMBCST; } else { + if (UNLIKELY(prefix_evex)) + scale = op_modrm->size - 1; op_modrm->type = FD_OT_MEM; } - // RIP-relative addressing only if SIB-byte is absent - if (mod == 0 && rm == 5 && mode == DECODE_64) - op_modrm->reg = FD_REG_IP; - else if (mod == 0 && base == 5) - op_modrm->reg = FD_REG_NONE; - else - op_modrm->reg = base + (prefix_rex & PREFIX_REXB ? 8 : 0); - - if (mod == 0x40) + if (op_byte & 0x40) { if (UNLIKELY(off + 1 > len)) return FD_ERR_PARTIAL; - instr->disp = (int8_t) LOAD_LE_1(&buffer[off]); - if (prefix_evex) - instr->disp <<= scale; + instr->disp = (int8_t) LOAD_LE_1(&buffer[off]) << scale; off += 1; } - else if (mod == 0x80 || (mod == 0 && base == 5)) + else if (op_byte & 0x80 || (mod == 0 && base == 5)) { if (UNLIKELY(off + 4 > len)) return FD_ERR_PARTIAL; @@ -550,11 +552,11 @@ prefix_end: // EVEX.vvvv to refer to non-vector registers. Verified in parseinstrs. operand->reg = vex_operand | DESC_ZEROREG_VAL(desc); - unsigned reg_ty = DESC_REGTY_VEXREG(desc); // GPL VEC MSK FPU + unsigned reg_ty = DESC_REGTY_VEXREG(desc); // VEC GPL MSK FPU // In 64-bit mode: UD if FD_RT_MASK and vex_operand&8 != 0 if (reg_ty == 2 && vex_operand >= 8) return FD_ERR_UD; - operand->misc = (04761 >> (3 * reg_ty)) & 0x7; + operand->misc = (04710 >> (3 * reg_ty)) & 0x7; } else if (vex_operand != 0) { @@ -562,7 +564,7 @@ prefix_end: return FD_ERR_UD; } - uint32_t imm_control = DESC_IMM_CONTROL(desc); + uint32_t imm_control = UNLIKELY(DESC_IMM_CONTROL(desc)); if (UNLIKELY(imm_control == 1)) { // 1 = immediate constant 1, used for shifts diff --git a/fadec.h b/fadec.h index 9ffc9b7..4a1c338 100644 --- a/fadec.h +++ b/fadec.h @@ -53,8 +53,8 @@ typedef enum { } FdOpType; typedef enum { - /** Register type is encoded in mnemonic **/ - FD_RT_IMP = 0, + /** Vector (SSE/AVX) register XMMn/YMMn/ZMMn **/ + FD_RT_VEC = 0, /** Low general purpose register **/ FD_RT_GPL = 1, /** High-byte general purpose register **/ @@ -65,8 +65,6 @@ typedef enum { FD_RT_FPU = 4, /** MMX register MMn **/ FD_RT_MMX = 5, - /** Vector (SSE/AVX) register XMMn/YMMn/ZMMn **/ - FD_RT_VEC = 6, /** Vector mask (AVX-512) register Kn **/ FD_RT_MASK = 7, /** Bound register BNDn **/ diff --git a/parseinstrs.py b/parseinstrs.py index 8df69c0..943f41f 100644 --- a/parseinstrs.py +++ b/parseinstrs.py @@ -181,12 +181,12 @@ class InstrDesc(NamedTuple): flags: FrozenSet[str] OPKIND_REGTYS = { - ("modrm", "GP"): 0, ("modreg", "GP"): 0, ("vexreg", "GP"): 0, - ("modrm", "XMM"): 1, ("modreg", "XMM"): 1, ("vexreg", "XMM"): 1, - ("modrm", "MMX"): 4, ("modreg", "MMX"): 4, - ("modrm", "FPU"): 5, ("vexreg", "FPU"): 3, - ("modrm", "MASK"): 6, ("modreg", "MASK"): 2, ("vexreg", "MASK"): 2, - ("modreg", "SEG"): 5, + ("modrm", "GP"): 1, ("modreg", "GP"): 1, ("vexreg", "GP"): 1, + ("modrm", "XMM"): 0, ("modreg", "XMM"): 0, ("vexreg", "XMM"): 0, + ("modrm", "MMX"): 5, ("modreg", "MMX"): 5, + ("modrm", "FPU"): 4, ("vexreg", "FPU"): 3, + ("modrm", "MASK"): 7, ("modreg", "MASK"): 7, ("vexreg", "MASK"): 2, + ("modreg", "SEG"): 3, ("modreg", "DR"): 0, # handled in code ("modreg", "CR"): 0, # handled in code ("modrm", "MEM"): 0,