Compare commits

...

10 Commits

Author SHA1 Message Date
T0b1
9263c3d6b2 wasm support 2023-04-28 20:41:17 +02:00
Alexis Engelke
e2480e9f85 instrs: Fix VMOVD_G2X with W1 in 32-bit mode 2023-04-24 22:21:00 +02:00
Alexis Engelke
6abc971576 decode: Move instr-width to legacy path
Very few instructions set use instrwidth, so move this check as well to
the legacy path. The only affected common instructions are RET and
LEAVE.
2023-04-24 08:55:56 +02:00
Alexis Engelke
48f886e130 decode: Group all VSIB handling in single branch
Most instructions don't use VSIB encoding, so move all VSIB-related
handling to a single block behind a single branch.
2023-04-23 11:55:45 +02:00
Alexis Engelke
1290e9f094 decode: Group all EVEX-handling for ModRM operands
Most instructions aren't EVEX-encoded, so hide all uncommon paths behind
a single branch.
2023-04-23 11:29:34 +02:00
Alexis Engelke
bbc1b0b648 decode: Use macros for all desc accesses [NFC] 2023-04-23 08:57:08 +02:00
Alexis Engelke
8dad665751 encode: Fix unhelpful warning [NFC] 2023-04-23 08:57:08 +02:00
Alexis Engelke
247acd6221 decode: Unify VEX opcode escape extraction 2023-04-23 08:57:08 +02:00
Alexis Engelke
239be46d4a format: Fix out-of-bounds reads for strings 2023-04-23 08:57:08 +02:00
Alexis Engelke
0297f66de6 decode-test: Add VPEXTRB sil case 2023-04-23 08:57:08 +02:00
8 changed files with 101 additions and 81 deletions

View File

@@ -1449,6 +1449,9 @@ main(int argc, char** argv)
TEST("\xc5\xf9\x6e\xc8", "vmovd xmm1, eax");
TEST64("\xc4\xe1\xf9\x6e\xc8", "vmovq xmm1, rax");
TEST32("\xc4\xe1\xf9\x6e\xc8", "vmovd xmm1, eax");
TEST("\xc5\xf9\x7e\xc8", "vmovd eax, xmm1");
TEST64("\xc4\xe1\xf9\x7e\xc8", "vmovq rax, xmm1");
TEST32("\xc4\xe1\xf9\x7e\xc8", "vmovd eax, xmm1");
TEST("\xc5\xf2\x10\xc2", "vmovss xmm0, xmm1, xmm2");
TEST("\xc5\xf6\x10\xc2", "vmovss xmm0, xmm1, xmm2"); // VEX.L=1
TEST("\xc5\xfa\x10\x04\x25\x34\x12\x00\x00", "vmovss xmm0, dword ptr [0x1234]");
@@ -1547,6 +1550,7 @@ main(int argc, char** argv)
TEST("\xc4\xe3\x71\x20\xc0\x00", "vpinsrb xmm0, xmm1, al, 0x0");
TEST("\xc4\xe3\xf1\x20\xc0\x00", "vpinsrb xmm0, xmm1, al, 0x0");
TEST("\xc4\xe3\x71\x20\xc6\x00", "vpinsrb xmm0, xmm1, sil, 0x0");
TEST("\xc4\xe1\x71\xc4\xc0\x00", "vpinsrw xmm0, xmm1, ax, 0x0");
TEST("\xc4\xe1\xf1\xc4\xc0\x00", "vpinsrw xmm0, xmm1, ax, 0x0");
TEST("\xc4\xe3\x71\x22\xc0\x00", "vpinsrd xmm0, xmm1, eax, 0x0");

View File

