Source code
Revision control
Copy as Markdown
Other Tools
/***************************************************************************************************
Zyan Disassembler Library (Zydis)
Original Author : Mappa
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
***************************************************************************************************/
// ReSharper disable CppClangTidyClangDiagnosticSwitchEnum
// ReSharper disable CppClangTidyClangDiagnosticCoveredSwitchDefault
// ReSharper disable CppClangTidyClangDiagnosticImplicitFallthrough
#include "zydis/Zycore/LibC.h"
#include "zydis/Zydis/Encoder.h"
#include "zydis/Zydis/Utils.h"
#include "zydis/Zydis/Internal/EncoderData.h"
#include "zydis/Zydis/Internal/SharedData.h"
/* ============================================================================================== */
/* Macros */
/* ============================================================================================== */
/* ---------------------------------------------------------------------------------------------- */
/* Constants */
/* ---------------------------------------------------------------------------------------------- */
#define ZYDIS_OPSIZE_MAP_BYTEOP 1
#define ZYDIS_OPSIZE_MAP_DEFAULT64 4
#define ZYDIS_OPSIZE_MAP_FORCE64 5
#define ZYDIS_ADSIZE_MAP_IGNORED 1
#define ZYDIS_LEGACY_SEGMENTS (ZYDIS_ATTRIB_HAS_SEGMENT_CS | \
ZYDIS_ATTRIB_HAS_SEGMENT_SS | \
ZYDIS_ATTRIB_HAS_SEGMENT_DS | \
ZYDIS_ATTRIB_HAS_SEGMENT_ES)
#define ZYDIS_ENCODABLE_PREFIXES_NO_SEGMENTS (ZYDIS_ENCODABLE_PREFIXES ^ \
ZYDIS_ATTRIB_HAS_SEGMENT)
/* ---------------------------------------------------------------------------------------------- */
/* ============================================================================================== */
/* Internal enums and types */
/* ============================================================================================== */
/**
* Usage of `REX.W` prefix makes it impossible to use some byte-sized registers. Values of this
* enum are used to track and facilitate enforcement of these restrictions.
*/
typedef enum ZydisEncoderRexType_
{
ZYDIS_REX_TYPE_UNKNOWN,
ZYDIS_REX_TYPE_REQUIRED,
ZYDIS_REX_TYPE_FORBIDDEN,
/**
* Maximum value of this enum.
*/
ZYDIS_REX_TYPE_MAX_VALUE = ZYDIS_REX_TYPE_FORBIDDEN,
/**
* The minimum number of bits required to represent all values of this enum.
*/
ZYDIS_REX_TYPE_REQUIRED_BITS = ZYAN_BITS_TO_REPRESENT(ZYDIS_REX_TYPE_MAX_VALUE)
} ZydisEncoderRexType;
/**
* Primary structure used during instruction matching phase. Once filled it contains information
* about matched instruction definition and some values deduced from encoder request. It gets
* converted to `ZydisEncoderInstruction` during instruction building phase.
*/
typedef struct ZydisEncoderInstructionMatch_
{
/**
* A pointer to the `ZydisEncoderRequest` instance.
*/
const ZydisEncoderRequest *request;
/**
* A pointer to the `ZydisEncodableInstruction` instance.
*/
const ZydisEncodableInstruction *definition;
/**
* A pointer to the `ZydisInstructionDefinition` instance.
*/
const ZydisInstructionDefinition *base_definition;
/**
* A pointer to the `ZydisOperandDefinition` array.
*/
const ZydisOperandDefinition *operands;
/**
* Encodable attributes for this instruction.
*/
ZydisInstructionAttributes attributes;
/**
* Effective operand size attribute.
*/
ZyanU8 eosz;
/**
* Effective address size attribute.
*/
ZyanU8 easz;
/**
* Effective displacement size.
*/
ZyanU8 disp_size;
/**
* Effective immediate size.
*/
ZyanU8 imm_size;
/**
* Exponent of compressed displacement scale factor (2^cd8_scale)
*/
ZyanU8 cd8_scale;
/**
* `REX` prefix constraints.
*/
ZydisEncoderRexType rex_type;
/**
* True for special cases where operand size attribute must be lower than 64 bits.
*/
ZyanBool eosz64_forbidden;
/**
* True when instruction definition has relative operand (used for branching instructions).
*/
ZyanBool has_rel_operand;
} ZydisEncoderInstructionMatch;
/**
* Encapsulates information about writable buffer.
*/
typedef struct ZydisEncoderBuffer_
{
/**
* A pointer to actual data buffer.
*/
ZyanU8 *buffer;
/**
* Size of this buffer.
*/
ZyanUSize size;
/**
* Current write offset.
*/
ZyanUSize offset;
} ZydisEncoderBuffer;
/**
* Low-level instruction representation. Once filled this structure contains all information
* required for final instruction emission phase.
*/
typedef struct ZydisEncoderInstruction_
{
/**
* Encodable attributes for this instruction.
*/
ZydisInstructionAttributes attributes;
/**
* The instruction encoding.
*/
ZydisInstructionEncoding encoding;
/**
* The opcode map.
*/
ZydisOpcodeMap opcode_map;
/**
* The opcode.
*/
ZyanU8 opcode;
/**
* The `vvvv` field (`VEX`, `EVEX`, `MVEX`, `XOP`).
*/
ZyanU8 vvvv;
/**
* The `sss` field (`MVEX`).
*/
ZyanU8 sss;
/**
* The mask register ID.
*/
ZyanU8 mask;
/**
* The vector length.
*/
ZyanU8 vector_length;
/**
* The `mod` component of Mod/RM byte.
*/
ZyanU8 mod;
/**
* The `reg` component of Mod/RM byte.
*/
ZyanU8 reg;
/**
* The `rm` component of Mod/RM byte.
*/
ZyanU8 rm;
/**
* The scale component of SIB byte.
*/
ZyanU8 scale;
/**
* The index component of SIB byte.
*/
ZyanU8 index;
/**
* The base component of SIB byte.
*/
ZyanU8 base;
/**
* The `REX.W` bit.
*/
ZyanBool rex_w;
/**
* True if using zeroing mask (`EVEX`).
*/
ZyanBool zeroing;
/**
* True if using eviction hint (`MVEX`).
*/
ZyanBool eviction_hint;
/**
* Size of displacement value.
*/
ZyanU8 disp_size;
/**
* Size of immediate value.
*/
ZyanU8 imm_size;
/**
* The displacement value.
*/
ZyanU64 disp;
/**
* The immediate value.
*/
ZyanU64 imm;
} ZydisEncoderInstruction;
/* ============================================================================================== */
/* Internal functions */
/* ============================================================================================== */
/**
* Converts `ZydisInstructionEncoding` to `ZydisEncodableEncoding`.
*
* @param encoding `ZydisInstructionEncoding` value to convert.
*
* @return Equivalent `ZydisEncodableEncoding` value.
*/
static ZydisEncodableEncoding ZydisGetEncodableEncoding(ZydisInstructionEncoding encoding)
{
static const ZydisEncodableEncoding encoding_lookup[6] =
{
ZYDIS_ENCODABLE_ENCODING_LEGACY,
ZYDIS_ENCODABLE_ENCODING_3DNOW,
ZYDIS_ENCODABLE_ENCODING_XOP,
ZYDIS_ENCODABLE_ENCODING_VEX,
ZYDIS_ENCODABLE_ENCODING_EVEX,
ZYDIS_ENCODABLE_ENCODING_MVEX,
};
ZYAN_ASSERT((ZyanUSize)encoding <= ZYDIS_INSTRUCTION_ENCODING_MAX_VALUE);
return encoding_lookup[encoding];
}
/**
* Converts `ZydisMachineMode` to default stack width value expressed in bits.
*
* @param machine_mode `ZydisMachineMode` value to convert.
*
* @return Stack width for requested machine mode.
*/
static ZyanU8 ZydisGetMachineModeWidth(ZydisMachineMode machine_mode)
{
ZYAN_ASSERT((ZyanUSize)machine_mode <= ZYDIS_MACHINE_MODE_MAX_VALUE);
static const ZyanU8 lookup[6] =
{
/* ZYDIS_MACHINE_MODE_LONG_64 */ 64,
/* ZYDIS_MACHINE_MODE_LONG_COMPAT_32 */ 32,
/* ZYDIS_MACHINE_MODE_LONG_COMPAT_16 */ 16,
/* ZYDIS_MACHINE_MODE_LEGACY_32 */ 32,
/* ZYDIS_MACHINE_MODE_LEGACY_16 */ 16,
/* ZYDIS_MACHINE_MODE_REAL_16 */ 16,
};
return lookup[machine_mode];
}
/**
* Converts `ZydisAddressSizeHint` to address size expressed in bits.
*
* @param hint Address size hint.
*
* @return Address size in bits.
*/
static ZyanU8 ZydisGetAszFromHint(ZydisAddressSizeHint hint)
{
ZYAN_ASSERT((ZyanUSize)hint <= ZYDIS_ADDRESS_SIZE_HINT_MAX_VALUE);
static const ZyanU8 lookup[ZYDIS_ADDRESS_SIZE_HINT_MAX_VALUE + 1] = { 0, 16, 32, 64 };
return lookup[hint];
}
/**
* Converts `ZydisOperandSizeHint` to operand size expressed in bits.
*
* @param hint Operand size hint.
*
* @return Operand size in bits.
*/
static ZyanU8 ZydisGetOszFromHint(ZydisOperandSizeHint hint)
{
ZYAN_ASSERT((ZyanUSize)hint <= ZYDIS_OPERAND_SIZE_HINT_MAX_VALUE);
static const ZyanU8 lookup[ZYDIS_OPERAND_SIZE_HINT_MAX_VALUE + 1] = { 0, 8, 16, 32, 64 };
return lookup[hint];
}
/**
* Calculates maximum size of absolute address value based on address size hint.
*
* @param request A pointer to `ZydisEncoderRequest` struct.
*
* @return Maximum address size in bits.
*/
static ZyanU8 ZydisGetMaxAddressSize(const ZydisEncoderRequest *request)
{
ZyanU8 addr_size = ZydisGetAszFromHint(request->address_size_hint);
if (addr_size == 0)
{
addr_size = ZydisGetMachineModeWidth(request->machine_mode);
}
return addr_size;
}
/**
* Calculates effective operand size.
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
* @param size_table Array of possible size values for different operand sizes.
* @param desired_size Operand size requested by caller.
* @param exact_match_mode True if desired_size must be matched exactly, false when
* "not lower than" matching is desired.
*
* @return Effective operand size in bits.
*/
static ZyanU8 ZydisGetOperandSizeFromElementSize(ZydisEncoderInstructionMatch *match,
const ZyanU16 *size_table, ZyanU16 desired_size, ZyanBool exact_match_mode)
{
if ((match->base_definition->operand_size_map == ZYDIS_OPSIZE_MAP_DEFAULT64) &&
(match->request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64))
{
if ((exact_match_mode && (size_table[2] == desired_size)) ||
(!exact_match_mode && (size_table[2] >= desired_size)))
{
return 64;
}
else if (size_table[0] == desired_size)
{
return 16;
}
}
else if ((match->base_definition->operand_size_map == ZYDIS_OPSIZE_MAP_FORCE64) &&
(match->request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64))
{
if (size_table[2] == desired_size)
{
return 64;
}
}
else
{
static const ZyanI8 eosz_priority_lookup[4][3] =
{
{ 0, 1, -1 },
{ 1, 0, -1 },
{ 1, 2, 0 },
};
const ZyanU8 eosz_index = ZydisGetMachineModeWidth(match->request->machine_mode) >> 5;
for (int i = 0; i < 3; ++i)
{
const ZyanI8 eosz_candidate = eosz_priority_lookup[eosz_index][i];
if ((eosz_candidate == -1) ||
!(match->definition->operand_sizes & (1 << eosz_candidate)))
{
continue;
}
if ((exact_match_mode && (size_table[eosz_candidate] == desired_size)) ||
(!exact_match_mode && (size_table[eosz_candidate] >= desired_size)))
{
return 16 << eosz_candidate;
}
}
}
return 0;
}
/**
* Calculates effective immediate size.
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
* @param size_table Array of possible size values for different operand sizes.
* @param min_imm_size Minimum immediate size.
*
* @return Effective operand size in bits.
*/
static ZyanU8 ZydisGetScaledImmSize(ZydisEncoderInstructionMatch *match, const ZyanU16 *size_table,
ZyanU8 min_imm_size)
{
if (match->eosz == 0)
{
match->eosz = ZydisGetOperandSizeFromElementSize(match, size_table, min_imm_size,
ZYAN_FALSE);
return match->eosz != 0 ? (ZyanU8)size_table[match->eosz >> 5] : 0;
}
const ZyanU8 index = match->eosz >> 5;
return size_table[index] >= min_imm_size ? (ZyanU8)size_table[index] : 0;
}
/**
* Calculates size of smallest integral type able to represent provided signed value.
*
* @param imm Immediate to be represented.
*
* @return Size of smallest integral type able to represent provided signed value.
*/
static ZyanU8 ZydisGetSignedImmSize(ZyanI64 imm)
{
if (imm >= ZYAN_INT8_MIN && imm <= ZYAN_INT8_MAX)
{
return 8;
}
if (imm >= ZYAN_INT16_MIN && imm <= ZYAN_INT16_MAX)
{
return 16;
}
if (imm >= ZYAN_INT32_MIN && imm <= ZYAN_INT32_MAX)
{
return 32;
}
return 64;
}
/**
* Calculates size of smallest integral type able to represent provided unsigned value.
*
* @param imm Immediate to be represented.
*
* @return Size of smallest integral type able to represent provided unsigned value.
*/
static ZyanU8 ZydisGetUnsignedImmSize(ZyanU64 imm)
{
if (imm <= ZYAN_UINT8_MAX)
{
return 8;
}
if (imm <= ZYAN_UINT16_MAX)
{
return 16;
}
if (imm <= ZYAN_UINT32_MAX)
{
return 32;
}
return 64;
}
/**
* Checks if operand encoding encodes a signed immediate value.
*
* @param encoding Operand encoding for immediate value.
*
* @return True for encodings that represent signed values, false otherwise.
*/
static ZyanBool ZydisIsImmSigned(ZydisOperandEncoding encoding)
{
switch (encoding)
{
case ZYDIS_OPERAND_ENCODING_SIMM8:
case ZYDIS_OPERAND_ENCODING_SIMM16:
case ZYDIS_OPERAND_ENCODING_SIMM32:
case ZYDIS_OPERAND_ENCODING_SIMM64:
case ZYDIS_OPERAND_ENCODING_SIMM16_32_64:
case ZYDIS_OPERAND_ENCODING_SIMM32_32_64:
case ZYDIS_OPERAND_ENCODING_SIMM16_32_32:
case ZYDIS_OPERAND_ENCODING_JIMM8:
case ZYDIS_OPERAND_ENCODING_JIMM16:
case ZYDIS_OPERAND_ENCODING_JIMM32:
case ZYDIS_OPERAND_ENCODING_JIMM64:
case ZYDIS_OPERAND_ENCODING_JIMM16_32_64:
case ZYDIS_OPERAND_ENCODING_JIMM32_32_64:
case ZYDIS_OPERAND_ENCODING_JIMM16_32_32:
case ZYDIS_OPERAND_ENCODING_DISP8:
case ZYDIS_OPERAND_ENCODING_DISP16:
case ZYDIS_OPERAND_ENCODING_DISP32:
case ZYDIS_OPERAND_ENCODING_DISP64:
case ZYDIS_OPERAND_ENCODING_DISP16_32_64:
case ZYDIS_OPERAND_ENCODING_DISP32_32_64:
case ZYDIS_OPERAND_ENCODING_DISP16_32_32:
return ZYAN_TRUE;
case ZYDIS_OPERAND_ENCODING_UIMM8:
case ZYDIS_OPERAND_ENCODING_UIMM16:
case ZYDIS_OPERAND_ENCODING_UIMM32:
case ZYDIS_OPERAND_ENCODING_UIMM64:
case ZYDIS_OPERAND_ENCODING_UIMM16_32_64:
case ZYDIS_OPERAND_ENCODING_UIMM32_32_64:
case ZYDIS_OPERAND_ENCODING_UIMM16_32_32:
case ZYDIS_OPERAND_ENCODING_IS4:
return ZYAN_FALSE;
default:
ZYAN_UNREACHABLE;
}
}
/**
* Calculates effective immediate size.
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
* @param imm Immediate value to encode.
* @param def_op Operand definition for immediate operand.
*
* @return Effective operand size in bits (0 if function failed).
*/
static ZyanU8 ZydisGetEffectiveImmSize(ZydisEncoderInstructionMatch *match, ZyanI64 imm,
const ZydisOperandDefinition *def_op)
{
ZyanU8 eisz = 0;
ZyanU8 min_size = ZydisIsImmSigned((ZydisOperandEncoding)def_op->op.encoding)
? ZydisGetSignedImmSize(imm)
: ZydisGetUnsignedImmSize((ZyanU64)imm);
switch (def_op->op.encoding)
{
case ZYDIS_OPERAND_ENCODING_UIMM8:
case ZYDIS_OPERAND_ENCODING_SIMM8:
eisz = 8;
break;
case ZYDIS_OPERAND_ENCODING_IS4:
ZYAN_ASSERT(def_op->element_type == ZYDIS_IELEMENT_TYPE_UINT8);
eisz = ((ZyanU64)imm <= 15) ? 8 : 0;
break;
case ZYDIS_OPERAND_ENCODING_UIMM16:
case ZYDIS_OPERAND_ENCODING_SIMM16:
eisz = 16;
break;
case ZYDIS_OPERAND_ENCODING_UIMM32:
case ZYDIS_OPERAND_ENCODING_SIMM32:
eisz = 32;
break;
case ZYDIS_OPERAND_ENCODING_UIMM64:
case ZYDIS_OPERAND_ENCODING_SIMM64:
eisz = 64;
break;
case ZYDIS_OPERAND_ENCODING_UIMM16_32_64:
case ZYDIS_OPERAND_ENCODING_SIMM16_32_64:
{
static const ZyanU16 simm16_32_64_sizes[3] = { 16, 32, 64 };
return ZydisGetScaledImmSize(match, simm16_32_64_sizes, min_size);
}
case ZYDIS_OPERAND_ENCODING_UIMM32_32_64:
case ZYDIS_OPERAND_ENCODING_SIMM32_32_64:
{
static const ZyanU16 simm32_32_64_sizes[3] = { 32, 32, 64 };
return ZydisGetScaledImmSize(match, simm32_32_64_sizes, min_size);
}
case ZYDIS_OPERAND_ENCODING_UIMM16_32_32:
case ZYDIS_OPERAND_ENCODING_SIMM16_32_32:
{
static const ZyanU16 simm16_32_32_sizes[3] = { 16, 32, 32 };
return ZydisGetScaledImmSize(match, simm16_32_32_sizes, min_size);
}
case ZYDIS_OPERAND_ENCODING_DISP16_32_64:
{
ZYAN_ASSERT(match->easz == 0);
const ZyanU8 addr_size = ZydisGetMaxAddressSize(match->request);
const ZyanU64 uimm = imm & (~(0xFFFFFFFFFFFFFFFFULL << (addr_size - 1) << 1));
if (min_size < addr_size && ZydisGetUnsignedImmSize(uimm) > min_size)
{
min_size = addr_size;
}
if (match->request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64)
{
if (min_size < 32)
{
min_size = 32;
}
match->easz = eisz = min_size;
}
else
{
if (min_size < 16)
{
min_size = 16;
}
if (min_size == 16 || min_size == 32)
{
match->easz = eisz = min_size;
}
}
break;
}
case ZYDIS_OPERAND_ENCODING_JIMM8:
case ZYDIS_OPERAND_ENCODING_JIMM16:
case ZYDIS_OPERAND_ENCODING_JIMM32:
case ZYDIS_OPERAND_ENCODING_JIMM64:
{
ZyanU8 jimm_index = def_op->op.encoding - ZYDIS_OPERAND_ENCODING_JIMM8;
if ((match->request->branch_width != ZYDIS_BRANCH_WIDTH_NONE) &&
(match->request->branch_width != (ZydisBranchWidth)(ZYDIS_BRANCH_WIDTH_8 + jimm_index)))
{
return 0;
}
eisz = 8 << jimm_index;
break;
}
case ZYDIS_OPERAND_ENCODING_JIMM16_32_32:
switch (match->request->branch_width)
{
case ZYDIS_BRANCH_WIDTH_NONE:
{
static const ZyanU16 jimm16_32_32_sizes[3] = { 16, 32, 32 };
return ZydisGetScaledImmSize(match, jimm16_32_32_sizes, min_size);
}
case ZYDIS_BRANCH_WIDTH_16:
eisz = 16;
break;
case ZYDIS_BRANCH_WIDTH_32:
eisz = 32;
break;
case ZYDIS_BRANCH_WIDTH_8:
case ZYDIS_BRANCH_WIDTH_64:
return 0;
default:
ZYAN_UNREACHABLE;
}
break;
default:
ZYAN_UNREACHABLE;
}
return eisz >= min_size ? eisz : 0;
}
/**
* Checks if register width is compatible with effective operand size.
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
* @param reg_width Register width in bits.
*
* @return True if width is compatible, false otherwise.
*/
static ZyanBool ZydisCheckOsz(ZydisEncoderInstructionMatch *match, ZydisRegisterWidth reg_width)
{
ZYAN_ASSERT(reg_width <= ZYAN_UINT8_MAX);
if (match->eosz == 0)
{
if (reg_width == 8)
{
return ZYAN_FALSE;
}
match->eosz = (ZyanU8)reg_width;
return ZYAN_TRUE;
}
return match->eosz == (ZyanU8)reg_width ? ZYAN_TRUE : ZYAN_FALSE;
}
/**
* Checks if register width is compatible with effective address size.
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
* @param reg_width Register width in bits.
*
* @return True if width is compatible, false otherwise.
*/
static ZyanBool ZydisCheckAsz(ZydisEncoderInstructionMatch *match, ZydisRegisterWidth reg_width)
{
ZYAN_ASSERT(reg_width <= ZYAN_UINT8_MAX);
if (match->easz == 0)
{
if ((match->request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) &&
(reg_width == 16))
{
return ZYAN_FALSE;
}
match->easz = (ZyanU8)reg_width;
return ZYAN_TRUE;
}
return match->easz == (ZyanU8)reg_width ? ZYAN_TRUE : ZYAN_FALSE;
}
/**
* Checks if specified register is valid for provided register class, encoding and machine mode.
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
* @param reg `ZydisRegister` value.
* @param reg_class Register class.
*
* @return True if register value is allowed, false otherwise.
*/
static ZyanBool ZydisIsRegisterAllowed(ZydisEncoderInstructionMatch *match, ZydisRegister reg,
ZydisRegisterClass reg_class)
{
const ZyanI8 reg_id = ZydisRegisterGetId(reg);
ZYAN_ASSERT(reg_id >= 0 && reg_id <= 31);
if (match->request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64)
{
if ((match->definition->encoding != ZYDIS_INSTRUCTION_ENCODING_EVEX) &&
(match->definition->encoding != ZYDIS_INSTRUCTION_ENCODING_MVEX) &&
(reg_class != ZYDIS_REGCLASS_GPR8) &&
(reg_id >= 16))
{
return ZYAN_FALSE;
}
}
else
{
if (reg_class == ZYDIS_REGCLASS_GPR64)
{
return ZYAN_FALSE;
}
if (reg_id >= 8)
{
return ZYAN_FALSE;
}
}
return ZYAN_TRUE;
}
/**
* Checks if specified scale value is valid for use with SIB addressing.
*
* @param scale Scale value.
*
* @return True if value is valid, false otherwise.
*/
static ZyanBool ZydisIsScaleValid(ZyanU8 scale)
{
switch (scale)
{
case 0:
case 1:
case 2:
case 4:
case 8:
return ZYAN_TRUE;
default:
return ZYAN_FALSE;
}
}
/**
* Enforces register usage constraints associated with usage of `REX` prefix.
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
* @param reg `ZydisRegister` value.
* @param addressing_mode True if checked address is used for address calculations. This
* implies more permissive checks.
*
* @return True if register usage is allowed, false otherwise.
*/
static ZyanBool ZydisValidateRexType(ZydisEncoderInstructionMatch *match, ZydisRegister reg,
ZyanBool addressing_mode)
{
switch (reg)
{
case ZYDIS_REGISTER_AL:
case ZYDIS_REGISTER_CL:
case ZYDIS_REGISTER_DL:
case ZYDIS_REGISTER_BL:
return ZYAN_TRUE;
case ZYDIS_REGISTER_AH:
case ZYDIS_REGISTER_CH:
case ZYDIS_REGISTER_DH:
case ZYDIS_REGISTER_BH:
if (match->rex_type == ZYDIS_REX_TYPE_UNKNOWN)
{
match->rex_type = ZYDIS_REX_TYPE_FORBIDDEN;
}
else if (match->rex_type == ZYDIS_REX_TYPE_REQUIRED)
{
return ZYAN_FALSE;
}
break;
case ZYDIS_REGISTER_SPL:
case ZYDIS_REGISTER_BPL:
case ZYDIS_REGISTER_SIL:
case ZYDIS_REGISTER_DIL:
case ZYDIS_REGISTER_R8B:
case ZYDIS_REGISTER_R9B:
case ZYDIS_REGISTER_R10B:
case ZYDIS_REGISTER_R11B:
case ZYDIS_REGISTER_R12B:
case ZYDIS_REGISTER_R13B:
case ZYDIS_REGISTER_R14B:
case ZYDIS_REGISTER_R15B:
if (match->rex_type == ZYDIS_REX_TYPE_UNKNOWN)
{
match->rex_type = ZYDIS_REX_TYPE_REQUIRED;
}
else if (match->rex_type == ZYDIS_REX_TYPE_FORBIDDEN)
{
return ZYAN_FALSE;
}
break;
default:
if ((ZydisRegisterGetId(reg) > 7) ||
(!addressing_mode && (ZydisRegisterGetClass(reg) == ZYDIS_REGCLASS_GPR64)))
{
if (match->rex_type == ZYDIS_REX_TYPE_UNKNOWN)
{
match->rex_type = ZYDIS_REX_TYPE_REQUIRED;
}
else if (match->rex_type == ZYDIS_REX_TYPE_FORBIDDEN)
{
return ZYAN_FALSE;
}
}
break;
}
return ZYAN_TRUE;
}
/**
* Checks if specified register is valid for use with SIB addressing.
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
* @param reg_class Register class.
* @param reg `ZydisRegister` value.
*
* @return True if register value is allowed, false otherwise.
*/
static ZyanBool ZydisIsValidAddressingClass(ZydisEncoderInstructionMatch *match,
ZydisRegisterClass reg_class, ZydisRegister reg)
{
ZyanBool result;
const ZyanBool is_64 = (match->request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64);
switch (reg_class)
{
case ZYDIS_REGCLASS_INVALID:
return ZYAN_TRUE;
case ZYDIS_REGCLASS_GPR16:
result = !is_64;
break;
case ZYDIS_REGCLASS_GPR32:
result = is_64 || ZydisRegisterGetId(reg) < 8;
break;
case ZYDIS_REGCLASS_GPR64:
result = is_64;
break;
default:
return ZYAN_FALSE;
}
return result && ZydisValidateRexType(match, reg, ZYAN_TRUE);
}
/**
* Helper function that determines correct `ModR/M.RM` value for 16-bit addressing mode.
*
* @param base `ZydisRegister` used as `SIB.base`.
* @param index `ZydisRegister` used as `SIB.index`.
*
* @return `ModR/M.RM` value (-1 if function failed).
*/
static ZyanI8 ZydisGetRm16(ZydisRegister base, ZydisRegister index)
{
static const ZydisRegister modrm16_lookup[8][2] =
{
{ ZYDIS_REGISTER_BX, ZYDIS_REGISTER_SI },
{ ZYDIS_REGISTER_BX, ZYDIS_REGISTER_DI },
{ ZYDIS_REGISTER_BP, ZYDIS_REGISTER_SI },
{ ZYDIS_REGISTER_BP, ZYDIS_REGISTER_DI },
{ ZYDIS_REGISTER_SI, ZYDIS_REGISTER_NONE },
{ ZYDIS_REGISTER_DI, ZYDIS_REGISTER_NONE },
{ ZYDIS_REGISTER_BP, ZYDIS_REGISTER_NONE },
{ ZYDIS_REGISTER_BX, ZYDIS_REGISTER_NONE },
};
for (ZyanI8 i = 0; i < (ZyanI8)ZYAN_ARRAY_LENGTH(modrm16_lookup); ++i)
{
if ((modrm16_lookup[i][0] == base) &&
(modrm16_lookup[i][1] == index))
{
return i;
}
}
return -1;
}
/**
* Encodes `MVEX.sss` field for specified broadcast mode.
*
* @param broadcast Broadcast mode.
*
* @return Corresponding `MVEX.sss` value.
*/
static ZyanU8 ZydisEncodeMvexBroadcastMode(ZydisBroadcastMode broadcast)
{
switch (broadcast)
{
case ZYDIS_BROADCAST_MODE_INVALID:
return 0;
case ZYDIS_BROADCAST_MODE_1_TO_16:
case ZYDIS_BROADCAST_MODE_1_TO_8:
return 1;
case ZYDIS_BROADCAST_MODE_4_TO_16:
case ZYDIS_BROADCAST_MODE_4_TO_8:
return 2;
default:
ZYAN_UNREACHABLE;
}
}
/**
* Encodes `MVEX.sss` field for specified conversion mode.
*
* @param conversion Conversion mode.
*
* @return Corresponding `MVEX.sss` value.
*/
static ZyanU8 ZydisEncodeMvexConversionMode(ZydisConversionMode conversion)
{
switch (conversion)
{
case ZYDIS_CONVERSION_MODE_INVALID:
return 0;
case ZYDIS_CONVERSION_MODE_FLOAT16:
return 3;
case ZYDIS_CONVERSION_MODE_UINT8:
return 4;
case ZYDIS_CONVERSION_MODE_SINT8:
return 5;
case ZYDIS_CONVERSION_MODE_UINT16:
return 6;
case ZYDIS_CONVERSION_MODE_SINT16:
return 7;
default:
ZYAN_UNREACHABLE;
}
}
/**
* Determines scale factor for compressed 8-bit displacement (`EVEX` instructions only).
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
*
* @return log2(scale factor)
*/
static ZyanU8 ZydisGetCompDispScaleEvex(const ZydisEncoderInstructionMatch *match)
{
const ZydisInstructionDefinitionEVEX *evex_def =
(const ZydisInstructionDefinitionEVEX *)match->base_definition;
ZYAN_ASSERT(match->definition->encoding == ZYDIS_INSTRUCTION_ENCODING_EVEX);
ZYAN_ASSERT(evex_def->tuple_type);
ZYAN_ASSERT(evex_def->element_size);
const ZyanU8 vector_length = match->definition->vector_length - ZYDIS_VECTOR_LENGTH_128;
static const ZyanU8 size_indexes[ZYDIS_IELEMENT_SIZE_MAX_VALUE + 1] =
{
0, 0, 0, 1, 2, 4
};
ZYAN_ASSERT(evex_def->element_size < ZYAN_ARRAY_LENGTH(size_indexes));
const ZyanU8 size_index = size_indexes[evex_def->element_size];
switch (evex_def->tuple_type)
{
case ZYDIS_TUPLETYPE_FV:
{
static const ZyanU8 scales[2][3][3] =
{
/*B0*/ { /*16*/ { 4, 5, 6 }, /*32*/ { 4, 5, 6 }, /*64*/ { 4, 5, 6 } },
/*B1*/ { /*16*/ { 1, 1, 1 }, /*32*/ { 2, 2, 2 }, /*64*/ { 3, 3, 3 } }
};
const ZyanU8 broadcast = match->request->evex.broadcast ? 1 : 0;
ZYAN_ASSERT(size_index < 3);
return scales[broadcast][size_index][vector_length];
}
case ZYDIS_TUPLETYPE_HV:
{
static const ZyanU8 scales[2][2][3] =
{
/*B0*/ { /*16*/ { 3, 4, 5 }, /*32*/ { 3, 4, 5 } },
/*B1*/ { /*16*/ { 1, 1, 1 }, /*32*/ { 2, 2, 2 } }
};
const ZyanU8 broadcast = match->request->evex.broadcast ? 1 : 0;
ZYAN_ASSERT(size_index < 3);
return scales[broadcast][size_index][vector_length];
}
case ZYDIS_TUPLETYPE_FVM:
{
static const ZyanU8 scales[3] =
{
4, 5, 6
};
return scales[vector_length];
}
case ZYDIS_TUPLETYPE_GSCAT:
case ZYDIS_TUPLETYPE_T1S:
{
static const ZyanU8 scales[6] =
{
/* */ 0,
/* 8*/ 0,
/* 16*/ 1,
/* 32*/ 2,
/* 64*/ 3,
/*128*/ 4
};
ZYAN_ASSERT(evex_def->element_size < ZYAN_ARRAY_LENGTH(scales));
return scales[evex_def->element_size];
}
case ZYDIS_TUPLETYPE_T1F:
{
static const ZyanU8 scales[3] =
{
/* 16*/ 1,
/* 32*/ 2,
/* 64*/ 3
};
ZYAN_ASSERT(size_index < 3);
return scales[size_index];
}
case ZYDIS_TUPLETYPE_T1_4X:
return 4;
case ZYDIS_TUPLETYPE_T2:
return match->definition->rex_w ? 4 : 3;
case ZYDIS_TUPLETYPE_T4:
return match->definition->rex_w ? 5 : 4;
case ZYDIS_TUPLETYPE_T8:
return 5;
case ZYDIS_TUPLETYPE_HVM:
{
static const ZyanU8 scales[3] =
{
3, 4, 5
};
return scales[vector_length];
}
case ZYDIS_TUPLETYPE_QVM:
{
static const ZyanU8 scales[3] =
{
2, 3, 4
};
return scales[vector_length];
}
case ZYDIS_TUPLETYPE_OVM:
{
static const ZyanU8 scales[3] =
{
1, 2, 3
};
return scales[vector_length];
}
case ZYDIS_TUPLETYPE_M128:
return 4;
case ZYDIS_TUPLETYPE_DUP:
{
static const ZyanU8 scales[3] =
{
3, 5, 6
};
return scales[vector_length];
}
case ZYDIS_TUPLETYPE_QUARTER:
{
static const ZyanU8 scales[2][3] =
{
/*B0*/ { 2, 3, 4 },
/*B1*/ { 1, 1, 1 }
};
const ZyanU8 broadcast = match->request->evex.broadcast ? 1 : 0;
return scales[broadcast][vector_length];
}
default:
ZYAN_UNREACHABLE;
}
}
/**
* Determines scale factor for compressed 8-bit displacement (`MVEX` instructions only).
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
*
* @return log2(scale factor)
*/
static ZyanU8 ZydisGetCompDispScaleMvex(const ZydisEncoderInstructionMatch *match)
{
const ZydisInstructionDefinitionMVEX *mvex_def =
(const ZydisInstructionDefinitionMVEX *)match->base_definition;
ZyanU8 index = mvex_def->has_element_granularity;
ZYAN_ASSERT(!index || !mvex_def->broadcast);
if (!index && mvex_def->broadcast)
{
switch (mvex_def->broadcast)
{
case ZYDIS_MVEX_STATIC_BROADCAST_1_TO_8:
case ZYDIS_MVEX_STATIC_BROADCAST_1_TO_16:
index = 1;
break;
case ZYDIS_MVEX_STATIC_BROADCAST_4_TO_8:
case ZYDIS_MVEX_STATIC_BROADCAST_4_TO_16:
index = 2;
break;
default:
ZYAN_UNREACHABLE;
}
}
const ZyanU8 sss = ZydisEncodeMvexBroadcastMode(match->request->mvex.broadcast) |
ZydisEncodeMvexConversionMode(match->request->mvex.conversion);
switch (mvex_def->functionality)
{
case ZYDIS_MVEX_FUNC_IGNORED:
case ZYDIS_MVEX_FUNC_INVALID:
case ZYDIS_MVEX_FUNC_RC:
case ZYDIS_MVEX_FUNC_SAE:
case ZYDIS_MVEX_FUNC_SWIZZLE_32:
case ZYDIS_MVEX_FUNC_SWIZZLE_64:
return 0;
case ZYDIS_MVEX_FUNC_F_32:
case ZYDIS_MVEX_FUNC_I_32:
case ZYDIS_MVEX_FUNC_F_64:
case ZYDIS_MVEX_FUNC_I_64:
return 6;
case ZYDIS_MVEX_FUNC_SF_32:
case ZYDIS_MVEX_FUNC_SF_32_BCST:
case ZYDIS_MVEX_FUNC_SF_32_BCST_4TO16:
case ZYDIS_MVEX_FUNC_UF_32:
{
static const ZyanU8 lookup[3][8] =
{
{ 6, 2, 4, 5, 4, 4, 5, 5 },
{ 2, 0, 0, 1, 0, 0, 1, 1 },
{ 4, 0, 0, 3, 2, 2, 3, 3 }
};
ZYAN_ASSERT(sss < ZYAN_ARRAY_LENGTH(lookup[index]));
return lookup[index][sss];
}
case ZYDIS_MVEX_FUNC_SI_32:
case ZYDIS_MVEX_FUNC_UI_32:
case ZYDIS_MVEX_FUNC_SI_32_BCST:
case ZYDIS_MVEX_FUNC_SI_32_BCST_4TO16:
{
static const ZyanU8 lookup[3][8] =
{
{ 6, 2, 4, 0, 4, 4, 5, 5 },
{ 2, 0, 0, 0, 0, 0, 1, 1 },
{ 4, 0, 0, 0, 2, 2, 3, 3 }
};
ZYAN_ASSERT(sss < ZYAN_ARRAY_LENGTH(lookup[index]));
return lookup[index][sss];
}
case ZYDIS_MVEX_FUNC_SF_64:
case ZYDIS_MVEX_FUNC_UF_64:
case ZYDIS_MVEX_FUNC_SI_64:
case ZYDIS_MVEX_FUNC_UI_64:
{
static const ZyanU8 lookup[3][3] =
{
{ 6, 3, 5 },
{ 3, 0, 0 },
{ 5, 0, 0 }
};
ZYAN_ASSERT(sss < ZYAN_ARRAY_LENGTH(lookup[index]));
return lookup[index][sss];
}
case ZYDIS_MVEX_FUNC_DF_32:
case ZYDIS_MVEX_FUNC_DI_32:
{
static const ZyanU8 lookup[2][8] =
{
{ 6, 0, 0, 5, 4, 4, 5, 5 },
{ 2, 0, 0, 1, 0, 0, 1, 1 }
};
ZYAN_ASSERT(index < 2);
ZYAN_ASSERT(sss < ZYAN_ARRAY_LENGTH(lookup[index]));
return lookup[index][sss];
}
case ZYDIS_MVEX_FUNC_DF_64:
case ZYDIS_MVEX_FUNC_DI_64:
ZYAN_ASSERT(index < 2);
return index == 0 ? 6 : 3;
default:
ZYAN_UNREACHABLE;
}
}
/**
* Determines scale factor for compressed 8-bit displacement.
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
*
* @return log2(scale factor)
*/
static ZyanU8 ZydisGetCompDispScale(const ZydisEncoderInstructionMatch *match)
{
switch (match->definition->encoding)
{
case ZYDIS_INSTRUCTION_ENCODING_LEGACY:
case ZYDIS_INSTRUCTION_ENCODING_3DNOW:
case ZYDIS_INSTRUCTION_ENCODING_XOP:
case ZYDIS_INSTRUCTION_ENCODING_VEX:
return 0;
case ZYDIS_INSTRUCTION_ENCODING_EVEX:
return ZydisGetCompDispScaleEvex(match);
case ZYDIS_INSTRUCTION_ENCODING_MVEX:
return ZydisGetCompDispScaleMvex(match);
default:
ZYAN_UNREACHABLE;
}
}
/**
* Checks if requested operand matches register operand from instruction definition.
*
* @param match A pointer to `ZydisEncoderInstructionMatch` struct.
* @param user_op Operand definition from `ZydisEncoderRequest` structure.
* @param def_op Decoder's operand definition from current instruction definition.
*
* @return True if operands match, false otherwise.
*/
static ZyanBool ZydisIsRegisterOperandCompatible(ZydisEncoderInstructionMatch *match,
const ZydisEncoderOperand *user_op, const ZydisOperandDefinition *def_op)
{
const ZydisRegisterClass reg_class = ZydisRegisterGetClass(user_op->reg.value);
const ZydisRegisterWidth reg_width = ZydisRegisterClassGetWidth(match->request->machine_mode,
reg_class);
if (reg_width == 0)
{
return ZYAN_FALSE;
}
ZyanBool is4_expected_value = ZYAN_FALSE;
switch (def_op->type)
{
case ZYDIS_SEMANTIC_OPTYPE_IMPLICIT_REG:
switch (def_op->op.reg.type)
{
case ZYDIS_IMPLREG_TYPE_STATIC:
if (def_op->op.reg.reg.reg != user_op->reg.value)
{
return ZYAN_FALSE;
}
break;
case ZYDIS_IMPLREG_TYPE_GPR_OSZ:
if ((reg_class != ZYDIS_REGCLASS_GPR8) &&
(reg_class != ZYDIS_REGCLASS_GPR16) &&
(reg_class != ZYDIS_REGCLASS_GPR32) &&
(reg_class != ZYDIS_REGCLASS_GPR64))
{
return ZYAN_FALSE;
}
if (def_op->op.reg.reg.id != ZydisRegisterGetId(user_op->reg.value))
{
return ZYAN_FALSE;
}
if (!ZydisCheckOsz(match, reg_width))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_IMPLREG_TYPE_GPR_ASZ:
if ((reg_class != ZYDIS_REGCLASS_GPR8) &&
(reg_class != ZYDIS_REGCLASS_GPR16) &&
(reg_class != ZYDIS_REGCLASS_GPR32) &&
(reg_class != ZYDIS_REGCLASS_GPR64))
{
return ZYAN_FALSE;
}
if (def_op->op.reg.reg.id != ZydisRegisterGetId(user_op->reg.value))
{
return ZYAN_FALSE;
}
if (!ZydisCheckAsz(match, reg_width))
{
return ZYAN_FALSE;
}
break;
default:
ZYAN_UNREACHABLE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_GPR8:
if (reg_class != ZYDIS_REGCLASS_GPR8)
{
return ZYAN_FALSE;
}
if (!ZydisIsRegisterAllowed(match, user_op->reg.value, reg_class))
{
return ZYAN_FALSE;
}
if (!ZydisValidateRexType(match, user_op->reg.value, ZYAN_FALSE))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_GPR16:
if (reg_class != ZYDIS_REGCLASS_GPR16)
{
return ZYAN_FALSE;
}
if (!ZydisIsRegisterAllowed(match, user_op->reg.value, reg_class))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_GPR32:
if (reg_class != ZYDIS_REGCLASS_GPR32)
{
return ZYAN_FALSE;
}
if (!ZydisIsRegisterAllowed(match, user_op->reg.value, reg_class))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_GPR64:
if (reg_class != ZYDIS_REGCLASS_GPR64)
{
return ZYAN_FALSE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_GPR16_32_64:
if ((reg_class != ZYDIS_REGCLASS_GPR16) &&
(reg_class != ZYDIS_REGCLASS_GPR32) &&
(reg_class != ZYDIS_REGCLASS_GPR64))
{
return ZYAN_FALSE;
}
if (!ZydisCheckOsz(match, reg_width))
{
return ZYAN_FALSE;
}
if (!ZydisIsRegisterAllowed(match, user_op->reg.value, reg_class))
{
return ZYAN_FALSE;
}
if (!ZydisValidateRexType(match, user_op->reg.value, ZYAN_FALSE))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_GPR32_32_64:
if ((reg_class != ZYDIS_REGCLASS_GPR32) &&
(reg_class != ZYDIS_REGCLASS_GPR64))
{
return ZYAN_FALSE;
}
if (match->eosz == 0)
{
if (reg_class == ZYDIS_REGCLASS_GPR64)
{
match->eosz = 64;
}
else
{
match->eosz64_forbidden = ZYAN_TRUE;
}
}
else if (match->eosz != (ZyanU8)reg_width)
{
return ZYAN_FALSE;
}
if (!ZydisIsRegisterAllowed(match, user_op->reg.value, reg_class))
{
return ZYAN_FALSE;
}
if (!ZydisValidateRexType(match, user_op->reg.value, ZYAN_FALSE))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_GPR16_32_32:
if ((reg_class != ZYDIS_REGCLASS_GPR16) &&
(reg_class != ZYDIS_REGCLASS_GPR32))
{
return ZYAN_FALSE;
}
if (!ZydisCheckOsz(match, reg_width))
{
if (match->eosz != 64 || reg_class != ZYDIS_REGCLASS_GPR32)
{
return ZYAN_FALSE;
}
}
if (!ZydisIsRegisterAllowed(match, user_op->reg.value, reg_class))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_GPR_ASZ:
if ((reg_class != ZYDIS_REGCLASS_GPR16) &&
(reg_class != ZYDIS_REGCLASS_GPR32) &&
(reg_class != ZYDIS_REGCLASS_GPR64))
{
return ZYAN_FALSE;
}
if (!ZydisCheckAsz(match, reg_width))
{
return ZYAN_FALSE;
}
if (!ZydisIsRegisterAllowed(match, user_op->reg.value, reg_class))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_FPR:
if (reg_class != ZYDIS_REGCLASS_X87)
{
return ZYAN_FALSE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_MMX:
if (reg_class != ZYDIS_REGCLASS_MMX)
{
return ZYAN_FALSE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_XMM:
if (reg_class != ZYDIS_REGCLASS_XMM)
{
return ZYAN_FALSE;
}
if (!ZydisIsRegisterAllowed(match, user_op->reg.value, reg_class))
{
return ZYAN_FALSE;
}
is4_expected_value = def_op->op.encoding == ZYDIS_OPERAND_ENCODING_IS4;
break;
case ZYDIS_SEMANTIC_OPTYPE_YMM:
if (reg_class != ZYDIS_REGCLASS_YMM)
{
return ZYAN_FALSE;
}
if (!ZydisIsRegisterAllowed(match, user_op->reg.value, reg_class))
{
return ZYAN_FALSE;
}
is4_expected_value = def_op->op.encoding == ZYDIS_OPERAND_ENCODING_IS4;
break;
case ZYDIS_SEMANTIC_OPTYPE_ZMM:
if (reg_class != ZYDIS_REGCLASS_ZMM)
{
return ZYAN_FALSE;
}
if (!ZydisIsRegisterAllowed(match, user_op->reg.value, reg_class))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_TMM:
if (reg_class != ZYDIS_REGCLASS_TMM)
{
return ZYAN_FALSE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_BND:
if (reg_class != ZYDIS_REGCLASS_BOUND)
{
return ZYAN_FALSE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_SREG:
if (reg_class != ZYDIS_REGCLASS_SEGMENT)
{
return ZYAN_FALSE;
}
if ((def_op->actions & ZYDIS_OPERAND_ACTION_MASK_WRITE) &&
(user_op->reg.value == ZYDIS_REGISTER_CS))
{
return ZYAN_FALSE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_CR:
{
if (reg_class != ZYDIS_REGCLASS_CONTROL)
{
return ZYAN_FALSE;
}
static const ZyanU8 cr_lookup[16] =
{
1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0
};
const ZyanI8 reg_id = ZydisRegisterGetId(user_op->reg.value);
if ((match->request->machine_mode != ZYDIS_MACHINE_MODE_LONG_64) &&
(reg_id == 8))
{
return ZYAN_FALSE;
}
if (!cr_lookup[reg_id])
{
return ZYAN_FALSE;
}
break;
}
case ZYDIS_SEMANTIC_OPTYPE_DR:
if (reg_class != ZYDIS_REGCLASS_DEBUG)
{
return ZYAN_FALSE;
}
if (user_op->reg.value >= ZYDIS_REGISTER_DR8)
{
return ZYAN_FALSE;
}
break;
case ZYDIS_SEMANTIC_OPTYPE_MASK:
if (reg_class != ZYDIS_REGCLASS_MASK)
{
return ZYAN_FALSE;
}
// MVEX does not require similar policy check
if ((match->definition->encoding == ZYDIS_INSTRUCTION_ENCODING_EVEX) &&
(def_op->op.encoding == ZYDIS_OPERAND_ENCODING_MASK))
{
const ZydisInstructionDefinitionEVEX *evex_def =
(const ZydisInstructionDefinitionEVEX *)match->base_definition;
ZYAN_ASSERT((evex_def->mask_policy != ZYDIS_MASK_POLICY_INVALID) &&
(evex_def->mask_policy != ZYDIS_MASK_POLICY_FORBIDDEN));
if ((evex_def->mask_policy == ZYDIS_MASK_POLICY_REQUIRED) &&
(user_op->reg.value == ZYDIS_REGISTER_K0))
{
return ZYAN_FALSE;
}
if ((evex_def->mask_policy == ZYDIS_MASK_POLICY_ALLOWED) &&
(match->request->evex.zeroing_mask) &&
(user_op->reg.value == ZYDIS_REGISTER_K0))