/***************************************************************************************************
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 en
coding)
{
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 & (~(0 xFFFFFFFFFFFFFFFFULL << (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))
{
return ZYAN_FALSE;
}
}
break ;
default :
ZYAN_UNREACHABLE;
}
if (user_op->reg.is4 != is4_expected_value)
{
return ZYAN_FALSE;
}
return ZYAN_TRUE;
}
/**
* Checks if requested operand matches memory 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 ZydisIsMemoryOperandCompatible(ZydisEncoderInstructionMatch *match,
const ZydisEncoderOperand *user_op, const ZydisOperandDefinition *def_op)
{
switch (def_op->type)
{
case ZYDIS_SEMANTIC_OPTYPE_MEM:
case ZYDIS_SEMANTIC_OPTYPE_AGEN:
case ZYDIS_SEMANTIC_OPTYPE_MIB:
case ZYDIS_SEMANTIC_OPTYPE_MEM_VSIBX:
case ZYDIS_SEMANTIC_OPTYPE_MEM_VSIBY:
case ZYDIS_SEMANTIC_OPTYPE_MEM_VSIBZ:
{
if ((def_op->type == ZYDIS_SEMANTIC_OPTYPE_MIB) &&
(user_op->mem.scale != 0 ))
{
return ZYAN_FALSE;
}
const ZyanI64 displacement = user_op->mem.displacement;
ZyanU8 disp_size = 0 ;
if (displacement)
{
disp_size = ZydisGetSignedImmSize(displacement);
if (disp_size > 32 )
{
return ZYAN_FALSE;
}
match->cd8_scale = ZydisGetCompDispScale(match);
if (match->cd8_scale)
{
const ZyanI64 mask = (1 << match->cd8_scale) - 1 ;
if (!(displacement & mask))
{
if (ZydisGetSignedImmSize(displacement >> match->cd8_scale) == 8 )
{
disp_size = 8 ;
}
}
else if (disp_size == 8 )
{
disp_size = 16 ;
}
}
}
if (def_op->type != ZYDIS_SEMANTIC_OPTYPE_AGEN)
{
if (match->eosz != 0 )
{
const ZyanU8 eosz_index = match->eosz >> 5 ;
if (def_op->size[eosz_index] != user_op->mem.size)
{
return ZYAN_FALSE;
}
}
else if ((match->definition->vector_length != ZYDIS_VECTOR_LENGTH_INVALID) ||
(match->definition->encoding == ZYDIS_INSTRUCTION_ENCODING_MVEX))
{
ZyanU8 eosz_index = ZydisGetMachineModeWidth(match->request->machine_mode) >> 5 ;
if (match->eosz64_forbidden && (eosz_index == 2 ))
{
eosz_index = 1 ;
}
ZyanU16 allowed_mem_size = def_op->size[eosz_index];
if ((!allowed_mem_size) &&
(match->definition->encoding != ZYDIS_INSTRUCTION_ENCODING_VEX))
{
ZYAN_ASSERT((match->definition->encoding == ZYDIS_INSTRUCTION_ENCODING_EVEX) ||
(match->definition->encoding == ZYDIS_INSTRUCTION_ENCODING_MVEX));
switch (match->definition->vector_length)
{
case ZYDIS_VECTOR_LENGTH_128:
allowed_mem_size = 16 ;
break ;
case ZYDIS_VECTOR_LENGTH_256:
allowed_mem_size = 32 ;
break ;
case ZYDIS_VECTOR_LENGTH_INVALID:
ZYAN_ASSERT(match->definition->encoding == ZYDIS_INSTRUCTION_ENCODING_MVEX);
ZYAN_FALLTHROUGH;
case ZYDIS_VECTOR_LENGTH_512:
allowed_mem_size = 64 ;
break ;
default :
ZYAN_UNREACHABLE;
}
if (match->definition->encoding == ZYDIS_INSTRUCTION_ENCODING_EVEX)
{
const ZydisInstructionDefinitionEVEX *evex_def =
(const ZydisInstructionDefinitionEVEX *)match->base_definition;
static const ZyanU8 element_sizes[ZYDIS_IELEMENT_SIZE_MAX_VALUE + 1 ] =
{
0 , 1 , 2 , 4 , 8 , 16
};
ZYAN_ASSERT(evex_def->element_size < ZYAN_ARRAY_LENGTH(element_sizes));
const ZyanU8 element_size = element_sizes[evex_def->element_size];
if (match->request->evex.broadcast || evex_def->broadcast)
{
allowed_mem_size = element_size;
}
else
{
switch (evex_def->tuple_type)
{
case ZYDIS_TUPLETYPE_FV:
break ;
case ZYDIS_TUPLETYPE_HV:
allowed_mem_size /= 2 ;
break ;
case ZYDIS_TUPLETYPE_QUARTER:
allowed_mem_size /= 4 ;
break ;
default :
ZYAN_UNREACHABLE;
}
}
}
else
{
const ZydisInstructionDefinitionMVEX *mvex_def =
(const ZydisInstructionDefinitionMVEX *)match->base_definition;
ZyanU16 element_size;
switch (match->request->mvex.conversion)
{
case ZYDIS_CONVERSION_MODE_INVALID:
switch (mvex_def->functionality)
{
case ZYDIS_MVEX_FUNC_SF_32:
case ZYDIS_MVEX_FUNC_SF_32_BCST_4TO16:
case ZYDIS_MVEX_FUNC_UF_32:
case ZYDIS_MVEX_FUNC_DF_32:
case ZYDIS_MVEX_FUNC_SI_32:
case ZYDIS_MVEX_FUNC_SI_32_BCST_4TO16:
case ZYDIS_MVEX_FUNC_UI_32:
case ZYDIS_MVEX_FUNC_DI_32:
allowed_mem_size = 64 ;
element_size = 4 ;
break ;
case ZYDIS_MVEX_FUNC_SF_64:
case ZYDIS_MVEX_FUNC_UF_64:
case ZYDIS_MVEX_FUNC_DF_64:
case ZYDIS_MVEX_FUNC_SI_64:
case ZYDIS_MVEX_FUNC_UI_64:
case ZYDIS_MVEX_FUNC_DI_64:
allowed_mem_size = 64 ;
element_size = 8 ;
break ;
case ZYDIS_MVEX_FUNC_SF_32_BCST:
case ZYDIS_MVEX_FUNC_SI_32_BCST:
allowed_mem_size = 32 ;
element_size = 4 ;
break ;
default :
ZYAN_UNREACHABLE;
}
break ;
case ZYDIS_CONVERSION_MODE_FLOAT16:
case ZYDIS_CONVERSION_MODE_SINT16:
case ZYDIS_CONVERSION_MODE_UINT16:
allowed_mem_size = 32 ;
element_size = 2 ;
break ;
case ZYDIS_CONVERSION_MODE_SINT8:
case ZYDIS_CONVERSION_MODE_UINT8:
allowed_mem_size = 16 ;
element_size = 1 ;
break ;
default :
ZYAN_UNREACHABLE;
}
ZYAN_ASSERT(!mvex_def->broadcast || !match->request->mvex.broadcast);
switch (mvex_def->broadcast)
{
case ZYDIS_MVEX_STATIC_BROADCAST_NONE:
break ;
case ZYDIS_MVEX_STATIC_BROADCAST_1_TO_8:
case ZYDIS_MVEX_STATIC_BROADCAST_1_TO_16:
allowed_mem_size = element_size;
break ;
case ZYDIS_MVEX_STATIC_BROADCAST_4_TO_8:
case ZYDIS_MVEX_STATIC_BROADCAST_4_TO_16:
allowed_mem_size = element_size * 4 ;
break ;
default :
ZYAN_UNREACHABLE;
}
switch (match->request->mvex.broadcast)
{
case ZYDIS_BROADCAST_MODE_INVALID:
break ;
case ZYDIS_BROADCAST_MODE_1_TO_8:
case ZYDIS_BROADCAST_MODE_1_TO_16:
allowed_mem_size = element_size;
break ;
case ZYDIS_BROADCAST_MODE_4_TO_8:
case ZYDIS_BROADCAST_MODE_4_TO_16:
allowed_mem_size = element_size * 4 ;
break ;
default :
ZYAN_UNREACHABLE;
}
}
}
if (user_op->mem.size != allowed_mem_size)
{
return ZYAN_FALSE;
}
}
else if (match->definition->rex_w)
{
match->eosz = 64 ;
}
else if (match->definition->vector_length == ZYDIS_VECTOR_LENGTH_INVALID)
{
match->eosz = ZydisGetOperandSizeFromElementSize(match, def_op->size,
user_op->mem.size, ZYAN_TRUE);
if (match->eosz == 0 )
{
return ZYAN_FALSE;
}
}
else
{
ZYAN_UNREACHABLE;
}
}
else
{
if (match->easz != 0 )
{
if (match->easz != user_op->mem.size)
{
return ZYAN_FALSE;
}
}
else
{
switch (user_op->mem.size)
{
case 2 :
case 4 :
case 8 :
match->easz = (ZyanU8)user_op->mem.size << 3 ;
break ;
default :
return ZYAN_FALSE;
}
}
}
ZydisRegisterClass vsib_index_class = ZYDIS_REGCLASS_INVALID;
ZyanBool is_vsib = ZYAN_TRUE;
switch (def_op->type)
{
case ZYDIS_SEMANTIC_OPTYPE_MEM_VSIBX:
vsib_index_class = ZYDIS_REGCLASS_XMM;
break ;
case ZYDIS_SEMANTIC_OPTYPE_MEM_VSIBY:
vsib_index_class = ZYDIS_REGCLASS_YMM;
break ;
case ZYDIS_SEMANTIC_OPTYPE_MEM_VSIBZ:
vsib_index_class = ZYDIS_REGCLASS_ZMM;
break ;
default :
is_vsib = ZYAN_FALSE;
break ;
}
const ZyanBool is_rip_relative = (user_op->mem.base == ZYDIS_REGISTER_RIP) ||
(user_op->mem.base == ZYDIS_REGISTER_EIP);
if (is_rip_relative)
{
const ZyanBool no_rip_rel = ZYDIS_OPDEF_GET_MEM_HIGH_BIT(match->base_definition->op_rm);
if (no_rip_rel || ((match->definition->modrm & 7 ) == 4 ))
{
return ZYAN_FALSE;
}
}
const ZydisRegisterClass reg_base_class = ZydisRegisterGetClass(user_op->mem.base);
if ((reg_base_class == ZYDIS_REGCLASS_INVALID) &&
(user_op->mem.base != ZYDIS_REGISTER_NONE))
{
return ZYAN_FALSE;
}
const ZydisRegisterClass reg_index_class = ZydisRegisterGetClass(user_op->mem.index);
if ((reg_index_class == ZYDIS_REGCLASS_INVALID) &&
(user_op->mem.index != ZYDIS_REGISTER_NONE))
{
return ZYAN_FALSE;
}
if (is_vsib)
{
const ZyanU8 mode_width = ZydisGetMachineModeWidth(match->request->machine_mode);
const ZyanI8 reg_index_id = ZydisRegisterGetId(user_op->mem.index);
if (((match->request->machine_mode != ZYDIS_MACHINE_MODE_LONG_64) ||
(reg_base_class != ZYDIS_REGCLASS_GPR64)) &&
(reg_base_class != ZYDIS_REGCLASS_GPR32) &&
(reg_base_class != ZYDIS_REGCLASS_INVALID))
{
return ZYAN_FALSE;
}
if ((reg_base_class == ZYDIS_REGCLASS_GPR32) &&
(mode_width != 64 ) &&
(ZydisRegisterGetId(user_op->mem.base) > 7 ))
{
return ZYAN_FALSE;
}
ZyanU8 max_reg_id = 7 ;
if (mode_width == 64 )
{
max_reg_id = match->definition->encoding != ZYDIS_INSTRUCTION_ENCODING_VEX ?
31 : 15 ;
}
if ((reg_index_class != vsib_index_class) ||
(reg_index_id > max_reg_id))
{
return ZYAN_FALSE;
}
}
else
{
if (!ZydisIsValidAddressingClass(match, reg_base_class, user_op->mem.base))
{
if (!is_rip_relative || match->request->machine_mode != ZYDIS_MACHINE_MODE_LONG_64)
{
return ZYAN_FALSE;
}
}
if (!ZydisIsValidAddressingClass(match, reg_index_class, user_op->mem.index))
{
return ZYAN_FALSE;
}
if (reg_base_class != ZYDIS_REGCLASS_INVALID &&
reg_index_class != ZYDIS_REGCLASS_INVALID &&
reg_base_class != reg_index_class)
{
return ZYAN_FALSE;
}
if (user_op->mem.index == ZYDIS_REGISTER_ESP ||
user_op->mem.index == ZYDIS_REGISTER_RSP)
{
return ZYAN_FALSE;
}
}
if (reg_index_class != ZYDIS_REGCLASS_INVALID &&
user_op->mem.scale == 0 &&
def_op->type != ZYDIS_SEMANTIC_OPTYPE_MIB)
{
return ZYAN_FALSE;
}
if (reg_index_class == ZYDIS_REGCLASS_INVALID &&
user_op->mem.scale != 0 )
{
return ZYAN_FALSE;
}
ZyanU8 candidate_easz = 0 ;
ZyanBool disp_only = ZYAN_FALSE;
if (reg_base_class != ZYDIS_REGCLASS_INVALID)
{
if (is_rip_relative)
{
candidate_easz = user_op->mem.base == ZYDIS_REGISTER_RIP ? 64 : 32 ;
}
else
{
candidate_easz = (ZyanU8)ZydisRegisterClassGetWidth(match->request->machine_mode,
reg_base_class);
}
}
else if (reg_index_class != ZYDIS_REGCLASS_INVALID)
{
if (is_vsib)
{
candidate_easz = ZydisGetMachineModeWidth(match->request->machine_mode);
}
else
{
candidate_easz = (ZyanU8)ZydisRegisterClassGetWidth(match->request->machine_mode,
reg_index_class);
}
}
else if (disp_size != 8 || !match->cd8_scale)
{
const ZyanU8 addr_size = ZydisGetMaxAddressSize(match->request);
if (disp_size > addr_size)
{
return ZYAN_FALSE;
}
ZyanU8 min_disp_size = match->easz ? match->easz : 16 ;
if (((min_disp_size == 16 ) && !(match->definition->address_sizes & ZYDIS_WIDTH_16)) ||
(min_disp_size == 64 ) ||
(match->request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64))
{
min_disp_size = 32 ;
}
if (disp_size < min_disp_size)
{
disp_size = min_disp_size;
}
const ZyanI64 disp = user_op->mem.displacement;
if (match->request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64)
{
candidate_easz = addr_size;
if (addr_size == 32 && disp >= 0 && match->easz != 32 )
{
candidate_easz = 64 ;
}
}
else
{
const ZyanU64 uimm = disp & (~(0 xFFFFFFFFFFFFFFFFULL << (addr_size - 1 ) << 1 ));
if (disp_size < addr_size && ZydisGetUnsignedImmSize(uimm) > disp_size)
{
disp_size = addr_size;
}
candidate_easz = disp_size;
}
disp_only = ZYAN_TRUE;
}
if (match->request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64)
{
if (is_rip_relative && reg_index_class != ZYDIS_REGCLASS_INVALID)
{
return ZYAN_FALSE;
}
}
else
{
if (candidate_easz == 16 && !disp_only)
{
if (disp_size > 16 )
{
return ZYAN_FALSE;
}
const ZyanI8 rm16 = ZydisGetRm16(user_op->mem.base, user_op->mem.index);
if (rm16 == -1 )
{
return ZYAN_FALSE;
}
const ZyanU8 allowed_scale = rm16 < 4 ? 1 : 0 ;
if (user_op->mem.scale != allowed_scale)
{
return ZYAN_FALSE;
}
}
}
if (match->easz != 0 )
{
if (match->easz != candidate_easz)
{
return ZYAN_FALSE;
}
}
else
{
match->easz = candidate_easz;
}
if ((match->base_definition->address_size_map == ZYDIS_ADSIZE_MAP_IGNORED) &&
(match->easz != ZydisGetMachineModeWidth(match->request->machine_mode)))
{
return ZYAN_FALSE;
}
match->disp_size = disp_size;
break ;
}
case ZYDIS_SEMANTIC_OPTYPE_MOFFS:
{
if (user_op->mem.base != ZYDIS_REGISTER_NONE ||
user_op->mem.index != ZYDIS_REGISTER_NONE ||
user_op->mem.scale != 0 )
{
return ZYAN_FALSE;
}
const ZyanU8 min_disp_size = ZydisGetSignedImmSize(user_op->mem.displacement);
if (min_disp_size > ZydisGetMaxAddressSize(match->request))
{
return ZYAN_FALSE;
}
if (match->eosz != 0 )
{
const ZyanU8 eosz_index = match->eosz >> 5 ;
if (def_op->size[eosz_index] != user_op->mem.size)
{
return ZYAN_FALSE;
}
}
else
{
match->eosz = ZydisGetOperandSizeFromElementSize(match, def_op->size,
user_op->mem.size, ZYAN_TRUE);
if (match->eosz == 0 )
{
return ZYAN_FALSE;
}
}
match->disp_size = ZydisGetEffectiveImmSize(match, user_op->mem.displacement, def_op);
if (match->disp_size == 0 )
{
return ZYAN_FALSE;
}
// This is not a standard rejection. It's a special case for `mov` instructions (`moffs`
// variants only). In 64-bit mode it's possible to get a shorter encoding for addresses
// that can fit into 32-bit displacements.
if (match->disp_size == 64 && min_disp_size < match->disp_size)
{
return ZYAN_FALSE;
}
break ;
}
default :
ZYAN_UNREACHABLE;
}
return ZYAN_TRUE;
}
/**
* Checks if requested operand matches pointer operand from instruction definition .
*
* @ param match A pointer to ` ZydisEncoderInstructionMatch ` struct .
* @ param user_op Operand definition from ` ZydisEncoderRequest ` structure .
*
* @ return True if operands match , false otherwise .
*/
static ZyanBool ZydisIsPointerOperandCompatible(ZydisEncoderInstructionMatch *match,
const ZydisEncoderOperand *user_op)
{
ZYAN_ASSERT(match->eosz == 0 );
ZYAN_ASSERT(match->request->machine_mode != ZYDIS_MACHINE_MODE_LONG_64);
ZYAN_ASSERT((match->request->branch_type == ZYDIS_BRANCH_TYPE_NONE) ||
(match->request->branch_type == ZYDIS_BRANCH_TYPE_FAR));
const ZyanU8 min_disp_size = ZydisGetUnsignedImmSize(user_op->ptr.offset);
const ZyanU8 desired_disp_size = (match->request->branch_width == ZYDIS_BRANCH_WIDTH_NONE)
? ZydisGetMachineModeWidth(match->request->machine_mode)
: (4 << match->request->branch_width);
if (min_disp_size > desired_disp_size)
{
return ZYAN_FALSE;
}
match->eosz = match->disp_size = desired_disp_size;
match->imm_size = 16 ;
return ZYAN_TRUE;
}
/**
* Checks if requested operand matches immediate 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 ZydisIsImmediateOperandCompabile(ZydisEncoderInstructionMatch *match,
const ZydisEncoderOperand *user_op, const ZydisOperandDefinition *def_op)
{
switch (def_op->type)
{
case ZYDIS_SEMANTIC_OPTYPE_IMPLICIT_IMM1:
if (user_op->imm.u != 1 )
{
return ZYAN_FALSE;
}
break ;
case ZYDIS_SEMANTIC_OPTYPE_IMM:
case ZYDIS_SEMANTIC_OPTYPE_REL:
{
const ZyanU8 imm_size = ZydisGetEffectiveImmSize(match, user_op->imm.s, def_op);
if (def_op->op.encoding != ZYDIS_OPERAND_ENCODING_IS4)
{
if (imm_size == 0 )
{
return ZYAN_FALSE;
}
if (match->imm_size)
{
ZYAN_ASSERT(match->disp_size == 0 );
match->disp_size = match->imm_size;
}
}
else
{
ZYAN_ASSERT(match->imm_size == 0 );
if (imm_size != 8 )
{
return ZYAN_FALSE;
}
}
match->imm_size = imm_size;
match->has_rel_operand = (def_op->type == ZYDIS_SEMANTIC_OPTYPE_REL);
break ;
}
default :
ZYAN_UNREACHABLE;
}
return ZYAN_TRUE;
}
/**
* Checks if requested boardcast mode is compatible with instruction definition .
*
* @ param evex_def Definition for ` EVEX ` - encoded instruction .
* @ param vector_length Vector length .
* @ param broadcast Requested broadcast mode .
*
* @ return True if broadcast mode is compatible , false otherwise .
*/
static ZyanBool ZydisIsBroadcastModeCompatible(const ZydisInstructionDefinitionEVEX *evex_def,
ZydisVectorLength vector_length, ZydisBroadcastMode broadcast)
{
if (broadcast == ZYDIS_BROADCAST_MODE_INVALID)
{
return ZYAN_TRUE;
}
ZyanU8 vector_size = 0 ;
ZYAN_ASSERT(vector_length != ZYDIS_VECTOR_LENGTH_INVALID);
switch (vector_length)
{
case ZYDIS_VECTOR_LENGTH_128:
vector_size = 16 ;
break ;
case ZYDIS_VECTOR_LENGTH_256:
vector_size = 32 ;
break ;
case ZYDIS_VECTOR_LENGTH_512:
vector_size = 64 ;
break ;
default :
ZYAN_UNREACHABLE;
}
switch (evex_def->tuple_type)
{
case ZYDIS_TUPLETYPE_FV:
break ;
case ZYDIS_TUPLETYPE_HV:
vector_size /= 2 ;
break ;
case ZYDIS_TUPLETYPE_QUARTER:
vector_size /= 4 ;
break ;
default :
ZYAN_UNREACHABLE;
}
ZyanU8 element_size;
switch (evex_def->element_size)
{
case ZYDIS_IELEMENT_SIZE_16:
element_size = 2 ;
break ;
case ZYDIS_IELEMENT_SIZE_32:
element_size = 4 ;
break ;
case ZYDIS_IELEMENT_SIZE_64:
element_size = 8 ;
break ;
default :
ZYAN_UNREACHABLE;
}
ZydisBroadcastMode allowed_mode;
const ZyanU8 element_count = vector_size / element_size;
switch (element_count)
{
case 2 :
allowed_mode = ZYDIS_BROADCAST_MODE_1_TO_2;
break ;
case 4 :
allowed_mode = ZYDIS_BROADCAST_MODE_1_TO_4;
break ;
case 8 :
allowed_mode = ZYDIS_BROADCAST_MODE_1_TO_8;
break ;
case 16 :
allowed_mode = ZYDIS_BROADCAST_MODE_1_TO_16;
break ;
case 32 :
allowed_mode = ZYDIS_BROADCAST_MODE_1_TO_32;
break ;
default :
ZYAN_UNREACHABLE;
}
if (broadcast != allowed_mode)
{
return ZYAN_FALSE;
}
return ZYAN_TRUE;
}
/**
* Checks if requested ` EVEX ` - specific features are compatible with instruction definition .
*
* @ param match A pointer to ` ZydisEncoderInstructionMatch ` struct .
* @ param request A pointer to ` ZydisEncoderRequest ` struct .
*
* @ return True if features are compatible , false otherwise .
*/
static ZyanBool ZydisAreEvexFeaturesCompatible(const ZydisEncoderInstructionMatch *match,
const ZydisEncoderRequest *request)
{
if (match->definition->encoding != ZYDIS_INSTRUCTION_ENCODING_EVEX)
{
return ZYAN_TRUE;
}
const ZydisInstructionDefinitionEVEX *evex_def =
(const ZydisInstructionDefinitionEVEX *)match->base_definition;
if ((!evex_def->accepts_zero_mask) &&
(evex_def->mask_override != ZYDIS_MASK_OVERRIDE_ZEROING) &&
(request->evex.zeroing_mask))
{
return ZYAN_FALSE;
}
switch (evex_def->functionality)
{
case ZYDIS_EVEX_FUNC_INVALID:
if ((request->evex.sae) ||
(request->evex.broadcast != ZYDIS_BROADCAST_MODE_INVALID) ||
(request->evex.rounding != ZYDIS_ROUNDING_MODE_INVALID))
{
return ZYAN_FALSE;
}
break ;
case ZYDIS_EVEX_FUNC_BC:
if ((request->evex.sae) ||
(request->evex.rounding != ZYDIS_ROUNDING_MODE_INVALID))
{
return ZYAN_FALSE;
}
if (!ZydisIsBroadcastModeCompatible(evex_def, match->definition->vector_length,
request->evex.broadcast))
{
return ZYAN_FALSE;
}
break ;
case ZYDIS_EVEX_FUNC_RC:
if (request->evex.broadcast != ZYDIS_BROADCAST_MODE_INVALID)
{
return ZYAN_FALSE;
}
if (request->evex.rounding == ZYDIS_ROUNDING_MODE_INVALID)
{
if (request->evex.sae)
{
return ZYAN_FALSE;
}
}
else
{
if (!request->evex.sae)
{
return ZYAN_FALSE;
}
}
break ;
case ZYDIS_EVEX_FUNC_SAE:
if ((request->evex.broadcast != ZYDIS_BROADCAST_MODE_INVALID) ||
(request->evex.rounding != ZYDIS_ROUNDING_MODE_INVALID))
{
return ZYAN_FALSE;
}
break ;
default :
ZYAN_UNREACHABLE;
}
return ZYAN_TRUE;
}
/**
* Checks if requested ` MVEX ` - specific features are compatible with instruction definition .
*
* @ param match A pointer to ` ZydisEncoderInstructionMatch ` struct .
* @ param request A pointer to ` ZydisEncoderRequest ` struct .
*
* @ return True if features are compatible , false otherwise .
*/
static ZyanBool ZydisAreMvexFeaturesCompatible(const ZydisEncoderInstructionMatch *match,
const ZydisEncoderRequest *request)
{
if (match->definition->encoding != ZYDIS_INSTRUCTION_ENCODING_MVEX)
{
return ZYAN_TRUE;
}
if (((match->definition->modrm >> 6 ) == 3 ) &&
(request->mvex.eviction_hint))
{
return ZYAN_FALSE;
}
const ZydisInstructionDefinitionMVEX *mvex_def =
(const ZydisInstructionDefinitionMVEX *)match->base_definition;
switch (mvex_def->functionality)
{
case ZYDIS_MVEX_FUNC_IGNORED:
case ZYDIS_MVEX_FUNC_INVALID:
case ZYDIS_MVEX_FUNC_F_32:
case ZYDIS_MVEX_FUNC_I_32:
case ZYDIS_MVEX_FUNC_F_64:
case ZYDIS_MVEX_FUNC_I_64:
case ZYDIS_MVEX_FUNC_UF_64:
case ZYDIS_MVEX_FUNC_UI_64:
case ZYDIS_MVEX_FUNC_DF_64:
case ZYDIS_MVEX_FUNC_DI_64:
if ((request->mvex.broadcast != ZYDIS_BROADCAST_MODE_INVALID) ||
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_INVALID) ||
(request->mvex.rounding != ZYDIS_ROUNDING_MODE_INVALID) ||
(request->mvex.swizzle != ZYDIS_SWIZZLE_MODE_INVALID) ||
(request->mvex.sae))
{
return ZYAN_FALSE;
}
break ;
case ZYDIS_MVEX_FUNC_RC:
if ((request->mvex.broadcast != ZYDIS_BROADCAST_MODE_INVALID) ||
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_INVALID) ||
(request->mvex.swizzle != ZYDIS_SWIZZLE_MODE_INVALID) ||
(request->mvex.eviction_hint))
{
return ZYAN_FALSE;
}
break ;
case ZYDIS_MVEX_FUNC_SAE:
if ((request->mvex.broadcast != ZYDIS_BROADCAST_MODE_INVALID) ||
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_INVALID) ||
(request->mvex.rounding != ZYDIS_ROUNDING_MODE_INVALID) ||
(request->mvex.swizzle != ZYDIS_SWIZZLE_MODE_INVALID) ||
(request->mvex.eviction_hint))
{
return ZYAN_FALSE;
}
break ;
case ZYDIS_MVEX_FUNC_SWIZZLE_32:
case ZYDIS_MVEX_FUNC_SWIZZLE_64:
if ((request->mvex.broadcast != ZYDIS_BROADCAST_MODE_INVALID) ||
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_INVALID) ||
(request->mvex.rounding != ZYDIS_ROUNDING_MODE_INVALID) ||
(request->mvex.sae))
{
return ZYAN_FALSE;
}
break ;
case ZYDIS_MVEX_FUNC_SF_32:
if ((request->mvex.rounding != ZYDIS_ROUNDING_MODE_INVALID) ||
(request->mvex.swizzle != ZYDIS_SWIZZLE_MODE_INVALID) ||
(request->mvex.sae))
{
return ZYAN_FALSE;
}
if ((request->mvex.broadcast != ZYDIS_BROADCAST_MODE_INVALID) &&
(request->mvex.broadcast != ZYDIS_BROADCAST_MODE_1_TO_16) &&
(request->mvex.broadcast != ZYDIS_BROADCAST_MODE_4_TO_16))
{
return ZYAN_FALSE;
}
if ((request->mvex.conversion != ZYDIS_CONVERSION_MODE_INVALID) &&
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_FLOAT16) &&
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_UINT8) &&
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_UINT16) &&
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_SINT16))
{
return ZYAN_FALSE;
}
if ((request->mvex.broadcast != ZYDIS_BROADCAST_MODE_INVALID) &&
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_INVALID))
{
return ZYAN_FALSE;
}
break ;
case ZYDIS_MVEX_FUNC_SI_32:
if ((request->mvex.rounding != ZYDIS_ROUNDING_MODE_INVALID) ||
(request->mvex.swizzle != ZYDIS_SWIZZLE_MODE_INVALID) ||
(request->mvex.sae))
{
return ZYAN_FALSE;
}
if ((request->mvex.broadcast != ZYDIS_BROADCAST_MODE_INVALID) &&
(request->mvex.broadcast != ZYDIS_BROADCAST_MODE_1_TO_16) &&
(request->mvex.broadcast != ZYDIS_BROADCAST_MODE_4_TO_16))
{
return ZYAN_FALSE;
}
if ((request->mvex.conversion != ZYDIS_CONVERSION_MODE_INVALID) &&
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_UINT8) &&
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_SINT8) &&
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_UINT16) &&
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_SINT16))
{
return ZYAN_FALSE;
}
if ((request->mvex.broadcast != ZYDIS_BROADCAST_MODE_INVALID) &&
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_INVALID))
{
return ZYAN_FALSE;
}
break ;
case ZYDIS_MVEX_FUNC_SF_32_BCST:
case ZYDIS_MVEX_FUNC_SI_32_BCST:
if ((request->mvex.conversion != ZYDIS_CONVERSION_MODE_INVALID) ||
(request->mvex.rounding != ZYDIS_ROUNDING_MODE_INVALID) ||
(request->mvex.swizzle != ZYDIS_SWIZZLE_MODE_INVALID) ||
(request->mvex.sae))
{
return ZYAN_FALSE;
}
if ((request->mvex.broadcast != ZYDIS_BROADCAST_MODE_INVALID) &&
(request->mvex.broadcast != ZYDIS_BROADCAST_MODE_1_TO_16) &&
(request->mvex.broadcast != ZYDIS_BROADCAST_MODE_4_TO_16))
{
return ZYAN_FALSE;
}
break ;
case ZYDIS_MVEX_FUNC_SF_32_BCST_4TO16:
case ZYDIS_MVEX_FUNC_SI_32_BCST_4TO16:
if ((request->mvex.conversion != ZYDIS_CONVERSION_MODE_INVALID) ||
(request->mvex.rounding != ZYDIS_ROUNDING_MODE_INVALID) ||
(request->mvex.swizzle != ZYDIS_SWIZZLE_MODE_INVALID) ||
(request->mvex.sae))
{
return ZYAN_FALSE;
}
if ((request->mvex.broadcast != ZYDIS_BROADCAST_MODE_INVALID) &&
(request->mvex.broadcast != ZYDIS_BROADCAST_MODE_4_TO_16))
{
return ZYAN_FALSE;
}
break ;
case ZYDIS_MVEX_FUNC_SF_64:
case ZYDIS_MVEX_FUNC_SI_64:
if ((request->mvex.conversion != ZYDIS_CONVERSION_MODE_INVALID) ||
(request->mvex.rounding != ZYDIS_ROUNDING_MODE_INVALID) ||
(request->mvex.swizzle != ZYDIS_SWIZZLE_MODE_INVALID) ||
(request->mvex.sae))
{
return ZYAN_FALSE;
}
if ((request->mvex.broadcast != ZYDIS_BROADCAST_MODE_INVALID) &&
(request->mvex.broadcast != ZYDIS_BROADCAST_MODE_1_TO_8) &&
(request->mvex.broadcast != ZYDIS_BROADCAST_MODE_4_TO_8))
{
return ZYAN_FALSE;
}
break ;
case ZYDIS_MVEX_FUNC_UF_32:
case ZYDIS_MVEX_FUNC_DF_32:
if ((request->mvex.broadcast != ZYDIS_BROADCAST_MODE_INVALID) ||
(request->mvex.rounding != ZYDIS_ROUNDING_MODE_INVALID) ||
(request->mvex.swizzle != ZYDIS_SWIZZLE_MODE_INVALID) ||
(request->mvex.sae))
{
return ZYAN_FALSE;
}
break ;
case ZYDIS_MVEX_FUNC_UI_32:
case ZYDIS_MVEX_FUNC_DI_32:
if ((request->mvex.broadcast != ZYDIS_BROADCAST_MODE_INVALID) ||
(request->mvex.rounding != ZYDIS_ROUNDING_MODE_INVALID) ||
(request->mvex.swizzle != ZYDIS_SWIZZLE_MODE_INVALID) ||
(request->mvex.sae))
{
return ZYAN_FALSE;
}
if ((request->mvex.conversion != ZYDIS_CONVERSION_MODE_INVALID) &&
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_UINT8) &&
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_SINT8) &&
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_UINT16) &&
(request->mvex.conversion != ZYDIS_CONVERSION_MODE_SINT16))
{
return ZYAN_FALSE;
}
break ;
default :
ZYAN_UNREACHABLE;
}
return ZYAN_TRUE;
}
/**
* Checks if operands specified in encoder request satisfy additional constraints mandated by
* matched instruction definition .
*
* @ param match A pointer to ` ZydisEncoderInstructionMatch ` struct .
*
* @ return True if operands passed the checks , false otherwise .
*/
static ZyanBool ZydisCheckConstraints(const ZydisEncoderInstructionMatch *match)
{
const ZydisEncoderOperand *operands = match->request->operands;
ZyanBool is_gather = ZYAN_FALSE;
switch (match->definition->encoding)
{
case ZYDIS_INSTRUCTION_ENCODING_VEX:
{
const ZydisInstructionDefinitionVEX *vex_def =
(const ZydisInstructionDefinitionVEX *)match->base_definition;
if (vex_def->is_gather)
{
ZYAN_ASSERT(match->request->operand_count == 3 );
ZYAN_ASSERT(operands[0 ].type == ZYDIS_OPERAND_TYPE_REGISTER);
ZYAN_ASSERT(operands[1 ].type == ZYDIS_OPERAND_TYPE_MEMORY);
ZYAN_ASSERT(operands[2 ].type == ZYDIS_OPERAND_TYPE_REGISTER);
const ZyanI8 dest = ZydisRegisterGetId(operands[0 ].reg.value);
const ZyanI8 index = ZydisRegisterGetId(operands[1 ].mem.index);
const ZyanI8 mask = ZydisRegisterGetId(operands[2 ].reg.value);
// If any pair of the index, mask, or destination registers are the same, the
// instruction results a UD fault.
if ((dest == index) || (dest == mask) || (index == mask))
{
return ZYAN_FALSE;
}
}
if (vex_def->no_source_source_match)
{
ZYAN_ASSERT(match->request->operand_count == 3 );
ZYAN_ASSERT(operands[0 ].type == ZYDIS_OPERAND_TYPE_REGISTER);
ZYAN_ASSERT(operands[1 ].type == ZYDIS_OPERAND_TYPE_REGISTER);
ZYAN_ASSERT(operands[2 ].type == ZYDIS_OPERAND_TYPE_REGISTER);
const ZydisRegister dest = operands[0 ].reg.value;
const ZydisRegister source1 = operands[1 ].reg.value;
const ZydisRegister source2 = operands[2 ].reg.value;
// AMX-E4: #UD if srcdest == src1 OR src1 == src2 OR srcdest == src2.
if ((dest == source1) || (source1 == source2) || (dest == source2))
{
return ZYAN_FALSE;
}
}
return ZYAN_TRUE;
}
case ZYDIS_INSTRUCTION_ENCODING_EVEX:
{
const ZydisInstructionDefinitionEVEX *evex_def =
(const ZydisInstructionDefinitionEVEX *)match->base_definition;
is_gather = evex_def->is_gather;
if (evex_def->no_source_dest_match)
{
ZYAN_ASSERT(operands[0 ].type == ZYDIS_OPERAND_TYPE_REGISTER);
ZYAN_ASSERT(operands[2 ].type == ZYDIS_OPERAND_TYPE_REGISTER);
ZYAN_ASSERT((operands[3 ].type == ZYDIS_OPERAND_TYPE_REGISTER) ||
(operands[3 ].type == ZYDIS_OPERAND_TYPE_MEMORY));
const ZydisRegister dest = operands[0 ].reg.value;
const ZydisRegister source1 = operands[2 ].reg.value;
const ZydisRegister source2 = (operands[3 ].type == ZYDIS_OPERAND_TYPE_REGISTER)
? operands[3 ].reg.value
: ZYDIS_REGISTER_NONE;
if ((dest == source1) || (dest == source2))
{
return ZYAN_FALSE;
}
}
break ;
}
case ZYDIS_INSTRUCTION_ENCODING_MVEX:
{
const ZydisInstructionDefinitionMVEX *mvex_def =
(const ZydisInstructionDefinitionMVEX *)match->base_definition;
is_gather = mvex_def->is_gather;
break ;
}
default :
return ZYAN_TRUE;
}
if ((is_gather) && (operands[0 ].type == ZYDIS_OPERAND_TYPE_REGISTER))
{
ZYAN_ASSERT(match->request->operand_count == 3 );
ZYAN_ASSERT(operands[0 ].type == ZYDIS_OPERAND_TYPE_REGISTER);
ZYAN_ASSERT(operands[2 ].type == ZYDIS_OPERAND_TYPE_MEMORY);
const ZyanI8 dest = ZydisRegisterGetId(operands[0 ].reg.value);
const ZyanI8 index = ZydisRegisterGetId(operands[2 ].mem.index);
// EVEX: The instruction will #UD fault if the destination vector zmm1 is the same as
// index vector VINDEX.
// MVEX: The KNC GATHER instructions forbid using the same vector register for destination
// and for the index. (https://github.com/intelxed/xed/issues/281#issuecomment-970074554)
if (dest == index)
{
return ZYAN_FALSE;
}
}
return ZYAN_TRUE;
}
/**
* Checks if operands and encoding - specific features from ` ZydisEncoderRequest ` match
* encoder ' s instruction definition .
*
* @ param match A pointer to ` ZydisEncoderInstructionMatch ` struct .
* @ param request A pointer to ` ZydisEncoderRequest ` struct .
*
* @ return True if definition is compatible , false otherwise .
*/
static ZyanBool ZydisIsDefinitionCompatible(ZydisEncoderInstructionMatch *match,
const ZydisEncoderRequest *request)
{
ZYAN_ASSERT(request->operand_count == match->base_definition->operand_count_visible);
match->operands = ZydisGetOperandDefinitions(match->base_definition);
if (!ZydisAreEvexFeaturesCompatible(match, request))
{
return ZYAN_FALSE;
}
if (!ZydisAreMvexFeaturesCompatible(match, request))
{
return ZYAN_FALSE;
}
for (ZyanU8 i = 0 ; i < request->operand_count; ++i)
{
const ZydisEncoderOperand *user_op = &request->operands[i];
const ZydisOperandDefinition *def_op = &match->operands[i];
ZYAN_ASSERT(def_op->visibility != ZYDIS_OPERAND_VISIBILITY_HIDDEN);
ZyanBool is_compatible = ZYAN_FALSE;
switch (user_op->type)
{
case ZYDIS_OPERAND_TYPE_REGISTER:
is_compatible = ZydisIsRegisterOperandCompatible(match, user_op, def_op);
break ;
case ZYDIS_OPERAND_TYPE_MEMORY:
is_compatible = ZydisIsMemoryOperandCompatible(match, user_op, def_op);
break ;
case ZYDIS_OPERAND_TYPE_POINTER:
is_compatible = ZydisIsPointerOperandCompatible(match, user_op);
break ;
case ZYDIS_OPERAND_TYPE_IMMEDIATE:
is_compatible = ZydisIsImmediateOperandCompabile(match, user_op, def_op);
break ;
default :
ZYAN_UNREACHABLE;
}
if (!is_compatible)
{
return ZYAN_FALSE;
}
}
ZyanU8 eosz = 0 ;
if (match->base_definition->branch_type != ZYDIS_BRANCH_TYPE_NONE)
{
switch (request->branch_width)
{
case ZYDIS_BRANCH_WIDTH_NONE:
break ;
case ZYDIS_BRANCH_WIDTH_8:
if ((!match->has_rel_operand) ||
(match->base_definition->branch_type != ZYDIS_BRANCH_TYPE_SHORT))
{
return ZYAN_FALSE;
}
break ;
case ZYDIS_BRANCH_WIDTH_16:
eosz = 16 ;
break ;
case ZYDIS_BRANCH_WIDTH_32:
eosz = ((match->has_rel_operand) &&
(match->request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) &&
(match->base_definition->operand_size_map == ZYDIS_OPSIZE_MAP_FORCE64))
? 64
: 32 ;
break ;
case ZYDIS_BRANCH_WIDTH_64:
if (match->has_rel_operand)
{
return ZYAN_FALSE;
}
eosz = 64 ;
break ;
default :
ZYAN_UNREACHABLE;
}
}
if (eosz)
{
if (match->eosz != 0 )
{
if (match->eosz != eosz)
{
return ZYAN_FALSE;
}
}
else
{
match->eosz = eosz;
}
}
if (!ZydisCheckConstraints(match))
{
return ZYAN_FALSE;
}
return ZYAN_TRUE;
}
/**
* Checks if requested set of prefixes is compatible with instruction definition .
*
* @ param match A pointer to ` ZydisEncoderInstructionMatch ` struct .
*
* @ return A zyan status code .
*/
static ZyanBool ZydisArePrefixesCompatible(const ZydisEncoderInstructionMatch *match)
{
// Early-exit optimization for when no prefixes are requested at all.
if (!(match->attributes & ZYDIS_ENCODABLE_PREFIXES))
{
return ZYAN_TRUE;
}
if ((!match->base_definition->accepts_segment) &&
(match->attributes & ZYDIS_ATTRIB_HAS_SEGMENT))
{
return ZYAN_FALSE;
}
if (match->definition->encoding != ZYDIS_INSTRUCTION_ENCODING_LEGACY)
{
return !(match->attributes & ZYDIS_ENCODABLE_PREFIXES_NO_SEGMENTS);
}
const ZydisInstructionDefinitionLEGACY *legacy_def =
(const ZydisInstructionDefinitionLEGACY *)match->base_definition;
if ((!legacy_def->accepts_LOCK) &&
(match->attributes & ZYDIS_ATTRIB_HAS_LOCK))
{
return ZYAN_FALSE;
}
if ((!legacy_def->accepts_REP) &&
(match->attributes & ZYDIS_ATTRIB_HAS_REP))
{
return ZYAN_FALSE;
}
if ((!legacy_def->accepts_REPEREPZ) &&
(match->attributes & ZYDIS_ATTRIB_HAS_REPE))
{
return ZYAN_FALSE;
}
if ((!legacy_def->accepts_REPNEREPNZ) &&
(match->attributes & ZYDIS_ATTRIB_HAS_REPNE))
{
return ZYAN_FALSE;
}
if ((!legacy_def->accepts_BOUND) &&
(match->attributes & ZYDIS_ATTRIB_HAS_BND))
{
return ZYAN_FALSE;
}
if ((!legacy_def->accepts_XACQUIRE) &&
(match->attributes & ZYDIS_ATTRIB_HAS_XACQUIRE))
{
return ZYAN_FALSE;
}
if ((!legacy_def->accepts_XRELEASE) &&
(match->attributes & ZYDIS_ATTRIB_HAS_XRELEASE))
{
return ZYAN_FALSE;
}
if ((!legacy_def->accepts_branch_hints) &&
(match->attributes & (ZYDIS_ATTRIB_HAS_BRANCH_NOT_TAKEN |
ZYDIS_ATTRIB_HAS_BRANCH_TAKEN)))
{
return ZYAN_FALSE;
}
if ((!legacy_def->accepts_NOTRACK) &&
(match->attributes & ZYDIS_ATTRIB_HAS_NOTRACK))
{
return ZYAN_FALSE;
}
if ((!legacy_def->accepts_hle_without_lock) &&
(match->attributes & (ZYDIS_ATTRIB_HAS_XACQUIRE |
ZYDIS_ATTRIB_HAS_XRELEASE)) &&
!(match->attributes & ZYDIS_ATTRIB_HAS_LOCK))
{
return ZYAN_FALSE;
}
return ZYAN_TRUE;
}
/**
* Returns operand mask containing information about operand count and types in a compressed form .
*
* @ param request A pointer to ` ZydisEncoderRequest ` struct .
*
* @ return Operand mask .
*/
static ZyanU16 ZydisGetOperandMask(const ZydisEncoderRequest *request)
{
ZyanU16 operand_mask = request->operand_count;
ZyanU8 bit_offset = ZYAN_BITS_TO_REPRESENT(ZYDIS_ENCODER_MAX_OPERANDS);
for (ZyanU8 i = 0 ; i < request->operand_count; ++i)
{
operand_mask |= (request->operands[i].type - ZYDIS_OPERAND_TYPE_REGISTER) << bit_offset;
bit_offset += ZYAN_BITS_TO_REPRESENT(
ZYDIS_OPERAND_TYPE_MAX_VALUE - ZYDIS_OPERAND_TYPE_REGISTER);
}
return operand_mask;
}
/**
* Handles optimization opportunities indicated by ` swappable ` field in instruction definition
* structure . See ` ZydisEncodableInstruction ` for more information .
*
* @ param match A pointer to ` ZydisEncoderInstructionMatch ` struct .
*
* @ return True if definition has been swapped , false otherwise .
*/
static ZyanBool ZydisHandleSwappableDefinition(ZydisEncoderInstructionMatch *match)
{
if (!match->definition->swappable)
{
return ZYAN_FALSE;
}
// Special case for ISA-wide unique conflict between two `mov` variants
// mov gpr16_32_64(encoding=opcode), imm(encoding=simm16_32_64,scale_factor=osz)
// mov gpr16_32_64(encoding=modrm_rm), imm(encoding=simm16_32_32,scale_factor=osz)
if (match->request->mnemonic == ZYDIS_MNEMONIC_MOV)
{
const ZyanU8 imm_size = ZydisGetSignedImmSize(match->request->operands[1 ].imm.s);
if ((match->request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) &&
(match->eosz == 64 ) &&
(imm_size < 64 ))
{
return ZYAN_TRUE;
}
}
ZYAN_ASSERT((match->request->operand_count == 2 ) || (match->request->operand_count == 3 ));
const ZyanU8 src_index = (match->request->operand_count == 3 ) ? 2 : 1 ;
const ZyanI8 dest_id = ZydisRegisterGetId(match->request->operands[0 ].reg.value);
const ZyanI8 src_id = ZydisRegisterGetId(match->request->operands[src_index].reg.value);
if ((dest_id <= 7 ) && (src_id > 7 ))
{
++match->definition;
ZydisGetInstructionDefinition(match->definition->encoding,
match->definition->instruction_reference, &match->base_definition);
match->operands = ZydisGetOperandDefinitions(match->base_definition);
return ZYAN_TRUE;
}
return ZYAN_FALSE;
}
/**
* This function attempts to find a matching instruction definition for provided encoder request .
*
* @ param request A pointer to ` ZydisEncoderRequest ` struct .
* @ param match A pointer to ` ZydisEncoderInstructionMatch ` struct .
*
* @ return A zyan status code .
*/
static ZyanStatus ZydisFindMatchingDefinition(const ZydisEncoderRequest *request,
ZydisEncoderInstructionMatch *match)
{
ZYAN_MEMSET(match, 0 , sizeof (ZydisEncoderInstructionMatch));
match->request = request;
match->attributes = request->prefixes;
const ZydisEncodableInstruction *definition = ZYAN_NULL;
const ZyanU8 definition_count = ZydisGetEncodableInstructions(request->mnemonic, &definition);
ZYAN_ASSERT(definition && definition_count);
const ZydisWidthFlag mode_width = ZydisGetMachineModeWidth(request->machine_mode) >> 4 ;
const ZyanBool is_compat =
(request->machine_mode == ZYDIS_MACHINE_MODE_LONG_COMPAT_16) ||
(request->machine_mode == ZYDIS_MACHINE_MODE_LONG_COMPAT_32);
const ZyanU8 default_asz = ZydisGetAszFromHint(request->address_size_hint);
const ZyanU8 default_osz = ZydisGetOszFromHint(request->operand_size_hint);
const ZyanU16 operand_mask = ZydisGetOperandMask(request);
for (ZyanU8 i = 0 ; i < definition_count; ++i, ++definition)
{
if (definition->operand_mask != operand_mask)
{
continue ;
}
const ZydisInstructionDefinition *base_definition = ZYAN_NULL;
ZydisGetInstructionDefinition(definition->encoding, definition->instruction_reference,
&base_definition);
if (!(definition->modes & mode_width))
{
continue ;
}
if ((request->allowed_encodings != ZYDIS_ENCODABLE_ENCODING_DEFAULT) &&
!(ZydisGetEncodableEncoding(definition->encoding) & request->allowed_encodings))
{
continue ;
}
if (request->machine_mode == ZYDIS_MACHINE_MODE_REAL_16)
{
if (base_definition->requires_protected_mode)
{
continue ;
}
switch (definition->encoding)
{
case ZYDIS_INSTRUCTION_ENCODING_XOP:
case ZYDIS_INSTRUCTION_ENCODING_VEX:
case ZYDIS_INSTRUCTION_ENCODING_EVEX:
case ZYDIS_INSTRUCTION_ENCODING_MVEX:
continue ;
default :
break ;
}
}
else if ((request->machine_mode != ZYDIS_MACHINE_MODE_LONG_64) &&
(definition->encoding == ZYDIS_INSTRUCTION_ENCODING_MVEX))
{
continue ;
}
if (is_compat && base_definition->no_compat_mode)
{
continue ;
}
if ((request->branch_type != ZYDIS_BRANCH_TYPE_NONE) &&
(request->branch_type != base_definition->branch_type))
{
continue ;
}
if ((base_definition->branch_type == ZYDIS_BRANCH_TYPE_NONE) &&
(request->branch_width != ZYDIS_BRANCH_WIDTH_NONE))
{
continue ;
}
match->definition = definition;
match->base_definition = base_definition;
match->operands = ZYAN_NULL;
match->easz = definition->accepts_hint == ZYDIS_SIZE_HINT_ASZ ? default_asz : 0 ;
match->eosz = definition->accepts_hint == ZYDIS_SIZE_HINT_OSZ ? default_osz : 0 ;
match->disp_size = match->imm_size = match->cd8_scale = 0 ;
match->rex_type = ZYDIS_REX_TYPE_UNKNOWN;
match->eosz64_forbidden = ZYAN_FALSE;
match->has_rel_operand = ZYAN_FALSE;
if ((base_definition->operand_size_map != ZYDIS_OPSIZE_MAP_BYTEOP) &&
(match->eosz == 8 ))
{
continue ;
}
if (!ZydisArePrefixesCompatible(match))
{
continue ;
}
if (!ZydisIsDefinitionCompatible(match, request))
{
continue ;
}
if (ZydisHandleSwappableDefinition(match))
{
if (definition == match->definition)
{
continue ;
}
++i;
definition = match->definition;
base_definition = match->base_definition;
}
if (match->easz == 0 )
{
if (definition->address_sizes & mode_width)
{
match->easz = (ZyanU8)(mode_width << 4 );
}
else if (mode_width == ZYDIS_WIDTH_16)
{
match->easz = 32 ;
}
else if (mode_width == ZYDIS_WIDTH_32)
{
match->easz = 16 ;
}
else
{
match->easz = 32 ;
}
ZYAN_ASSERT(definition->address_sizes & (match->easz >> 4 ));
}
else if (!(definition->address_sizes & (match->easz >> 4 )))
{
continue ;
}
if (mode_width == ZYDIS_WIDTH_64)
{
if (base_definition->operand_size_map == ZYDIS_OPSIZE_MAP_DEFAULT64)
{
if (match->eosz == 0 )
{
ZYAN_ASSERT(definition->operand_sizes & (ZYDIS_WIDTH_16 | ZYDIS_WIDTH_64));
if (definition->operand_sizes & ZYDIS_WIDTH_64)
{
match->eosz = 64 ;
}
else
{
match->eosz = 16 ;
}
}
else if (match->eosz == 32 )
{
continue ;
}
}
else if (base_definition->operand_size_map == ZYDIS_OPSIZE_MAP_FORCE64)
{
if (match->eosz == 0 )
{
match->eosz = 64 ;
}
else if (match->eosz != 64 )
{
continue ;
}
}
}
if (match->eosz == 0 )
{
const ZydisWidthFlag default_width = (mode_width == ZYDIS_WIDTH_64)
? ZYDIS_WIDTH_32
: mode_width;
if (definition->operand_sizes & default_width)
{
match->eosz = (ZyanU8)(default_width << 4 );
}
else if (definition->operand_sizes & ZYDIS_WIDTH_16)
{
match->eosz = 16 ;
}
else if (definition->operand_sizes & ZYDIS_WIDTH_32)
{
match->eosz = 32 ;
}
else
{
match->eosz = 64 ;
}
}
else if (match->eosz64_forbidden && match->eosz == 64 )
{
continue ;
}
else if (!(definition->operand_sizes & (match->eosz >> 4 )))
{
continue ;
}
return ZYAN_STATUS_SUCCESS;
}
return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION;
}
/**
* Emits unsigned integer value .
*
* @ param data Value to emit .
* @ param size Value size in bytes .
* @ param buffer A pointer to ` ZydisEncoderBuffer ` struct .
*
* @ return A zyan status code .
*/
static ZyanStatus ZydisEmitUInt(ZyanU64 data, ZyanU8 size, ZydisEncoderBuffer *buffer)
{
ZYAN_ASSERT(size == 1 || size == 2 || size == 4 || size == 8 );
const ZyanUSize new_offset = buffer->offset + size;
if (new_offset > buffer->size)
{
return ZYAN_STATUS_INSUFFICIENT_BUFFER_SIZE;
}
// TODO: fix for big-endian systems
// The size variable is not passed on purpose to allow the compiler
// to generate better code with a known size at compile time.
if (size == 1 )
{
ZYAN_MEMCPY(buffer->buffer + buffer->offset, &data, 1 );
}
else if (size == 2 )
{
ZYAN_MEMCPY(buffer->buffer + buffer->offset, &data, 2 );
}
else if (size == 4 )
{
ZYAN_MEMCPY(buffer->buffer + buffer->offset, &data, 4 );
}
else if (size == 8 )
{
ZYAN_MEMCPY(buffer->buffer + buffer->offset, &data, 8 );
}
else
{
ZYAN_UNREACHABLE;
}
buffer->offset = new_offset;
return ZYAN_STATUS_SUCCESS;
}
/**
* Emits a single byte .
*
* @ param byte Value to emit .
* @ param buffer A pointer to ` ZydisEncoderBuffer ` struct .
*
* @ return A zyan status code .
*/
static ZyanStatus ZydisEmitByte(ZyanU8 byte, ZydisEncoderBuffer *buffer)
{
return ZydisEmitUInt(byte, 1 , buffer);
}
/**
* Emits legact prefixes .
*
* @ param instruction A pointer to ` ZydisEncoderInstruction ` struct .
* @ param buffer A pointer to ` ZydisEncoderBuffer ` struct .
*
* @ return A zyan status code .
*/
static ZyanStatus ZydisEmitLegacyPrefixes(const ZydisEncoderInstruction *instruction,
ZydisEncoderBuffer *buffer)
{
ZyanBool compressed_prefixes = ZYAN_FALSE;
switch (instruction->encoding)
{
case ZYDIS_INSTRUCTION_ENCODING_XOP:
case ZYDIS_INSTRUCTION_ENCODING_VEX:
case ZYDIS_INSTRUCTION_ENCODING_EVEX:
case ZYDIS_INSTRUCTION_ENCODING_MVEX:
compressed_prefixes = ZYAN_TRUE;
break ;
default :
break ;
}
// Group 1
if (instruction->attributes & ZYDIS_ATTRIB_HAS_LOCK)
{
ZYAN_CHECK(ZydisEmitByte(0 xF0, buffer));
}
if (!compressed_prefixes)
{
if (instruction->attributes & (ZYDIS_ATTRIB_HAS_REPNE |
ZYDIS_ATTRIB_HAS_BND |
ZYDIS_ATTRIB_HAS_XACQUIRE))
{
ZYAN_CHECK(ZydisEmitByte(0 xF2, buffer));
}
if (instruction->attributes & (ZYDIS_ATTRIB_HAS_REP |
ZYDIS_ATTRIB_HAS_REPE |
ZYDIS_ATTRIB_HAS_XRELEASE))
{
ZYAN_CHECK(ZydisEmitByte(0 xF3, buffer));
}
}
// Group 2
if (instruction->attributes & (ZYDIS_ATTRIB_HAS_SEGMENT_CS |
ZYDIS_ATTRIB_HAS_BRANCH_NOT_TAKEN))
{
ZYAN_CHECK(ZydisEmitByte(0 x2E, buffer));
}
if (instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT_SS)
{
ZYAN_CHECK(ZydisEmitByte(0 x36, buffer));
}
if (instruction->attributes & (ZYDIS_ATTRIB_HAS_SEGMENT_DS |
ZYDIS_ATTRIB_HAS_BRANCH_TAKEN))
{
ZYAN_CHECK(ZydisEmitByte(0 x3E, buffer));
}
if (instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT_ES)
{
ZYAN_CHECK(ZydisEmitByte(0 x26, buffer));
}
if (instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT_FS)
{
ZYAN_CHECK(ZydisEmitByte(0 x64, buffer));
}
if (instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT_GS)
{
ZYAN_CHECK(ZydisEmitByte(0 x65, buffer));
}
if (instruction->attributes & ZYDIS_ATTRIB_HAS_NOTRACK)
{
ZYAN_CHECK(ZydisEmitByte(0 x3E, buffer));
}
// Group 3
if (!compressed_prefixes)
{
if (instruction->attributes & ZYDIS_ATTRIB_HAS_OPERANDSIZE)
{
ZYAN_CHECK(ZydisEmitByte(0 x66, buffer));
}
}
// Group 4
if (instruction->attributes & ZYDIS_ATTRIB_HAS_ADDRESSSIZE)
{
ZYAN_CHECK(ZydisEmitByte(0 x67, buffer));
}
return ZYAN_STATUS_SUCCESS;
}
/**
* Encodes low nibble of ` REX ` prefix .
*
* @ param instruction A pointer to ` ZydisEncoderInstruction ` struct .
* @ param high_r A pointer to ` ZyanBool ` variable that will be set to true when the
* highest ` ModR / M . reg ` bit cannot be encoded using ` REX ` prefix .
*
* @ return A zyan status code .
*/
static ZyanU8 ZydisEncodeRexLowNibble(const ZydisEncoderInstruction *instruction, ZyanBool *high_r)
{
if (high_r)
{
*high_r = ZYAN_FALSE;
}
ZyanU8 rex = 0 ;
if ((instruction->attributes & ZYDIS_ATTRIB_HAS_MODRM) &&
(instruction->attributes & ZYDIS_ATTRIB_HAS_SIB))
{
if (instruction->base & 0 x08)
{
rex |= 1 ;
}
if (instruction->index & 0 x08)
{
rex |= 2 ;
}
if (instruction->reg & 0 x08)
{
rex |= 4 ;
}
if (high_r && (instruction->reg & 0 x10))
{
*high_r = ZYAN_TRUE;
}
}
else if (instruction->attributes & ZYDIS_ATTRIB_HAS_MODRM)
{
if (instruction->rm & 0 x08)
{
rex |= 1 ;
}
if (instruction->rm & 0 x10)
{
rex |= 2 ;
}
if (instruction->reg & 0 x08)
{
rex |= 4 ;
}
if (high_r && (instruction->reg & 0 x10))
{
*high_r = ZYAN_TRUE;
}
}
else
{
if (instruction->rm & 0 x08)
{
rex |= 1 ;
}
}
if (instruction->rex_w)
{
rex |= 8 ;
}
return rex;
}
/**
* Emits ` REX ` prefix .
*
* @ param instruction A pointer to ` ZydisEncoderInstruction ` struct .
* @ param buffer A pointer to ` ZydisEncoderBuffer ` struct .
*
* @ return A zyan status code .
*/
static ZyanStatus ZydisEmitRex(const ZydisEncoderInstruction *instruction,
ZydisEncoderBuffer *buffer)
{
const ZyanU8 rex = ZydisEncodeRexLowNibble(instruction, ZYAN_NULL);
if (rex || (instruction->attributes & ZYDIS_ATTRIB_HAS_REX))
{
ZYAN_CHECK(ZydisEmitByte(0 x40 | rex, buffer));
}
return ZYAN_STATUS_SUCCESS;
}
/**
* Encodes common parts of ` VEX ` prefix .
*
* @ param instruction A pointer to ` ZydisEncoderInstruction ` struct .
* @ param mmmmm A pointer to ` ZyanU8 ` variable that will receive ` VEX . mmmmm `
* @ param pp A pointer to ` ZyanU8 ` variable that will receive ` VEX . pp `
* @ param vvvv A pointer to ` ZyanU8 ` variable that will receive ` VEX . vvvv `
* @ param rex A pointer to ` ZyanU8 ` variable that will receive ' REX `
* @ param high_r A pointer to ` ZyanBool ` variable that will be set to true when the
* highest ` ModR / M . reg ` bit cannot be encoded using ` REX ` prefix .
*/
static void ZydisEncodeVexCommons(ZydisEncoderInstruction *instruction, ZyanU8 *mmmmm, ZyanU8 *pp,
ZyanU8 *vvvv, ZyanU8 *rex, ZyanBool *high_r)
{
switch (instruction->opcode_map)
{
case ZYDIS_OPCODE_MAP_DEFAULT:
case ZYDIS_OPCODE_MAP_0F:
case ZYDIS_OPCODE_MAP_0F38:
case ZYDIS_OPCODE_MAP_0F3A:
case ZYDIS_OPCODE_MAP_MAP5:
case ZYDIS_OPCODE_MAP_MAP6:
*mmmmm = (ZyanU8)instruction->opcode_map;
break ;
case ZYDIS_OPCODE_MAP_XOP8:
case ZYDIS_OPCODE_MAP_XOP9:
case ZYDIS_OPCODE_MAP_XOPA:
*mmmmm = 8 + ((ZyanU8)instruction->opcode_map - ZYDIS_OPCODE_MAP_XOP8);
break ;
default :
ZYAN_UNREACHABLE;
}
instruction->opcode_map = ZYDIS_OPCODE_MAP_DEFAULT;
*pp = 0 ;
if (instruction->attributes & ZYDIS_ATTRIB_HAS_OPERANDSIZE)
{
*pp = 1 ;
}
else if (instruction->attributes & ZYDIS_ATTRIB_HAS_REP)
{
*pp = 2 ;
}
else if (instruction->attributes & ZYDIS_ATTRIB_HAS_REPNE)
{
*pp = 3 ;
}
*vvvv = ~instruction->vvvv;
*rex = ZydisEncodeRexLowNibble(instruction, high_r);
}
/**
* Emits ` XOP ` prefix .
*
* @ param instruction A pointer to ` ZydisEncoderInstruction ` struct .
* @ param buffer A pointer to ` ZydisEncoderBuffer ` struct .
*
* @ return A zyan status code .
*/
static ZyanStatus ZydisEmitXop(ZydisEncoderInstruction *instruction, ZydisEncoderBuffer *buffer)
{
ZyanU8 mmmmm, pp, vvvv, rex;
ZydisEncodeVexCommons(instruction, &mmmmm, &pp, &vvvv, &rex, ZYAN_NULL);
ZYAN_ASSERT(instruction->vector_length <= 1 );
const ZyanU8 b1 = (((~rex) & 0 x07) << 5 ) | mmmmm;
const ZyanU8 b2 = ((rex & 0 x08) << 4 ) | ((vvvv & 0 xF) << 3 ) | (instruction->vector_length << 2 ) | pp;
ZYAN_CHECK(ZydisEmitByte(0 x8F, buffer));
ZYAN_CHECK(ZydisEmitByte(b1, buffer));
ZYAN_CHECK(ZydisEmitByte(b2, buffer));
return ZYAN_STATUS_SUCCESS;
}
/**
* Emits ` VEX ` prefix .
*
* @ param instruction A pointer to ` ZydisEncoderInstruction ` struct .
* @ param buffer A pointer to ` ZydisEncoderBuffer ` struct .
*
* @ return A zyan status code .
*/
static ZyanStatus ZydisEmitVex(ZydisEncoderInstruction *instruction, ZydisEncoderBuffer *buffer)
{
ZyanU8 mmmmm, pp, vvvv, rex;
ZydisEncodeVexCommons(instruction, &mmmmm, &pp, &vvvv, &rex, ZYAN_NULL);
ZYAN_ASSERT(instruction->vector_length <= 1 );
if (mmmmm != 1 || (rex & 0 x0B))
{
const ZyanU8 b1 = (((~rex) & 0 x07) << 5 ) | mmmmm;
const ZyanU8 b2 = ((rex & 0 x08) << 4 ) |
((vvvv & 0 xF) << 3 ) |
(instruction->vector_length << 2 ) |
pp;
ZYAN_CHECK(ZydisEmitByte(0 xC4, buffer));
ZYAN_CHECK(ZydisEmitByte(b1, buffer));
ZYAN_CHECK(ZydisEmitByte(b2, buffer));
}
else
{
const ZyanU8 b1 = (((~rex) & 0 x04) << 5 ) |
((vvvv & 0 xF) << 3 ) |
(instruction->vector_length << 2 ) |
pp;
ZYAN_CHECK(ZydisEmitByte(0 xC5, buffer));
ZYAN_CHECK(ZydisEmitByte(b1, buffer));
}
return ZYAN_STATUS_SUCCESS;
}
/**
* Encodes common parts of ` EVEX ` prefix .
*
* @ param instruction A pointer to ` ZydisEncoderInstruction ` struct .
* @ param p0 A pointer to ` ZyanU8 ` variable that will receive 2 nd byte of ` EVEX ` prefix .
* @ param p1 A pointer to ` ZyanU8 ` variable that will receive 3 rd byte of ` EVEX ` prefix .
* @ param vvvvv A pointer to ` ZyanU8 ` variable that will receive ` EVEX . vvvvv ` .
*/
static void ZydisEncodeEvexCommons(ZydisEncoderInstruction *instruction, ZyanU8 *p0, ZyanU8 *p1,
ZyanU8 *vvvvv)
{
ZyanBool high_r;
ZyanU8 mmmmm, pp, rex;
ZydisEncodeVexCommons(instruction, &mmmmm, &pp, vvvvv, &rex, &high_r);
*p0 = (((~rex) & 0 x07) << 5 ) | mmmmm;
if (!high_r)
{
*p0 |= 0 x10;
}
*p1 = ((rex & 0 x08) << 4 ) | ((*vvvvv & 0 x0F) << 3 ) | 0 x04 | pp;
}
/**
* Emits ` EVEX ` prefix .
*
* @ param instruction A pointer to ` ZydisEncoderInstruction ` struct .
* @ param buffer A pointer to ` ZydisEncoderBuffer ` struct .
*
* @ return A zyan status code .
*/
static ZyanStatus ZydisEmitEvex(ZydisEncoderInstruction *instruction, ZydisEncoderBuffer *buffer)
{
ZyanU8 p0, p1, vvvvv;
ZydisEncodeEvexCommons(instruction, &p0, &p1, &vvvvv);
ZyanU8 p2 = (instruction->vector_length << 5 ) | ((vvvvv & 0 x10) >> 1 ) | instruction->mask;
if (instruction->zeroing)
{
p2 |= 0 x80;
}
if (instruction->attributes & ZYDIS_ATTRIB_HAS_EVEX_B)
{
p2 |= 0 x10;
}
if (instruction->index & 0 x10)
{
p2 &= 0 xF7;
}
ZYAN_CHECK(ZydisEmitByte(0 x62, buffer));
ZYAN_CHECK(ZydisEmitByte(p0, buffer));
ZYAN_CHECK(ZydisEmitByte(p1, buffer));
ZYAN_CHECK(ZydisEmitByte(p2, buffer));
return ZYAN_STATUS_SUCCESS;
}
/**
* Emits ` MVEX ` prefix .
*
* @ param instruction A pointer to ` ZydisEncoderInstruction ` struct .
* @ param buffer A pointer to ` ZydisEncoderBuffer ` struct .
*
* @ return A zyan status code .
*/
static ZyanStatus ZydisEmitMvex(ZydisEncoderInstruction *instruction, ZydisEncoderBuffer *buffer)
{
ZyanU8 p0, p1, vvvvv;
ZydisEncodeEvexCommons(instruction, &p0, &p1, &vvvvv);
ZyanU8 p2 = (instruction->sss << 4 ) | ((vvvvv & 0 x10) >> 1 ) | instruction->mask;
if (instruction->eviction_hint)
{
p2 |= 0 x80;
}
if (instruction->index & 0 x10)
{
p2 &= 0 xF7;
}
ZYAN_CHECK(ZydisEmitByte(0 x62, buffer));
ZYAN_CHECK(ZydisEmitByte(p0, buffer));
ZYAN_CHECK(ZydisEmitByte(p1 & 0 xFB, buffer));
ZYAN_CHECK(ZydisEmitByte(p2, buffer));
return ZYAN_STATUS_SUCCESS;
}
/**
* Emits instruction as stream of bytes .
*
* @ param instruction A pointer to ` ZydisEncoderInstruction ` struct .
* @ param buffer A pointer to ` ZydisEncoderBuffer ` struct .
*
* @ return A zyan status code .
*/
static ZyanStatus ZydisEmitInstruction(ZydisEncoderInstruction *instruction,
ZydisEncoderBuffer *buffer)
{
ZYAN_CHECK(ZydisEmitLegacyPrefixes(instruction, buffer));
switch (instruction->encoding)
{
case ZYDIS_INSTRUCTION_ENCODING_LEGACY:
case ZYDIS_INSTRUCTION_ENCODING_3DNOW:
ZYAN_CHECK(ZydisEmitRex(instruction, buffer));
break ;
case ZYDIS_INSTRUCTION_ENCODING_XOP:
ZYAN_CHECK(ZydisEmitXop(instruction, buffer));
break ;
case ZYDIS_INSTRUCTION_ENCODING_VEX:
ZYAN_CHECK(ZydisEmitVex(instruction, buffer));
break ;
case ZYDIS_INSTRUCTION_ENCODING_EVEX:
ZYAN_CHECK(ZydisEmitEvex(instruction, buffer));
break ;
case ZYDIS_INSTRUCTION_ENCODING_MVEX:
ZYAN_CHECK(ZydisEmitMvex(instruction, buffer));
break ;
default :
ZYAN_UNREACHABLE;
}
switch (instruction->opcode_map)
{
case ZYDIS_OPCODE_MAP_DEFAULT:
break ;
case ZYDIS_OPCODE_MAP_0F:
ZYAN_CHECK(ZydisEmitByte(0 x0F, buffer));
break ;
case ZYDIS_OPCODE_MAP_0F38:
ZYAN_CHECK(ZydisEmitByte(0 x0F, buffer));
ZYAN_CHECK(ZydisEmitByte(0 x38, buffer));
break ;
case ZYDIS_OPCODE_MAP_0F3A:
ZYAN_CHECK(ZydisEmitByte(0 x0F, buffer));
ZYAN_CHECK(ZydisEmitByte(0 x3A, buffer));
break ;
case ZYDIS_OPCODE_MAP_0F0F:
ZYAN_CHECK(ZydisEmitByte(0 x0F, buffer));
ZYAN_CHECK(ZydisEmitByte(0 x0F, buffer));
break ;
default :
ZYAN_UNREACHABLE;
}
if (instruction->encoding != ZYDIS_INSTRUCTION_ENCODING_3DNOW)
{
ZYAN_CHECK(ZydisEmitByte(instruction->opcode, buffer));
}
if (instruction->attributes & ZYDIS_ATTRIB_HAS_MODRM)
{
const ZyanU8 modrm = (instruction->mod << 6 ) |
((instruction->reg & 7 ) << 3 ) |
(instruction->rm & 7 );
ZYAN_CHECK(ZydisEmitByte(modrm, buffer));
}
if (instruction->attributes & ZYDIS_ATTRIB_HAS_SIB)
{
const ZyanU8 sib = (instruction->scale << 6 ) |
((instruction->index & 7 ) << 3 ) |
(instruction->base & 7 );
ZYAN_CHECK(ZydisEmitByte(sib, buffer));
}
if (instruction->disp_size)
{
ZYAN_CHECK(ZydisEmitUInt(instruction->disp, instruction->disp_size / 8 , buffer));
}
if (instruction->imm_size)
{
ZYAN_CHECK(ZydisEmitUInt(instruction->imm, instruction->imm_size / 8 , buffer));
}
if (instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_3DNOW)
{
ZYAN_CHECK(ZydisEmitByte(instruction->opcode, buffer));
}
return ZYAN_STATUS_SUCCESS;
}
/**
* Encodes register operand as fields inside ` ZydisEncoderInstruction ` structure .
*
* @ param user_op Validated operand definition from ` ZydisEncoderRequest ` structure .
* @ param def_op Decoder ' s operand definition from instruction definition .
* @ param instruction A pointer to ` ZydisEncoderInstruction ` struct .
*/
static void ZydisBuildRegisterOperand(const ZydisEncoderOperand *user_op,
const ZydisOperandDefinition *def_op, ZydisEncoderInstruction *instruction)
{
if (def_op->type == ZYDIS_SEMANTIC_OPTYPE_IMPLICIT_REG)
{
return ;
}
ZyanU8 reg_id = 0 ;
if (ZydisRegisterGetClass(user_op->reg.value) != ZYDIS_REGCLASS_GPR8)
{
reg_id = (ZyanU8)ZydisRegisterGetId(user_op->reg.value);
}
else
{
static const ZyanU8 reg8_lookup[] = {
0 , 1 , 2 , 3 , // AL, CL, DL, BL
4 , 5 , 6 , 7 , // AH, CH, DH, BH
4 , 5 , 6 , 7 , // SPL, BPL, SIL, DIL
8 , 9 , 10 , 11 , 12 , 13 , 14 , 15 , // R8B-R15B
};
ZYAN_ASSERT(
((ZyanUSize)user_op->reg.value - ZYDIS_REGISTER_AL) < ZYAN_ARRAY_LENGTH(reg8_lookup));
reg_id = reg8_lookup[user_op->reg.value - ZYDIS_REGISTER_AL];
if (user_op->reg.value >= ZYDIS_REGISTER_SPL && user_op->reg.value <= ZYDIS_REGISTER_DIL)
{
instruction->attributes |= ZYDIS_ATTRIB_HAS_REX;
}
}
switch (def_op->op.encoding)
{
case ZYDIS_OPERAND_ENCODING_MODRM_REG:
instruction->attributes |= ZYDIS_ATTRIB_HAS_MODRM;
instruction->reg = reg_id;
break ;
case ZYDIS_OPERAND_ENCODING_MODRM_RM:
instruction->attributes |= ZYDIS_ATTRIB_HAS_MODRM;
instruction->rm = reg_id;
break ;
case ZYDIS_OPERAND_ENCODING_OPCODE:
instruction->opcode += reg_id & 7 ;
instruction->rm = reg_id;
break ;
case ZYDIS_OPERAND_ENCODING_NDSNDD:
instruction->vvvv = reg_id;
break ;
case ZYDIS_OPERAND_ENCODING_IS4:
instruction->imm_size = 8 ;
instruction->imm = reg_id << 4 ;
break ;
case ZYDIS_OPERAND_ENCODING_MASK:
instruction->mask = reg_id;
break ;
default :
ZYAN_UNREACHABLE;
}
}
/**
* Encodes memory operand as fields inside ` ZydisEncoderInstruction ` structure .
*
* @ param match A pointer to ` ZydisEncoderInstructionMatch ` struct .
* @ param user_op Decoder ' s operand definition from instruction definition .
* @ param instruction A pointer to ` ZydisEncoderInstruction ` struct .
*/
static void ZydisBuildMemoryOperand(ZydisEncoderInstructionMatch *match,
const ZydisEncoderOperand *user_op, ZydisEncoderInstruction *instruction)
{
instruction->attributes |= ZYDIS_ATTRIB_HAS_MODRM;
instruction->disp = (ZyanU64)user_op->mem.displacement;
if (match->easz == 16 )
{
const ZyanI8 rm = ZydisGetRm16(user_op->mem.base, user_op->mem.index);
if (rm != -1 )
{
instruction->rm = (ZyanU8)rm;
instruction->disp_size = match->disp_size;
switch (instruction->disp_size)
{
case 0 :
if (rm == 6 )
{
instruction->disp_size = 8 ;
instruction->mod = 1 ;
}
break ;
case 8 :
instruction->mod = 1 ;
break ;
case 16 :
instruction->mod = 2 ;
break ;
default :
ZYAN_UNREACHABLE;
}
}
else
{
instruction->rm = 6 ;
instruction->disp_size = 16 ;
}
return ;
}
if (user_op->mem.index == ZYDIS_REGISTER_NONE)
{
if (user_op->mem.base == ZYDIS_REGISTER_NONE)
{
if (match->request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64)
{
instruction->rm = 4 ;
instruction->attributes |= ZYDIS_ATTRIB_HAS_SIB;
instruction->base = 5 ;
instruction->index = 4 ;
}
else
{
instruction->rm = 5 ;
}
instruction->disp_size = 32 ;
return ;
}
else if ((user_op->mem.base == ZYDIS_REGISTER_RIP) ||
(user_op->mem.base == ZYDIS_REGISTER_EIP))
{
instruction->rm = 5 ;
instruction->disp_size = 32 ;
return ;
}
}
const ZyanU8 reg_base_id = (ZyanU8)ZydisRegisterGetId(user_op->mem.base);
const ZyanU8 reg_index_id = (ZyanU8)ZydisRegisterGetId(user_op->mem.index);
instruction->disp_size = match->disp_size;
switch (instruction->disp_size)
{
case 0 :
if (reg_base_id == 5 || reg_base_id == 13 )
{
instruction->disp_size = 8 ;
instruction->disp = 0 ;
instruction->mod = 1 ;
}
break ;
case 8 :
instruction->mod = 1 ;
break ;
case 16 :
instruction->disp_size = 32 ;
ZYAN_FALLTHROUGH;
case 32 :
instruction->mod = 2 ;
break ;
default :
ZYAN_UNREACHABLE;
}
if ((user_op->mem.index == ZYDIS_REGISTER_NONE) &&
(reg_base_id != 4 ) &&
(reg_base_id != 12 ) &&
((match->definition->modrm & 7 ) != 4 ))
{
instruction->rm = reg_base_id;
return ;
}
instruction->rm = 4 ;
instruction->attributes |= ZYDIS_ATTRIB_HAS_SIB;
if (reg_base_id != 0 xFF)
{
instruction->base = reg_base_id;
}
else
{
instruction->base = 5 ;
instruction->mod = 0 ;
instruction->disp_size = 32 ;
}
if (reg_index_id != 0 xFF)
{
instruction->index = reg_index_id;
}
else
{
instruction->index = 4 ;
}
switch (user_op->mem.scale)
{
case 0 :
case 1 :
break ;
case 2 :
instruction->scale = 1 ;
break ;
case 4 :
instruction->scale = 2 ;
break ;
case 8 :
instruction->scale = 3 ;
break ;
default :
ZYAN_UNREACHABLE;
}
}
/**
* Encodes instruction as emittable ` ZydisEncoderInstruction ` struct .
*
* @ param match A pointer to ` ZydisEncoderInstructionMatch ` struct .
* @ param instruction A pointer to ` ZydisEncoderInstruction ` struct .
*
* @ return A zyan status code .
*/
static ZyanStatus ZydisBuildInstruction(ZydisEncoderInstructionMatch *match,
ZydisEncoderInstruction *instruction)
{
ZYAN_MEMSET(instruction, 0 , sizeof (ZydisEncoderInstruction));
instruction->attributes = match->attributes;
instruction->encoding = match->definition->encoding;
instruction->opcode_map = match->definition->opcode_map;
instruction->opcode = match->definition->opcode;
instruction->rex_w = match->definition->rex_w;
instruction->mod = (match->definition->modrm >> 6 ) & 3 ;
instruction->reg = (match->definition->modrm >> 3 ) & 7 ;
instruction->rm = match->definition->modrm & 7 ;
if (match->definition->modrm)
{
instruction->attributes |= ZYDIS_ATTRIB_HAS_MODRM;
}
switch (match->definition->vector_length)
{
case ZYDIS_VECTOR_LENGTH_INVALID:
case ZYDIS_VECTOR_LENGTH_128:
instruction->vector_length = 0 ;
break ;
case ZYDIS_VECTOR_LENGTH_256:
instruction->vector_length = 1 ;
break ;
case ZYDIS_VECTOR_LENGTH_512:
instruction->vector_length = 2 ;
break ;
default :
ZYAN_UNREACHABLE;
}
if (match->definition->encoding == ZYDIS_INSTRUCTION_ENCODING_EVEX)
{
const ZydisInstructionDefinitionEVEX *evex_def =
(const ZydisInstructionDefinitionEVEX *)match->base_definition;
if (evex_def->mask_override != ZYDIS_MASK_OVERRIDE_ZEROING)
{
instruction->zeroing = match->request->evex.zeroing_mask;
}
if ((match->request->evex.sae) ||
(match->request->evex.broadcast != ZYDIS_BROADCAST_MODE_INVALID))
{
instruction->attributes |= ZYDIS_ATTRIB_HAS_EVEX_B;
}
if (match->request->evex.rounding != ZYDIS_ROUNDING_MODE_INVALID)
{
instruction->attributes |= ZYDIS_ATTRIB_HAS_EVEX_B;
switch (match->request->evex.rounding)
{
case ZYDIS_ROUNDING_MODE_RN:
instruction->vector_length = 0 ;
break ;
case ZYDIS_ROUNDING_MODE_RD:
instruction->vector_length = 1 ;
break ;
case ZYDIS_ROUNDING_MODE_RU:
instruction->vector_length = 2 ;
break ;
case ZYDIS_ROUNDING_MODE_RZ:
instruction->vector_length = 3 ;
break ;
default :
ZYAN_UNREACHABLE;
}
}
}
else if (match->definition->encoding == ZYDIS_INSTRUCTION_ENCODING_MVEX)
{
instruction->sss |= ZydisEncodeMvexBroadcastMode(match->request->mvex.broadcast);
instruction->sss |= ZydisEncodeMvexConversionMode(match->request->mvex.conversion);
switch (match->request->mvex.rounding)
{
case ZYDIS_ROUNDING_MODE_INVALID:
break ;
case ZYDIS_ROUNDING_MODE_RN:
case ZYDIS_ROUNDING_MODE_RD:
case ZYDIS_ROUNDING_MODE_RU:
case ZYDIS_ROUNDING_MODE_RZ:
instruction->sss |= match->request->mvex.rounding - ZYDIS_ROUNDING_MODE_RN;
break ;
default :
ZYAN_UNREACHABLE;
}
switch (match->request->mvex.swizzle)
{
case ZYDIS_SWIZZLE_MODE_INVALID:
break ;
case ZYDIS_SWIZZLE_MODE_DCBA:
case ZYDIS_SWIZZLE_MODE_CDAB:
case ZYDIS_SWIZZLE_MODE_BADC:
case ZYDIS_SWIZZLE_MODE_DACB:
case ZYDIS_SWIZZLE_MODE_AAAA:
case ZYDIS_SWIZZLE_MODE_BBBB:
case ZYDIS_SWIZZLE_MODE_CCCC:
case ZYDIS_SWIZZLE_MODE_DDDD:
instruction->sss |= match->request->mvex.swizzle - ZYDIS_SWIZZLE_MODE_DCBA;
break ;
default :
ZYAN_UNREACHABLE;
}
if ((match->request->mvex.sae) ||
(match->request->mvex.eviction_hint) ||
(match->request->mvex.rounding != ZYDIS_ROUNDING_MODE_INVALID))
{
instruction->eviction_hint = ZYAN_TRUE;
}
if (match->request->mvex.sae)
{
instruction->sss |= 4 ;
}
// Following instructions violate general `MVEX.EH` handling rules. In all other cases this
// bit is used either as eviction hint (memory operands present) or to encode MVEX-specific
// functionality (register forms). Instructions listed below use `MVEX.EH` to identify
// different instructions with memory operands and don't treat it as eviction hint.
switch (match->request->mnemonic)
{
case ZYDIS_MNEMONIC_VMOVNRAPD:
case ZYDIS_MNEMONIC_VMOVNRAPS:
instruction->eviction_hint = ZYAN_FALSE;
break ;
case ZYDIS_MNEMONIC_VMOVNRNGOAPD:
case ZYDIS_MNEMONIC_VMOVNRNGOAPS:
instruction->eviction_hint = ZYAN_TRUE;
break ;
default :
break ;
}
}
switch (match->definition->mandatory_prefix)
{
case ZYDIS_MANDATORY_PREFIX_NONE:
break ;
case ZYDIS_MANDATORY_PREFIX_66:
instruction->attributes |= ZYDIS_ATTRIB_HAS_OPERANDSIZE;
break ;
case ZYDIS_MANDATORY_PREFIX_F2:
instruction->attributes |= ZYDIS_ATTRIB_HAS_REPNE;
break ;
case ZYDIS_MANDATORY_PREFIX_F3:
instruction->attributes |= ZYDIS_ATTRIB_HAS_REP;
break ;
default :
ZYAN_UNREACHABLE;
}
const ZyanU8 mode_width = ZydisGetMachineModeWidth(match->request->machine_mode);
if (match->easz != mode_width)
{
instruction->attributes |= ZYDIS_ATTRIB_HAS_ADDRESSSIZE;
}
if ((match->request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) &&
(match->base_definition->operand_size_map != ZYDIS_OPSIZE_MAP_FORCE64))
{
switch (match->eosz)
{
case 16 :
instruction->attributes |= ZYDIS_ATTRIB_HAS_OPERANDSIZE;
break ;
case 32 :
break ;
case 64 :
instruction->rex_w =
match->base_definition->operand_size_map != ZYDIS_OPSIZE_MAP_DEFAULT64;
break ;
default :
ZYAN_UNREACHABLE;
}
}
else
{
if (match->eosz != mode_width)
{
instruction->attributes |= ZYDIS_ATTRIB_HAS_OPERANDSIZE;
}
}
for (ZyanU8 i = 0 ; i < match->request->operand_count; ++i)
{
const ZydisEncoderOperand *user_op = &match->request->operands[i];
const ZydisOperandDefinition *def_op = &match->operands[i];
switch (user_op->type)
{
case ZYDIS_OPERAND_TYPE_REGISTER:
ZydisBuildRegisterOperand(user_op, def_op, instruction);
break ;
case ZYDIS_OPERAND_TYPE_MEMORY:
if (def_op->type != ZYDIS_SEMANTIC_OPTYPE_MOFFS)
{
ZydisBuildMemoryOperand(match, user_op, instruction);
if ((match->cd8_scale) &&
(instruction->disp_size == 8 ))
{
instruction->disp >>= match->cd8_scale;
}
}
else
{
instruction->disp_size = match->disp_size;
instruction->disp = (ZyanU64)user_op->mem.displacement;
}
break ;
case ZYDIS_OPERAND_TYPE_POINTER:
instruction->disp_size = match->disp_size;
instruction->disp = user_op->ptr.offset;
instruction->imm_size = match->imm_size;
instruction->imm = user_op->ptr.segment;
break ;
case ZYDIS_OPERAND_TYPE_IMMEDIATE:
if (def_op->type == ZYDIS_SEMANTIC_OPTYPE_IMPLICIT_IMM1)
{
break ;
}
if (def_op->op.encoding != ZYDIS_OPERAND_ENCODING_IS4)
{
if (instruction->imm_size)
{
ZYAN_ASSERT(instruction->disp_size == 0 );
instruction->disp_size = match->disp_size;
instruction->disp = instruction->imm;
}
instruction->imm_size = match->imm_size;
instruction->imm = user_op->imm.u;
}
else
{
ZYAN_ASSERT(instruction->imm_size == 8 );
instruction->imm |= user_op->imm.u;
}
break ;
default :
ZYAN_UNREACHABLE;
}
}
return ZYAN_STATUS_SUCCESS;
}
/**
* Performs a set of sanity checks that must be satisfied for every valid encoder request .
*
* @ param request A pointer to ` ZydisEncoderRequest ` struct .
*
* @ return A zyan status code .
*/
static ZyanStatus ZydisEncoderCheckRequestSanity(const ZydisEncoderRequest *request)
{
if (((ZyanUSize)request->machine_mode > ZYDIS_MACHINE_MODE_MAX_VALUE) ||
((ZyanUSize)request->allowed_encodings > ZYDIS_ENCODABLE_ENCODING_MAX_VALUE) ||
((ZyanUSize)request->mnemonic > ZYDIS_MNEMONIC_MAX_VALUE) ||
((ZyanUSize)request->branch_type > ZYDIS_BRANCH_TYPE_MAX_VALUE) ||
((ZyanUSize)request->branch_width > ZYDIS_BRANCH_WIDTH_MAX_VALUE) ||
((ZyanUSize)request->address_size_hint > ZYDIS_ADDRESS_SIZE_HINT_MAX_VALUE) ||
((ZyanUSize)request->operand_size_hint > ZYDIS_OPERAND_SIZE_HINT_MAX_VALUE) ||
((ZyanUSize)request->evex.broadcast > ZYDIS_BROADCAST_MODE_MAX_VALUE) ||
((ZyanUSize)request->evex.rounding > ZYDIS_ROUNDING_MODE_MAX_VALUE) ||
((ZyanUSize)request->mvex.broadcast > ZYDIS_BROADCAST_MODE_MAX_VALUE) ||
((ZyanUSize)request->mvex.conversion > ZYDIS_CONVERSION_MODE_MAX_VALUE) ||
((ZyanUSize)request->mvex.rounding > ZYDIS_ROUNDING_MODE_MAX_VALUE) ||
((ZyanUSize)request->mvex.swizzle > ZYDIS_SWIZZLE_MODE_MAX_VALUE) ||
(request->operand_count > ZYDIS_ENCODER_MAX_OPERANDS) ||
(request->mnemonic == ZYDIS_MNEMONIC_INVALID) ||
(request->prefixes & ~ZYDIS_ENCODABLE_PREFIXES))
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
if (request->prefixes & ZYDIS_ATTRIB_HAS_SEGMENT)
{
if ((request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) &&
(request->prefixes & ZYDIS_LEGACY_SEGMENTS))
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
ZyanU8 seg_override_count = 0 ;
if (request->prefixes & ZYDIS_ATTRIB_HAS_SEGMENT_CS)
{
++seg_override_count;
}
if (request->prefixes & ZYDIS_ATTRIB_HAS_SEGMENT_SS)
{
++seg_override_count;
}
if (request->prefixes & ZYDIS_ATTRIB_HAS_SEGMENT_DS)
{
++seg_override_count;
}
if (request->prefixes & ZYDIS_ATTRIB_HAS_SEGMENT_ES)
{
++seg_override_count;
}
if (request->prefixes & ZYDIS_ATTRIB_HAS_SEGMENT_FS)
{
++seg_override_count;
}
if (request->prefixes & ZYDIS_ATTRIB_HAS_SEGMENT_GS)
{
++seg_override_count;
}
if (seg_override_count != 1 )
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
}
ZyanU8 rep_family_count = 0 ;
if (request->prefixes & ZYDIS_ATTRIB_HAS_REP)
{
++rep_family_count;
}
if (request->prefixes & ZYDIS_ATTRIB_HAS_REPE)
{
++rep_family_count;
}
if (request->prefixes & ZYDIS_ATTRIB_HAS_REPNE)
{
++rep_family_count;
}
if (rep_family_count > 1 )
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
if ((request->prefixes & ZYDIS_ATTRIB_HAS_XACQUIRE) &&
(request->prefixes & ZYDIS_ATTRIB_HAS_XRELEASE))
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
if ((request->prefixes & ZYDIS_ATTRIB_HAS_BRANCH_NOT_TAKEN) &&
(request->prefixes & ZYDIS_ATTRIB_HAS_BRANCH_TAKEN))
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
if ((request->prefixes & ZYDIS_ATTRIB_HAS_NOTRACK) &&
(request->prefixes & ZYDIS_ATTRIB_HAS_SEGMENT))
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
static const ZyanBool branch_lookup
[ZYDIS_BRANCH_WIDTH_MAX_VALUE + 1 ][ZYDIS_BRANCH_TYPE_MAX_VALUE + 1 ] =
{
/* NONE */ { ZYAN_TRUE, ZYAN_TRUE, ZYAN_TRUE, ZYAN_TRUE },
/* 8 */ { ZYAN_TRUE, ZYAN_TRUE, ZYAN_FALSE, ZYAN_FALSE },
/* 16 */ { ZYAN_TRUE, ZYAN_FALSE, ZYAN_TRUE, ZYAN_TRUE },
/* 32 */ { ZYAN_TRUE, ZYAN_FALSE, ZYAN_TRUE, ZYAN_TRUE },
/* 64 */ { ZYAN_TRUE, ZYAN_FALSE, ZYAN_TRUE, ZYAN_TRUE },
};
if (!branch_lookup[request->branch_width][request->branch_type])
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
if (request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64)
{
if (request->address_size_hint == ZYDIS_ADDRESS_SIZE_HINT_16)
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
}
else
{
if ((request->branch_width == ZYDIS_BRANCH_WIDTH_64) ||
(request->address_size_hint == ZYDIS_ADDRESS_SIZE_HINT_64) ||
(request->operand_size_hint == ZYDIS_OPERAND_SIZE_HINT_64))
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
}
for (ZyanU8 i = 0 ; i < request->operand_count; ++i)
{
const ZydisEncoderOperand *op = &request->operands[i];
if ((op->type == ZYDIS_OPERAND_TYPE_UNUSED) ||
((ZyanUSize)op->type > ZYDIS_OPERAND_TYPE_MAX_VALUE))
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
switch (op->type)
{
case ZYDIS_OPERAND_TYPE_REGISTER:
if (op->reg.value > ZYDIS_REGISTER_MAX_VALUE)
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
break ;
case ZYDIS_OPERAND_TYPE_MEMORY:
if (((ZyanUSize)op->mem.base > ZYDIS_REGISTER_MAX_VALUE) ||
((ZyanUSize)op->mem.index > ZYDIS_REGISTER_MAX_VALUE) ||
!ZydisIsScaleValid(op->mem.scale))
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
break ;
case ZYDIS_OPERAND_TYPE_POINTER:
case ZYDIS_OPERAND_TYPE_IMMEDIATE:
break ;
default :
return ZYAN_STATUS_INVALID_ARGUMENT;
}
}
return ZYAN_STATUS_SUCCESS;
}
/**
* Encodes instruction with semantics specified in encoder request structure .
*
* @ param request A pointer to the ` ZydisEncoderRequest ` struct . Must be validated before
* calling this function .
* @ param buffer A pointer to the output buffer receiving encoded instruction .
* @ param length A pointer to the variable containing length of the output buffer . Upon
* successful return this variable receives length of the encoded instruction .
* @ param instruction Internal state of the encoder .
*
* @ return A zyan status code .
*/
static ZyanStatus ZydisEncoderEncodeInstructionInternal(const ZydisEncoderRequest *request,
void *buffer, ZyanUSize *length, ZydisEncoderInstruction *instruction)
{
ZydisEncoderInstructionMatch match;
ZYAN_CHECK(ZydisFindMatchingDefinition(request, &match));
ZydisEncoderBuffer output;
output.buffer = (ZyanU8 *)buffer;
output.size = *length;
output.offset = 0 ;
ZYAN_CHECK(ZydisBuildInstruction(&match, instruction));
ZYAN_CHECK(ZydisEmitInstruction(instruction, &output));
*length = output.offset;
return ZYAN_STATUS_SUCCESS;
}
/* ============================================================================================== */
/* Exported functions */
/* ============================================================================================== */
ZYDIS_EXPORT ZyanStatus ZydisEncoderEncodeInstruction(const ZydisEncoderRequest *request,
void *buffer, ZyanUSize *length)
{
if (!request || !buffer || !length)
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
ZYAN_CHECK(ZydisEncoderCheckRequestSanity(request));
ZydisEncoderInstruction instruction;
return ZydisEncoderEncodeInstructionInternal(request, buffer, length, &instruction);
}
ZYDIS_EXPORT ZyanStatus ZydisEncoderEncodeInstructionAbsolute(ZydisEncoderRequest *request,
void *buffer, ZyanUSize *length, ZyanU64 runtime_address)
{
if (!request || !buffer || !length)
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
ZYAN_CHECK(ZydisEncoderCheckRequestSanity(request));
const ZydisEncoderRelInfo *rel_info = ZydisGetRelInfo(request->mnemonic);
ZydisEncoderOperand *op_rip_rel = ZYAN_NULL;
ZyanBool adjusted_rel = ZYAN_FALSE;
ZyanU64 absolute_address = 0 ;
ZyanU8 mode_index = ZydisGetMachineModeWidth(request->machine_mode) >> 5 ;
for (ZyanU8 i = 0 ; i < request->operand_count; ++i)
{
ZydisEncoderOperand *op = &request->operands[i];
if ((op->type == ZYDIS_OPERAND_TYPE_IMMEDIATE) && rel_info)
{
if (adjusted_rel)
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
switch (rel_info->accepts_scaling_hints)
{
case ZYDIS_SIZE_HINT_NONE:
case ZYDIS_SIZE_HINT_OSZ:
{
static const ZyanI8 asz_priority[3 ][3 ] =
{
{ 0 , 1 , 2 },
{ 0 , 2 , 1 },
{ 0 , 2 , -1 },
};
static const ZyanI8 osz_priority[3 ][3 ] =
{
{ 0 , 1 , 2 },
{ 0 , 2 , 1 },
{ 0 , 2 , 1 },
};
ZyanI8 forced_priority_row[3 ] = { -1 , -1 , -1 };
ZyanI8 *priority_row = ZYAN_NULL;
ZyanU8 extra_length = 0 ;
ZyanU8 start_offset = 0 ;
if (rel_info->accepts_scaling_hints == ZYDIS_SIZE_HINT_NONE)
{
if ((request->branch_type == ZYDIS_BRANCH_TYPE_FAR) ||
(request->branch_width == ZYDIS_BRANCH_WIDTH_64))
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
if ((rel_info->accepts_branch_hints) &&
(request->prefixes & (ZYDIS_ATTRIB_HAS_BRANCH_NOT_TAKEN |
ZYDIS_ATTRIB_HAS_BRANCH_TAKEN)))
{
extra_length += 1 ;
}
if ((rel_info->accepts_bound) && (request->prefixes & ZYDIS_ATTRIB_HAS_BND))
{
extra_length += 1 ;
// `BND` prefix is not accepted for short `JMP` (Intel SDM Vol. 1)
if ((request->mnemonic == ZYDIS_MNEMONIC_JMP) &&
(request->branch_type == ZYDIS_BRANCH_TYPE_NONE) &&
(request->branch_width == ZYDIS_BRANCH_WIDTH_NONE))
{
start_offset = 1 ;
}
}
if (request->branch_width == ZYDIS_BRANCH_WIDTH_NONE)
{
if (request->branch_type == ZYDIS_BRANCH_TYPE_NEAR)
{
start_offset = 1 ;
}
priority_row = (ZyanI8 *)&asz_priority[mode_index];
}
else
{
forced_priority_row[0 ] = (ZyanI8)(request->branch_width - 1 );
priority_row = (ZyanI8 *)&forced_priority_row;
}
}
else
{
if (request->operand_size_hint == ZYDIS_OPERAND_SIZE_HINT_NONE)
{
priority_row = (ZyanI8 *)&osz_priority[mode_index];
}
else
{
if (request->operand_size_hint == ZYDIS_OPERAND_SIZE_HINT_64)
{
extra_length = 1 ;
forced_priority_row[0 ] = 2 ;
}
else
{
forced_priority_row[0 ] = (ZyanI8)(request->operand_size_hint - 1 );
}
priority_row = (ZyanI8 *)&forced_priority_row;
}
}
ZYAN_ASSERT(ZYAN_ARRAY_LENGTH(asz_priority[0 ]) ==
ZYAN_ARRAY_LENGTH(osz_priority[0 ]));
for (ZyanU8 j = start_offset; j < ZYAN_ARRAY_LENGTH(asz_priority[0 ]); ++j)
{
ZyanI8 size_index = priority_row[j];
if (size_index < 0 )
{
break ;
}
ZyanU8 base_size = rel_info->size[mode_index][size_index];
if (base_size == 0 )
{
continue ;
}
ZyanU8 predicted_size = base_size + extra_length;
if (runtime_address > ZYAN_UINT64_MAX - predicted_size + 1 )
{
continue ;
}
ZyanI64 rel = (ZyanI64)(op->imm.u - (runtime_address + predicted_size));
ZyanU8 rel_size = ZydisGetSignedImmSize(rel);
if (rel_size > (8 << size_index))
{
continue ;
}
op->imm.s = rel;
adjusted_rel = ZYAN_TRUE;
if (rel_info->accepts_scaling_hints == ZYDIS_SIZE_HINT_NONE)
{
if (request->branch_width == ZYDIS_BRANCH_WIDTH_NONE)
{
request->branch_width =
(ZydisBranchWidth)(ZYDIS_BRANCH_WIDTH_8 + size_index);
}
}
else
{
if (request->operand_size_hint == ZYDIS_OPERAND_SIZE_HINT_NONE)
{
request->operand_size_hint =
(ZydisOperandSizeHint)(ZYDIS_OPERAND_SIZE_HINT_8 + size_index);
}
}
break;
}
break;
}
case ZYDIS_SIZE_HINT_ASZ:
{
static const ZyanI8 asz_prefix_lookup[3 ][ZYDIS_ADDRESS_SIZE_HINT_MAX_VALUE + 1 ] =
{
{ 0 , 0 , 1 , -1 },
{ 0 , 1 , 0 , -1 },
{ 0 , -1 , 1 , 0 },
};
ZyanI8 extra_length = asz_prefix_lookup[mode_index][request->address_size_hint];
if (extra_length < 0 )
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
ZyanU8 asz_index = (request->address_size_hint == ZYDIS_ADDRESS_SIZE_HINT_NONE)
? mode_index
: ZydisGetAszFromHint(request->address_size_hint) >> 5 ;
ZYAN_ASSERT((rel_info->size[asz_index][0 ] != 0 ) &&
(rel_info->size[asz_index][1 ] == 0 ) &&
(rel_info->size[asz_index][2 ] == 0 ) &&
!rel_info->accepts_branch_hints);
ZyanU8 predicted_size = rel_info->size[asz_index][0 ] + extra_length;
if (runtime_address > ZYAN_UINT64_MAX - predicted_size + 1 )
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
ZyanI64 rel = (ZyanI64)(op->imm.u - (runtime_address + predicted_size));
ZyanU8 rel_size = ZydisGetSignedImmSize(rel);
if (rel_size > 8 )
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
op->imm.s = rel;
adjusted_rel = ZYAN_TRUE;
break;
}
default:
ZYAN_UNREACHABLE;
}
if (!adjusted_rel)
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
}
else if ((op->type == ZYDIS_OPERAND_TYPE_MEMORY) &&
((op->mem.base == ZYDIS_REGISTER_EIP) ||
(op->mem.base == ZYDIS_REGISTER_RIP)))
{
if (op_rip_rel)
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
absolute_address = op->mem.displacement;
op->mem.displacement = 0 ;
op_rip_rel = op;
}
}
ZydisEncoderInstruction instruction;
ZYAN_CHECK(ZydisEncoderEncodeInstructionInternal(request, buffer, length, &instruction));
if (op_rip_rel)
{
ZyanUSize instruction_size = *length;
if (runtime_address > ZYAN_UINT64_MAX - instruction_size + 1 )
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
ZyanI64 rip_rel = (ZyanI64)(absolute_address - (runtime_address + instruction_size));
if (ZydisGetSignedImmSize(rip_rel) > 32 )
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
ZYAN_ASSERT(instruction.disp_size != 0 );
ZyanU8 disp_offset = (instruction.disp_size >> 3 ) + (instruction.imm_size >> 3 );
if (instruction.encoding == ZYDIS_INSTRUCTION_ENCODING_3DNOW)
{
disp_offset += 1 ;
}
ZYAN_ASSERT(instruction_size > disp_offset);
ZYAN_MEMCPY((ZyanU8 *)buffer + instruction_size - disp_offset, &rip_rel, sizeof(ZyanI32));
op_rip_rel->mem.displacement = rip_rel;
}
return ZYAN_STATUS_SUCCESS;
}
ZYDIS_EXPORT ZyanStatus ZydisEncoderDecodedInstructionToEncoderRequest(
const ZydisDecodedInstruction *instruction, const ZydisDecodedOperand* operands,
ZyanU8 operand_count, ZydisEncoderRequest *request)
{
if (!instruction || !request || (operand_count && !operands))
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
ZYAN_MEMSET(request, 0 , sizeof(ZydisEncoderRequest));
request->machine_mode = instruction->machine_mode;
request->mnemonic = instruction->mnemonic;
request->prefixes = instruction->attributes & ZYDIS_ENCODABLE_PREFIXES;
request->branch_type = instruction->meta.branch_type;
if (!(instruction->attributes & ZYDIS_ATTRIB_ACCEPTS_SEGMENT))
{
request->prefixes &= ~ZYDIS_ATTRIB_HAS_SEGMENT;
}
switch (instruction->address_width)
{
case 16 :
request->address_size_hint = ZYDIS_ADDRESS_SIZE_HINT_16;
break;
case 32 :
request->address_size_hint = ZYDIS_ADDRESS_SIZE_HINT_32;
break;
case 64 :
request->address_size_hint = ZYDIS_ADDRESS_SIZE_HINT_64;
break;
default:
return ZYAN_STATUS_INVALID_ARGUMENT;
}
switch (instruction->operand_width)
{
case 8 :
request->operand_size_hint = ZYDIS_OPERAND_SIZE_HINT_8;
break;
case 16 :
request->operand_size_hint = ZYDIS_OPERAND_SIZE_HINT_16;
break;
case 32 :
request->operand_size_hint = ZYDIS_OPERAND_SIZE_HINT_32;
break;
case 64 :
request->operand_size_hint = ZYDIS_OPERAND_SIZE_HINT_64;
break;
default:
return ZYAN_STATUS_INVALID_ARGUMENT;
}
switch (request->branch_type)
{
case ZYDIS_BRANCH_TYPE_NONE:
request->branch_width = ZYDIS_BRANCH_WIDTH_NONE;
break;
case ZYDIS_BRANCH_TYPE_SHORT:
request->branch_width = ZYDIS_BRANCH_WIDTH_8;
break;
case ZYDIS_BRANCH_TYPE_NEAR:
case ZYDIS_BRANCH_TYPE_FAR:
switch (instruction->operand_width)
{
case 16 :
request->branch_width = ZYDIS_BRANCH_WIDTH_16;
break;
case 32 :
request->branch_width = ZYDIS_BRANCH_WIDTH_32;
break;
case 64 :
request->branch_width = ZYDIS_BRANCH_WIDTH_64;
break;
default:
ZYAN_UNREACHABLE;
}
break;
default:
return ZYAN_STATUS_INVALID_ARGUMENT;
}
switch (instruction->encoding)
{
case ZYDIS_INSTRUCTION_ENCODING_LEGACY:
case ZYDIS_INSTRUCTION_ENCODING_3DNOW:
case ZYDIS_INSTRUCTION_ENCODING_XOP:
case ZYDIS_INSTRUCTION_ENCODING_VEX:
break;
case ZYDIS_INSTRUCTION_ENCODING_EVEX:
request->evex.broadcast = !instruction->avx.broadcast.is_static ?
instruction->avx.broadcast.mode : ZYDIS_BROADCAST_MODE_INVALID;
request->evex.rounding = instruction->avx.rounding.mode;
request->evex.sae = instruction->avx.has_sae;
request->evex.zeroing_mask = (instruction->avx.mask.mode == ZYDIS_MASK_MODE_ZEROING ||
instruction->avx.mask.mode == ZYDIS_MASK_MODE_CONTROL_ZEROING) &&
(instruction->raw.evex.z) ? ZYAN_TRUE : ZYAN_FALSE;
break;
case ZYDIS_INSTRUCTION_ENCODING_MVEX:
request->mvex.broadcast = !instruction->avx.broadcast.is_static ?
instruction->avx.broadcast.mode : ZYDIS_BROADCAST_MODE_INVALID;
request->mvex.conversion = instruction->avx.conversion.mode;
request->mvex.rounding = instruction->avx.rounding.mode;
request->mvex.swizzle = instruction->avx.swizzle.mode;
request->mvex.sae = instruction->avx.has_sae;
request->mvex.eviction_hint = instruction->avx.has_eviction_hint;
break;
default:
return ZYAN_STATUS_INVALID_ARGUMENT;
}
request->allowed_encodings = 1 << instruction->encoding;
if ((operand_count > ZYDIS_ENCODER_MAX_OPERANDS) ||
(operand_count > instruction->operand_count_visible))
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
request->operand_count = operand_count;
for (ZyanU8 i = 0 ; i < operand_count; ++i)
{
const ZydisDecodedOperand *dec_op = &operands[i];
ZydisEncoderOperand *enc_op = &request->operands[i];
enc_op->type = dec_op->type;
switch (dec_op->type)
{
case ZYDIS_OPERAND_TYPE_REGISTER:
enc_op->reg.value = dec_op->reg.value;
enc_op->reg.is4 = dec_op->encoding == ZYDIS_OPERAND_ENCODING_IS4;
break;
case ZYDIS_OPERAND_TYPE_MEMORY:
enc_op->mem.base = dec_op->mem.base;
enc_op->mem.index = dec_op->mem.index;
enc_op->mem.scale = dec_op->mem.type != ZYDIS_MEMOP_TYPE_MIB ? dec_op->mem.scale : 0 ;
if (dec_op->mem.disp.has_displacement)
{
enc_op->mem.displacement = dec_op->mem.disp.value;
}
enc_op->mem.size = dec_op->size / 8 ;
break;
case ZYDIS_OPERAND_TYPE_POINTER:
enc_op->ptr.segment = dec_op->ptr.segment;
enc_op->ptr.offset = dec_op->ptr.offset;
break;
case ZYDIS_OPERAND_TYPE_IMMEDIATE:
enc_op->imm.u = dec_op->imm.value.u;
// `XBEGIN` is an ISA-wide unique instruction because it's not a branching instruction
// but it has a relative operand which behaves differently from all other relatives
// (no truncating behavior in 16 -bit mode). Encoder treats it as non-branching
// instruction that scales with hidden operand size.
if ((dec_op->imm.is_relative) &&
(instruction->mnemonic != ZYDIS_MNEMONIC_XBEGIN))
{
switch (instruction->raw.imm->size)
{
case 8 :
request->branch_width = ZYDIS_BRANCH_WIDTH_8;
break;
case 16 :
request->branch_width = ZYDIS_BRANCH_WIDTH_16;
break;
case 32 :
request->branch_width = ZYDIS_BRANCH_WIDTH_32;
break;
default:
return ZYAN_STATUS_INVALID_ARGUMENT;
}
}
break;
default:
return ZYAN_STATUS_INVALID_ARGUMENT;
}
}
return ZYAN_STATUS_SUCCESS;
}
ZYDIS_EXPORT ZyanStatus ZydisEncoderNopFill(void *buffer, ZyanUSize length)
{
if (!buffer)
{
return ZYAN_STATUS_INVALID_ARGUMENT;
}
// Intel SDM Vol. 2 B "Recommended Multi-Byte Sequence of NOP Instruction"
static const ZyanU8 nops[9 ][9 ] =
{
{ 0 x90 },
{ 0 x66, 0 x90 },
{ 0 x0F, 0 x1F, 0 x00 },
{ 0 x0F, 0 x1F, 0 x40, 0 x00 },
{ 0 x0F, 0 x1F, 0 x44, 0 x00, 0 x00 },
{ 0 x66, 0 x0F, 0 x1F, 0 x44, 0 x00, 0 x00 },
{ 0 x0F, 0 x1F, 0 x80, 0 x00, 0 x00, 0 x00, 0 x00 },
{ 0 x0F, 0 x1F, 0 x84, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00 },
{ 0 x66, 0 x0F, 0 x1F, 0 x84, 0 x00, 0 x00, 0 x00, 0 x00, 0 x00 },
};
ZyanU8 *output = (ZyanU8 *)buffer;
while (length)
{
ZyanUSize nop_size = (length > 9 ) ? 9 : length;
ZYAN_MEMCPY(output, nops[nop_size - 1 ], nop_size);
output += nop_size;
length -= nop_size;
}
return ZYAN_STATUS_SUCCESS;
}
/* ============================================================================================== */
Messung V0.5 in Prozent C=95 H=100 G=97
¤ Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.0.158Bemerkung:
¤
*Bot Zugriff