@@ -91,6 +91,10 @@ struct InstrDesc
#define DESC_LOCK(desc) (((desc)->operand_indices >> 11) & 1)
#define DESC_VSIB(desc) (((desc)->operand_indices >> 15) & 1)
#define DESC_OPSIZE(desc) (((desc)->reg_types >> 11) & 7)
#define DESC_MODRM_SIZE(desc) (((desc)->operand_sizes >> 0) & 3)
#define DESC_MODREG_SIZE(desc) (((desc)->operand_sizes >> 2) & 3)
#define DESC_VEXREG_SIZE(desc) (((desc)->operand_sizes >> 4) & 3)
#define DESC_IMM_SIZE(desc) (((desc)->operand_sizes >> 6) & 3)
#define DESC_LEGACY(desc) (((desc)->operand_sizes >> 8) & 1)
#define DESC_SIZE_FIX1(desc) (((desc)->operand_sizes >> 10) & 7)
#define DESC_SIZE_FIX2(desc) (((desc)->operand_sizes >> 13) & 3)
@@ -245,18 +249,17 @@ fd_decode(const uint8_t* buffer, size_t len_sz, int mode_int, uintptr_t address,
{
if (byte & 0x08) // Bit 3 of opcode_escape must be clear.
return FD_ERR_UD;
opcode_escape = (byte & 0x07);
_Static_assert(PREFIX_REXRR == 0x10, "wrong REXRR value");
if (mode == DECODE_64)
prefix_rex |= (byte & PREFIX_REXRR) ^ PREFIX_REXRR;
}
else // 3-byte VEX
{
if (byte & 0x1c) // Bits 4:2 of opcode_escape must be clear.
if (byte & 0x18) // Bits 4:3 of opcode_escape must be clear.
return FD_ERR_UD;
opcode_escape = (byte & 0x03); // 4 is table index with VEX
}
opcode_escape = (byte & 0x07);
if (UNLIKELY(opcode_escape == 0)) {
int prefix_len = vex_prefix == 0x62 ? 4 : 3;
// Pretend to decode the prefix plus one opcode byte.
@@ -451,14 +454,14 @@ direct:
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];
op_modreg->size = operand_sizes[DESC_MODREG_SIZE(desc)];
op_modreg->reg = reg_idx;
}
if (DESC_HAS_MODRM(desc))
{
FdOp* op_modrm = &instr->operands[DESC_MODRM_IDX(desc)];
op_modrm->size = operand_sizes[(desc->operand_sizes >> 0) & 3];
op_modrm->size = operand_sizes[DESC_MODRM_SIZE(desc)];
unsigned rm = op_byte & 0x07;
if (op_byte >= 0xc0)
@@ -475,33 +478,35 @@ direct:
}
else
{
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)))
if (UNLIKELY(prefix_evex)) {
// EVEX.z for memory destination operand is UD.
if (UNLIKELY(prefix_evex & 0x80) && DESC_MODRM_IDX(desc) == 0)
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))
// EVEX.b for memory-operand without broadcast support is UD.
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 {
dispscale = op_modrm->size - 1;
op_modrm->type = FD_OT_MEM;
}
} else {
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
if (UNLIKELY(DESC_VSIB(desc))) // 16-bit addr size + VSIB is UD
return FD_ERR_UD;
if (rm < 6)
op_modrm->misc = rm & 1 ? FD_REG_DI : FD_REG_SI;
@@ -532,8 +537,7 @@ direct:
// SIB byte
uint8_t base = rm;
if (rm == 4)
{
if (rm == 4) {
if (UNLIKELY(off >= len))
return FD_ERR_PARTIAL;
uint8_t sib = buffer[off++];
@@ -541,22 +545,25 @@ direct:
unsigned idx = (sib & 0x38) >> 3;
idx += prefix_rex & PREFIX_REXX ? 8 : 0;
base = sib & 0x07;
if (!vsib && idx == 4)
if (idx == 4)
idx = FD_REG_NONE;
if (vsib && prefix_evex) {
// EVEX.V':EVEX.X:SIB.idx
idx |= prefix_evex & 0x8 ? 0 : 0x10;
}
op_modrm->misc = scale | idx;
}
else
{
// VSIB must have a memory operand with SIB byte.
if (vsib)
return FD_ERR_UD;
} else {
op_modrm->misc = FD_REG_NONE;
}
if (UNLIKELY(DESC_VSIB(desc))) {
// VSIB must have a memory operand with SIB byte.
if (rm != 4)
return FD_ERR_UD;
_Static_assert(FD_REG_NONE == 0x3f, "unexpected FD_REG_NONE");
// idx 4 is valid for VSIB
if ((op_modrm->misc & 0x3f) == FD_REG_NONE)
op_modrm->misc &= 0xc4;
if (prefix_evex) // EVEX.V':EVEX.X:SIB.idx
op_modrm->misc |= prefix_evex & 0x8 ? 0 : 0x10;
}
// RIP-relative addressing only if SIB-byte is absent
if (op_byte < 0x40 && rm == 5 && mode == DECODE_64)
op_modrm->reg = FD_REG_IP;
@@ -592,7 +599,7 @@ direct:
} else {
operand->type = FD_OT_REG;
// Without VEX prefix, this encodes an implicit register
operand->size = operand_sizes[(desc->operand_sizes >> 4) & 3];
operand->size = operand_sizes[DESC_VEXREG_SIZE(desc)];
if (mode == DECODE_32)
vex_operand &= 0x7;
// Note: 32-bit will never UD here. EVEX.V' is caught above already.
@@ -628,7 +635,7 @@ direct:
// 2 = memory, address-sized, used for mov with moffs operand
FdOp* operand = &instr->operands[DESC_IMM_IDX(desc)];
operand->type = FD_OT_MEM;
operand->size = operand_sizes[(desc->operand_sizes >> 6) & 3];
operand->size = operand_sizes[DESC_IMM_SIZE(desc)];
operand->reg = FD_REG_NONE;
operand->misc = FD_REG_NONE;
@@ -675,9 +682,9 @@ direct:
if (UNLIKELY(off + 1 > len))
return FD_ERR_PARTIAL;
instr->imm = (int8_t) LOAD_LE_1(&buffer[off++]);
operand->size = desc->operand_sizes & 0x40 ? 1 : op_size;
operand->size = DESC_IMM_SIZE(desc) & 1 ? 1 : op_size;
} else {
operand->size = operand_sizes[(desc->operand_sizes >> 6) & 3];
operand->size = operand_sizes[DESC_IMM_SIZE(desc)];
uint8_t imm_size;
if (UNLIKELY(instr->type == FDI_RET || instr->type == FDI_RETF ||
@@ -760,9 +767,9 @@ skip_modrm:
return FD_ERR_UD;
}
instr->operandsz = 0;
} else {
instr->operandsz = UNLIKELY(DESC_INSTR_WIDTH(desc)) ? op_size - 1 : 0;
} else {
instr->operandsz = 0;
}
instr->size = off;

View File

@@ -274,36 +274,36 @@ struct EncodingInfo {
const struct EncodingInfo encoding_infos[ENC_MAX] = {
[ENC_INVALID] = { 0 },
[ENC_NP] = { 0 },
[ENC_M] = { .modrm = 0^3 },
[ENC_M1] = { .modrm = 0^3, .immctl = 1, .immidx = 1 },
[ENC_MI] = { .modrm = 0^3, .immctl = 4, .immidx = 1 },
[ENC_MC] = { .modrm = 0^3, .zregidx = 1^3, .zregval = 1 },
[ENC_MR] = { .modrm = 0^3, .modreg = 1^3 },
[ENC_RM] = { .modrm = 1^3, .modreg = 0^3 },
[ENC_RMA] = { .modrm = 1^3, .modreg = 0^3, .zregidx = 2^3, .zregval = 0 },
[ENC_MRI] = { .modrm = 0^3, .modreg = 1^3, .immctl = 4, .immidx = 2 },
[ENC_RMI] = { .modrm = 1^3, .modreg = 0^3, .immctl = 4, .immidx = 2 },
[ENC_MRC] = { .modrm = 0^3, .modreg = 1^3, .zregidx = 2^3, .zregval = 1 },
[ENC_AM] = { .modrm = 1^3, .zregidx = 0^3, .zregval = 0 },
[ENC_MA] = { .modrm = 0^3, .zregidx = 1^3, .zregval = 0 },
[ENC_M] = { .modrm = 0x0^3 },
[ENC_M1] = { .modrm = 0x0^3, .immctl = 1, .immidx = 1 },
[ENC_MI] = { .modrm = 0x0^3, .immctl = 4, .immidx = 1 },
[ENC_MC] = { .modrm = 0x0^3, .zregidx = 0x1^3, .zregval = 1 },
[ENC_MR] = { .modrm = 0x0^3, .modreg = 0x1^3 },
[ENC_RM] = { .modrm = 0x1^3, .modreg = 0x0^3 },
[ENC_RMA] = { .modrm = 0x1^3, .modreg = 0x0^3, .zregidx = 0x2^3, .zregval = 0 },
[ENC_MRI] = { .modrm = 0x0^3, .modreg = 0x1^3, .immctl = 4, .immidx = 2 },
[ENC_RMI] = { .modrm = 0x1^3, .modreg = 0x0^3, .immctl = 4, .immidx = 2 },
[ENC_MRC] = { .modrm = 0x0^3, .modreg = 0x1^3, .zregidx = 0x2^3, .zregval = 1 },
[ENC_AM] = { .modrm = 0x1^3, .zregidx = 0x0^3, .zregval = 0 },
[ENC_MA] = { .modrm = 0x0^3, .zregidx = 0x1^3, .zregval = 0 },
[ENC_I] = { .immctl = 4, .immidx = 0 },
[ENC_IA] = { .zregidx = 0^3, .zregval = 0, .immctl = 4, .immidx = 1 },
[ENC_O] = { .modreg = 0^3 },
[ENC_OI] = { .modreg = 0^3, .immctl = 4, .immidx = 1 },
[ENC_OA] = { .modreg = 0^3, .zregidx = 1^3, .zregval = 0 },
[ENC_IA] = { .zregidx = 0x0^3, .zregval = 0, .immctl = 4, .immidx = 1 },
[ENC_O] = { .modreg = 0x0^3 },
[ENC_OI] = { .modreg = 0x0^3, .immctl = 4, .immidx = 1 },
[ENC_OA] = { .modreg = 0x0^3, .zregidx = 0x1^3, .zregval = 0 },
[ENC_S] = { 0 },
[ENC_A] = { .zregidx = 0^3, .zregval = 0 },
[ENC_A] = { .zregidx = 0x0^3, .zregval = 0 },
[ENC_D] = { .immctl = 6, .immidx = 0 },
[ENC_FD] = { .zregidx = 0^3, .zregval = 0, .immctl = 2, .immidx = 1 },
[ENC_TD] = { .zregidx = 1^3, .zregval = 0, .immctl = 2, .immidx = 0 },
[ENC_RVM] = { .modrm = 2^3, .modreg = 0^3, .vexreg = 1^3 },
[ENC_RVMI] = { .modrm = 2^3, .modreg = 0^3, .vexreg = 1^3, .immctl = 4, .immidx = 3 },
[ENC_RVMR] = { .modrm = 2^3, .modreg = 0^3, .vexreg = 1^3, .immctl = 3, .immidx = 3 },
[ENC_RMV] = { .modrm = 1^3, .modreg = 0^3, .vexreg = 2^3 },
[ENC_VM] = { .modrm = 1^3, .vexreg = 0^3 },
[ENC_VMI] = { .modrm = 1^3, .vexreg = 0^3, .immctl = 4, .immidx = 2 },
[ENC_MVR] = { .modrm = 0^3, .modreg = 2^3, .vexreg = 1^3 },
[ENC_MRV] = { .modrm = 0^3, .modreg = 1^3, .vexreg = 2^3 },
[ENC_FD] = { .zregidx = 0x0^3, .zregval = 0, .immctl = 2, .immidx = 1 },
[ENC_TD] = { .zregidx = 0x1^3, .zregval = 0, .immctl = 2, .immidx = 0 },
[ENC_RVM] = { .modrm = 0x2^3, .modreg = 0x0^3, .vexreg = 0x1^3 },
[ENC_RVMI] = { .modrm = 0x2^3, .modreg = 0x0^3, .vexreg = 0x1^3, .immctl = 4, .immidx = 3 },
[ENC_RVMR] = { .modrm = 0x2^3, .modreg = 0x0^3, .vexreg = 0x1^3, .immctl = 3, .immidx = 3 },
[ENC_RMV] = { .modrm = 0x1^3, .modreg = 0x0^3, .vexreg = 0x2^3 },
[ENC_VM] = { .modrm = 0x1^3, .vexreg = 0x0^3 },
[ENC_VMI] = { .modrm = 0x1^3, .vexreg = 0x0^3, .immctl = 4, .immidx = 2 },
[ENC_MVR] = { .modrm = 0x0^3, .modreg = 0x2^3, .vexreg = 0x1^3 },
[ENC_MRV] = { .modrm = 0x0^3, .modreg = 0x1^3, .vexreg = 0x2^3 },
};
static const uint64_t alt_tab[] = {

View File

@@ -26,7 +26,7 @@ struct FdStr {
unsigned sz;
};
#define fd_stre(s) ((struct FdStr) { (s "\0\0\0\0\0\0\0\0\0\0"), sizeof (s)-1 })
#define fd_stre(s) ((struct FdStr) { (s "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"), sizeof (s)-1 })
static char*
fd_strpcat(char* restrict dst, struct FdStr src) {

View File

@@ -1026,7 +1026,7 @@ VEX.F2.0f7c RVM Vx Hx Wx - VHADDPS F=AVX
VEX.66.0f7d RVM Vx Hx Wx - VHSUBPD F=AVX
VEX.F2.0f7d RVM Vx Hx Wx - VHSUBPS F=AVX
VEX.66.W0.L0.0f7e MR Ey Vy - - VMOVD_X2G F=AVX ENC_NOSZ
VEX.66.W1.L0.0f7e MR Ey Vy - - VMOVQ_X2G I64 F=AVX ENC_NOSZ
VEX.66.W1.L0.0f7e MR Ey Vy - - VMOVD_X2G I64 F=AVX ENC_NOSZ
VEX.66.W1.L0.0f7e MR Ey Vy - - VMOVQ_X2G O64 F=AVX ENC_NOSZ
VEX.F3.L0.0f7e RM Vq Wq - - VMOVQ F=AVX ENC_NOSZ
VEX.66.0f7f MR Wx Vx - - VMOVDQA F=AVX

View File

@@ -11,6 +11,12 @@ if not py_version.version_compare('>=3.6')
endif
cc = meson.get_compiler('c')
if get_option('wasm_build')
add_project_arguments('--target=wasm32', language: 'c')
add_project_arguments(['-nostdlib', '-fvisibility=default'], language: 'c')
endif
if cc.has_argument('-fstrict-aliasing')
add_project_arguments('-fstrict-aliasing', language: 'c')
endif
@@ -99,11 +105,13 @@ fadec = declare_dependency(link_with: libfadec,
sources: tables)
install_headers(headers)
foreach component : components
test(component, executable('@0@-test'.format(component),
'@0@-test.c'.format(component),
dependencies: fadec))
endforeach
if get_option('wasm_build') == false
foreach component : components
test(component, executable('@0@-test'.format(component),
'@0@-test.c'.format(component),
dependencies: fadec))
endforeach
endif
if meson.version().version_compare('>=0.54.0')
meson.override_dependency('fadec', fadec)

View File

@@ -4,3 +4,4 @@ option('with_decode', type: 'boolean', value: true)
option('with_encode', type: 'boolean', value: true)
# encode2 is off-by-default to reduce size and compile-time
option('with_encode2', type: 'boolean', value: false)
option('wasm_build', type: 'boolean', value: false)

View File

@@ -276,17 +276,17 @@ class InstrDesc(NamedTuple):
else: # either empty or GP operand size
dynsizes = [OpKind.SZ_OP]
if "SZ8" in self.flags:
extraflags["opsize"] = 1
dynsizes = []
if "D64" in self.flags: extraflags["opsize"] = 2
if "F64" in self.flags: extraflags["opsize"] = 3
extraflags["instr_width"] = "INSTR_WIDTH" in self.flags
extraflags["lock"] = "LOCK" in self.flags
if "SZ8" in self.flags or mnem in ("MOVSX", "MOVZX", "XCHG_NOP", "3DNOW"):
if "SZ8" not in self.flags and "INSTR_WIDTH" in self.flags:
raise Exception("legacy instr with +w without SZ8")
if (self.flags & {"SZ8", "INSTR_WIDTH"} or
mnem in ("MOVSX", "MOVZX", "XCHG_NOP", "3DNOW")):
extraflags["legacy"] = 1
# INSTR_WIDTH defaults to zero, so only enable when SZ8 is unset
if "INSTR_WIDTH" in self.flags and "SZ8" not in self.flags:
extraflags["instr_width"] = 1
imm_byte = self.imm_size(4) == 1
extraflags["imm_control"] = flags.imm_control | imm_byte