decode: Implement 16-bit ModRM decoding

This commit is contained in:
Alexis Engelke
2023-02-16 09:14:34 +01:00
parent 2c21073379
commit 49f5b7b338
2 changed files with 1159 additions and 40 deletions

File diff suppressed because it is too large Load Diff

106
decode.c
View File

@@ -9,9 +9,11 @@
#ifdef __GNUC__ #ifdef __GNUC__
#define LIKELY(x) __builtin_expect((x), 1) #define LIKELY(x) __builtin_expect((x), 1)
#define UNLIKELY(x) __builtin_expect((x), 0) #define UNLIKELY(x) __builtin_expect((x), 0)
#define ASSUME(x) do { if (!(x)) __builtin_unreachable(); } while (0)
#else #else
#define LIKELY(x) (x) #define LIKELY(x) (x)
#define UNLIKELY(x) (x) #define UNLIKELY(x) (x)
#define ASSUME(x) ((void) 0)
#endif #endif
// Defines FD_TABLE_OFFSET_32 and FD_TABLE_OFFSET_64, if available // Defines FD_TABLE_OFFSET_32 and FD_TABLE_OFFSET_64, if available
@@ -442,9 +444,61 @@ fd_decode(const uint8_t* buffer, size_t len_sz, int mode_int, uintptr_t address,
} }
else else
{ {
unsigned mod = op_byte & 0xc0;
bool vsib = UNLIKELY(DESC_VSIB(desc)); bool vsib = UNLIKELY(DESC_VSIB(desc));
// EVEX.z for memory destination operand is UD.
if (UNLIKELY(prefix_evex & 0x80) && DESC_MODRM_IDX(desc) == 0)
return FD_ERR_UD;
// EVEX.b for memory-operand without broadcast support is UD.
unsigned dispscale = 0;
if (UNLIKELY(prefix_evex & 0x10)) {
if (UNLIKELY(!DESC_EVEX_BCST(desc)))
return FD_ERR_UD;
if (UNLIKELY(DESC_EVEX_BCST16(desc)))
dispscale = 1;
else
dispscale = prefix_rex & PREFIX_REXW ? 3 : 2;
instr->segment |= dispscale << 6; // Store broadcast size
op_modrm->type = FD_OT_MEMBCST;
} else {
if (UNLIKELY(prefix_evex))
dispscale = op_modrm->size - 1;
op_modrm->type = FD_OT_MEM;
}
// 16-bit address size implies different ModRM encoding
if (UNLIKELY(addr_size == 1)) {
ASSUME(mode == DECODE_32);
if (vsib) // 16-bit address size + VSIB is UD
return FD_ERR_UD;
if (rm < 6)
op_modrm->misc = rm & 1 ? FD_REG_DI : FD_REG_SI;
else
op_modrm->misc = FD_REG_NONE;
if (rm < 4)
op_modrm->reg = rm & 2 ? FD_REG_BP : FD_REG_BX;
else if (rm < 6 || (op_byte & 0xc7) == 0x06)
op_modrm->reg = FD_REG_NONE;
else
op_modrm->reg = rm == 6 ? FD_REG_BP : FD_REG_BX;
const uint8_t* dispbase = &buffer[off];
if (op_byte & 0x40) {
if (UNLIKELY((off += 1) > len))
return FD_ERR_PARTIAL;
instr->disp = (int8_t) LOAD_LE_1(dispbase) << dispscale;
} else if (op_byte & 0x80 || (op_byte & 0xc7) == 0x06) {
if (UNLIKELY((off += 2) > len))
return FD_ERR_PARTIAL;
instr->disp = (int16_t) LOAD_LE_2(dispbase);
} else {
instr->disp = 0;
}
goto end_modrm;
}
// SIB byte // SIB byte
uint8_t base = rm; uint8_t base = rm;
if (rm == 4) if (rm == 4)
@@ -472,53 +526,27 @@ fd_decode(const uint8_t* buffer, size_t len_sz, int mode_int, uintptr_t address,
op_modrm->misc = FD_REG_NONE; op_modrm->misc = FD_REG_NONE;
} }
// EVEX.z for memory destination operand is UD.
if (UNLIKELY(prefix_evex & 0x80) && DESC_MODRM_IDX(desc) == 0)
return FD_ERR_UD;
// RIP-relative addressing only if SIB-byte is absent // RIP-relative addressing only if SIB-byte is absent
if (mod == 0 && rm == 5 && mode == DECODE_64) if (op_byte < 0x40 && rm == 5 && mode == DECODE_64)
op_modrm->reg = FD_REG_IP; op_modrm->reg = FD_REG_IP;
else if (mod == 0 && base == 5) else if (op_byte < 0x40 && base == 5)
op_modrm->reg = FD_REG_NONE; op_modrm->reg = FD_REG_NONE;
else else
op_modrm->reg = base + (prefix_rex & PREFIX_REXB ? 8 : 0); op_modrm->reg = base + (prefix_rex & PREFIX_REXB ? 8 : 0);
// EVEX.b for memory-operand without broadcast support is UD. const uint8_t* dispbase = &buffer[off];
unsigned scale = 0; if (op_byte & 0x40) {
if (UNLIKELY(prefix_evex & 0x10)) { if (UNLIKELY((off += 1) > len))
if (UNLIKELY(!DESC_EVEX_BCST(desc))) return FD_ERR_PARTIAL;
return FD_ERR_UD; instr->disp = (int8_t) LOAD_LE_1(dispbase) << dispscale;
if (UNLIKELY(DESC_EVEX_BCST16(desc))) } else if (op_byte & 0x80 || (op_byte < 0x40 && base == 5)) {
scale = 1; if (UNLIKELY((off += 4) > len))
else return FD_ERR_PARTIAL;
scale = prefix_rex & PREFIX_REXW ? 3 : 2; instr->disp = (int32_t) LOAD_LE_4(dispbase);
instr->segment |= scale << 6; // Store broadcast size
op_modrm->type = FD_OT_MEMBCST;
} else { } else {
if (UNLIKELY(prefix_evex))
scale = op_modrm->size - 1;
op_modrm->type = FD_OT_MEM;
}
if (op_byte & 0x40)
{
if (UNLIKELY(off + 1 > len))
return FD_ERR_PARTIAL;
instr->disp = (int8_t) LOAD_LE_1(&buffer[off]) << scale;
off += 1;
}
else if (op_byte & 0x80 || (mod == 0 && base == 5))
{
if (UNLIKELY(off + 4 > len))
return FD_ERR_PARTIAL;
instr->disp = (int32_t) LOAD_LE_4(&buffer[off]);
off += 4;
}
else
{
instr->disp = 0; instr->disp = 0;
} }
end_modrm:;
} }
} }