summaryrefslogtreecommitdiffstats
path: root/js/src/zydis/Zydis/Encoder.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/src/zydis/Zydis/Encoder.c4742
1 files changed, 4742 insertions, 0 deletions
diff --git a/js/src/zydis/Zydis/Encoder.c b/js/src/zydis/Zydis/Encoder.c
new file mode 100644
index 0000000000..6f7256fe34
--- /dev/null
+++ b/js/src/zydis/Zydis/Encoder.c
@@ -0,0 +1,4742 @@
+/***************************************************************************************************
+
+ Zyan Disassembler Library (Zydis)
+
+ Original Author : Mappa
+
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all
+ * copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+
+***************************************************************************************************/
+
+// ReSharper disable CppClangTidyClangDiagnosticSwitchEnum
+// ReSharper disable CppClangTidyClangDiagnosticCoveredSwitchDefault
+// ReSharper disable CppClangTidyClangDiagnosticImplicitFallthrough
+
+#include "zydis/Zycore/LibC.h"
+#include "zydis/Zydis/Encoder.h"
+#include "zydis/Zydis/Utils.h"
+#include "zydis/Zydis/Internal/EncoderData.h"
+#include "zydis/Zydis/Internal/SharedData.h"
+
+/* ============================================================================================== */
+/* Macros */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Constants */
+/* ---------------------------------------------------------------------------------------------- */
+
+#define ZYDIS_OPSIZE_MAP_BYTEOP 1
+#define ZYDIS_OPSIZE_MAP_DEFAULT64 4
+#define ZYDIS_OPSIZE_MAP_FORCE64 5
+#define ZYDIS_ADSIZE_MAP_IGNORED 1
+#define ZYDIS_LEGACY_SEGMENTS (ZYDIS_ATTRIB_HAS_SEGMENT_CS | \
+ ZYDIS_ATTRIB_HAS_SEGMENT_SS | \
+ ZYDIS_ATTRIB_HAS_SEGMENT_DS | \
+ ZYDIS_ATTRIB_HAS_SEGMENT_ES)
+#define ZYDIS_ENCODABLE_PREFIXES_NO_SEGMENTS (ZYDIS_ENCODABLE_PREFIXES ^ \
+ ZYDIS_ATTRIB_HAS_SEGMENT)
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+/* Internal enums and types */
+/* ============================================================================================== */
+
+/**
+ * Usage of `REX.W` prefix makes it impossible to use some byte-sized registers. Values of this
+ * enum are used to track and facilitate enforcement of these restrictions.
+ */
+typedef enum ZydisEncoderRexType_
+{
+ ZYDIS_REX_TYPE_UNKNOWN,
+ ZYDIS_REX_TYPE_REQUIRED,
+ ZYDIS_REX_TYPE_FORBIDDEN,
+
+ /**
+ * Maximum value of this enum.
+ */
+ ZYDIS_REX_TYPE_MAX_VALUE = ZYDIS_REX_TYPE_FORBIDDEN,
+ /**
+ * The minimum number of bits required to represent all values of this enum.
+ */
+ ZYDIS_REX_TYPE_REQUIRED_BITS = ZYAN_BITS_TO_REPRESENT(ZYDIS_REX_TYPE_MAX_VALUE)
+} ZydisEncoderRexType;
+
+/**
+ * Primary structure used during instruction matching phase. Once filled it contains information
+ * about matched instruction definition and some values deduced from encoder request. It gets
+ * converted to `ZydisEncoderInstruction` during instruction building phase.
+ */
+typedef struct ZydisEncoderInstructionMatch_
+{
+ /**
+ * A pointer to the `ZydisEncoderRequest` instance.
+ */
+ const ZydisEncoderRequest *request;
+ /**
+ * A pointer to the `ZydisEncodableInstruction` instance.
+ */
+ const ZydisEncodableInstruction *definition;
+ /**
+ * A pointer to the `ZydisInstructionDefinition` instance.
+ */
+ const ZydisInstructionDefinition *base_definition;
+ /**
+ * A pointer to the `ZydisOperandDefinition` array.
+ */
+ const ZydisOperandDefinition *operands;
+ /**
+ * Encodable attributes for this instruction.
+ */
+ ZydisInstructionAttributes attributes;
+ /**
+ * Effective operand size attribute.
+ */
+ ZyanU8 eosz;
+ /**
+ * Effective address size attribute.
+ */
+ ZyanU8 easz;
+ /**
+ * Effective displacement size.
+ */
+ ZyanU8 disp_size;
+ /**
+ * Effective immediate size.
+ */
+ ZyanU8 imm_size;
+ /**
+ * Exponent of compressed displacement scale factor (2^cd8_scale)
+ */
+ ZyanU8 cd8_scale;
+ /**
+ * `REX` prefix constraints.
+ */
+ ZydisEncoderRexType rex_type;
+ /**
+ * True for special cases where operand size attribute must be lower than 64 bits.
+ */
+ ZyanBool eosz64_forbidden;
+ /**
+ * True when instruction definition has relative operand (used for branching instructions).
+ */
+ ZyanBool has_rel_operand;
+} ZydisEncoderInstructionMatch;
+
+/**
+ * Encapsulates information about writable buffer.
+ */
+typedef struct ZydisEncoderBuffer_
+{
+ /**
+ * A pointer to actual data buffer.
+ */
+ ZyanU8 *buffer;
+ /**
+ * Size of this buffer.
+ */
+ ZyanUSize size;
+ /**
+ * Current write offset.
+ */
+ ZyanUSize offset;
+} ZydisEncoderBuffer;
+
+/**
+ * Low-level instruction representation. Once filled this structure contains all information
+ * required for final instruction emission phase.
+ */
+typedef struct ZydisEncoderInstruction_
+{
+ /**
+ * Encodable attributes for this instruction.
+ */
+ ZydisInstructionAttributes attributes;
+ /**
+ * The instruction encoding.
+ */
+ ZydisInstructionEncoding encoding;
+ /**
+ * The opcode map.
+ */
+ ZydisOpcodeMap opcode_map;
+ /**
+ * The opcode.
+ */
+ ZyanU8 opcode;
+ /**
+ * The `vvvv` field (`VEX`, `EVEX`, `MVEX`, `XOP`).
+ */
+ ZyanU8 vvvv;
+ /**
+ * The `sss` field (`MVEX`).
+ */
+ ZyanU8 sss;
+ /**
+ * The mask register ID.
+ */
+ ZyanU8 mask;
+ /**
+ * The vector length.
+ */
+ ZyanU8 vector_length;
+ /**
+ * The `mod` component of Mod/RM byte.
+ */
+ ZyanU8 mod;
+ /**
+ * The `reg` component of Mod/RM byte.
+ */
+ ZyanU8 reg;
+ /**
+ * The `rm` component of Mod/RM byte.
+ */
+ ZyanU8 rm;
+ /**
+ * The scale component of SIB byte.
+ */
+ ZyanU8 scale;
+ /**
+ * The index component of SIB byte.
+ */
+ ZyanU8 index;
+ /**
+ * The base component of SIB byte.
+ */
+ ZyanU8 base;
+ /**
+ * The `REX.W` bit.
+ */
+ ZyanBool rex_w;
+ /**
+ * True if using zeroing mask (`EVEX`).
+ */
+ ZyanBool zeroing;
+ /**
+ * True if using eviction hint (`MVEX`).
+ */
+ ZyanBool eviction_hint;
+ /**
+ * Size of displacement value.
+ */
+ ZyanU8 disp_size;
+ /**
+ * Size of immediate value.
+ */
+ ZyanU8 imm_size;
+ /**
+ * The displacement value.
+ */
+ ZyanU64 disp;
+ /**
+ * The immediate value.
+ */
+ ZyanU64 imm;
+} ZydisEncoderInstruction;
+
+/* ============================================================================================== */
+/* Internal functions */
+/* ============================================================================================== */
+
+/**
+ * Converts `ZydisInstructionEncoding` to `ZydisEncodableEncoding`.
+ *
+ * @param encoding `ZydisInstructionEncoding` value to convert.
+ *
+ * @return Equivalent `ZydisEncodableEncoding` value.
+ */
+static ZydisEncodableEncoding ZydisGetEncodableEncoding(ZydisInstructionEncoding encoding)
+{
+ static const ZydisEncodableEncoding encoding_lookup[6] =
+ {
+ ZYDIS_ENCODABLE_ENCODING_LEGACY,
+ ZYDIS_ENCODABLE_ENCODING_3DNOW,
+ ZYDIS_ENCODABLE_ENCODING_XOP,
+ ZYDIS_ENCODABLE_ENCODING_VEX,
+ ZYDIS_ENCODABLE_ENCODING_EVEX,
+ ZYDIS_ENCODABLE_ENCODING_MVEX,
+ };
+ ZYAN_ASSERT((ZyanUSize)encoding <= ZYDIS_INSTRUCTION_ENCODING_MAX_VALUE);
+ return encoding_lookup[encoding];
+}
+
+/**
+ * Converts `ZydisMachineMode` to default stack width value expressed in bits.
+ *
+ * @param machine_mode `ZydisMachineMode` value to convert.
+ *
+ * @return Stack width for requested machine mode.
+ */
+static ZyanU8 ZydisGetMachineModeWidth(ZydisMachineMode machine_mode)
+{
+ switch (machine_mode)
+ {
+ case ZYDIS_MACHINE_MODE_REAL_16:
+ case ZYDIS_MACHINE_MODE_LEGACY_16:
+ case ZYDIS_MACHINE_MODE_LONG_COMPAT_16:
+ return 16;
+ case ZYDIS_MACHINE_MODE_LEGACY_32:
+ case ZYDIS_MACHINE_MODE_LONG_COMPAT_32:
+ return 32;
+ case ZYDIS_MACHINE_MODE_LONG_64:
+ return 64;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+}
+
+/**
+ * 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 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:
+ return ZYAN_TRUE;
+ 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:
+ 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);
+ if (match->request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64)
+ {
+ if (min_size < 32)
+ {
+ min_size = 32;
+ }
+ if (min_size == 32 || min_size == 64)
+ {
+ 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;
+ }
+ ZyanI64 displacement = user_op->mem.displacement;
+ ZyanU8 disp_size = 0;
+ if (displacement)
+ {
+ disp_size = ZydisGetSignedImmSize(displacement);
+ if (disp_size > 32)
+ {
+ return ZYAN_FALSE;
+ }
+ if (ZydisGetMachineModeWidth(match->request->machine_mode) == 16)
+ {
+ if ((ZyanI16)displacement == 0)
+ {
+ disp_size = 0;
+ }
+ else
+ {
+ disp_size = ZydisGetSignedImmSize((ZyanI16)displacement);
+ }
+ }
+
+ match->cd8_scale = ZydisGetCompDispScale(match);
+ if (match->cd8_scale)
+ {
+ const ZyanI64 mask = (1 << match->cd8_scale) - 1;
+ if (!(displacement & mask))
+ {
+ disp_size = ZydisGetSignedImmSize(displacement >> match->cd8_scale);
+ }
+ 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
+ {
+ 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))
+ {
+ min_disp_size = 32;
+ }
+ if (ZydisGetUnsignedImmSize(displacement) == 16)
+ {
+ disp_size = 16;
+ }
+ if (disp_size < min_disp_size)
+ {
+ disp_size = min_disp_size;
+ }
+ if (match->request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64)
+ {
+ candidate_easz = match->easz == 32 ? 32 : 64;
+ }
+ else
+ {
+ 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;
+ }
+ 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 (only ones
+ // to use `moffs` operands). Size of `moffs` is tied to address size attribute, so its
+ // signedness doesn't matter. However if displacement can be represented as a signed
+ // integer of smaller size we reject `moffs` variant because it's guaranteed that better
+ // alternative exists (in terms of size).
+ ZyanU8 alternative_size = ZydisGetSignedImmSize(user_op->mem.displacement);
+ const ZyanU8 min_disp_size =
+ (match->request->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) ? 32 : 16;
+ if (alternative_size < min_disp_size)
+ {
+ alternative_size = min_disp_size;
+ }
+ if (alternative_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(0xF0, buffer));
+ }
+ if (!compressed_prefixes)
+ {
+ if (instruction->attributes & (ZYDIS_ATTRIB_HAS_REPNE |
+ ZYDIS_ATTRIB_HAS_BND |
+ ZYDIS_ATTRIB_HAS_XACQUIRE))
+ {
+ ZYAN_CHECK(ZydisEmitByte(0xF2, buffer));
+ }
+ if (instruction->attributes & (ZYDIS_ATTRIB_HAS_REP |
+ ZYDIS_ATTRIB_HAS_REPE |
+ ZYDIS_ATTRIB_HAS_XRELEASE))
+ {
+ ZYAN_CHECK(ZydisEmitByte(0xF3, buffer));
+ }
+ }
+
+ // Group 2
+ if (instruction->attributes & (ZYDIS_ATTRIB_HAS_SEGMENT_CS |
+ ZYDIS_ATTRIB_HAS_BRANCH_NOT_TAKEN))
+ {
+ ZYAN_CHECK(ZydisEmitByte(0x2E, buffer));
+ }
+ if (instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT_SS)
+ {
+ ZYAN_CHECK(ZydisEmitByte(0x36, buffer));
+ }
+ if (instruction->attributes & (ZYDIS_ATTRIB_HAS_SEGMENT_DS |
+ ZYDIS_ATTRIB_HAS_BRANCH_TAKEN))
+ {
+ ZYAN_CHECK(ZydisEmitByte(0x3E, buffer));
+ }
+ if (instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT_ES)
+ {
+ ZYAN_CHECK(ZydisEmitByte(0x26, buffer));
+ }
+ if (instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT_FS)
+ {
+ ZYAN_CHECK(ZydisEmitByte(0x64, buffer));
+ }
+ if (instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT_GS)
+ {
+ ZYAN_CHECK(ZydisEmitByte(0x65, buffer));
+ }
+ if (instruction->attributes & ZYDIS_ATTRIB_HAS_NOTRACK)
+ {
+ ZYAN_CHECK(ZydisEmitByte(0x3E, buffer));
+ }
+
+ // Group 3
+ if (!compressed_prefixes)
+ {
+ if (instruction->attributes & ZYDIS_ATTRIB_HAS_OPERANDSIZE)
+ {
+ ZYAN_CHECK(ZydisEmitByte(0x66, buffer));
+ }
+ }
+
+ // Group 4
+ if (instruction->attributes & ZYDIS_ATTRIB_HAS_ADDRESSSIZE)
+ {
+ ZYAN_CHECK(ZydisEmitByte(0x67, 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 & 0x08)
+ {
+ rex |= 1;
+ }
+ if (instruction->index & 0x08)
+ {
+ rex |= 2;
+ }
+ if (instruction->reg & 0x08)
+ {
+ rex |= 4;
+ }
+ if (high_r && (instruction->reg & 0x10))
+ {
+ *high_r = ZYAN_TRUE;
+ }
+ }
+ else if (instruction->attributes & ZYDIS_ATTRIB_HAS_MODRM)
+ {
+ if (instruction->rm & 0x08)
+ {
+ rex |= 1;
+ }
+ if (instruction->rm & 0x10)
+ {
+ rex |= 2;
+ }
+ if (instruction->reg & 0x08)
+ {
+ rex |= 4;
+ }
+ if (high_r && (instruction->reg & 0x10))
+ {
+ *high_r = ZYAN_TRUE;
+ }
+ }
+ else
+ {
+ if (instruction->rm & 0x08)
+ {
+ 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(0x40 | 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) & 0x07) << 5) | mmmmm;
+ const ZyanU8 b2 = ((rex & 0x08) << 4) | ((vvvv & 0xF) << 3) | (instruction->vector_length << 2) | pp;
+ ZYAN_CHECK(ZydisEmitByte(0x8F, 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 & 0x0B))
+ {
+ const ZyanU8 b1 = (((~rex) & 0x07) << 5) | mmmmm;
+ const ZyanU8 b2 = ((rex & 0x08) << 4) |
+ ((vvvv & 0xF) << 3) |
+ (instruction->vector_length << 2) |
+ pp;
+ ZYAN_CHECK(ZydisEmitByte(0xC4, buffer));
+ ZYAN_CHECK(ZydisEmitByte(b1, buffer));
+ ZYAN_CHECK(ZydisEmitByte(b2, buffer));
+ }
+ else
+ {
+ const ZyanU8 b1 = (((~rex) & 0x04) << 5) |
+ ((vvvv & 0xF) << 3) |
+ (instruction->vector_length << 2) |
+ pp;
+ ZYAN_CHECK(ZydisEmitByte(0xC5, 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 2nd byte of `EVEX` prefix.
+ * @param p1 A pointer to `ZyanU8` variable that will receive 3rd 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) & 0x07) << 5) | mmmmm;
+ if (!high_r)
+ {
+ *p0 |= 0x10;
+ }
+ *p1 = ((rex & 0x08) << 4) | ((*vvvvv & 0x0F) << 3) | 0x04 | 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 & 0x10) >> 1) | instruction->mask;
+ if (instruction->zeroing)
+ {
+ p2 |= 0x80;
+ }
+ if (instruction->attributes & ZYDIS_ATTRIB_HAS_EVEX_B)
+ {
+ p2 |= 0x10;
+ }
+ if (instruction->index & 0x10)
+ {
+ p2 &= 0xF7;
+ }
+
+ ZYAN_CHECK(ZydisEmitByte(0x62, 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 & 0x10) >> 1) | instruction->mask;
+ if (instruction->eviction_hint)
+ {
+ p2 |= 0x80;
+ }
+ if (instruction->index & 0x10)
+ {
+ p2 &= 0xF7;
+ }
+
+ ZYAN_CHECK(ZydisEmitByte(0x62, buffer));
+ ZYAN_CHECK(ZydisEmitByte(p0, buffer));
+ ZYAN_CHECK(ZydisEmitByte(p1 & 0xFB, 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(0x0F, buffer));
+ break;
+ case ZYDIS_OPCODE_MAP_0F38:
+ ZYAN_CHECK(ZydisEmitByte(0x0F, buffer));
+ ZYAN_CHECK(ZydisEmitByte(0x38, buffer));
+ break;
+ case ZYDIS_OPCODE_MAP_0F3A:
+ ZYAN_CHECK(ZydisEmitByte(0x0F, buffer));
+ ZYAN_CHECK(ZydisEmitByte(0x3A, buffer));
+ break;
+ case ZYDIS_OPCODE_MAP_0F0F:
+ ZYAN_CHECK(ZydisEmitByte(0x0F, buffer));
+ ZYAN_CHECK(ZydisEmitByte(0x0F, 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.
+ */
+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 != 0xFF)
+ {
+ instruction->base = reg_base_id;
+ }
+ else
+ {
+ instruction->base = 5;
+ instruction->mod = 0;
+ instruction->disp_size = 32;
+ }
+ if (reg_index_id != 0xFF)
+ {
+ 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 (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;
+ 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);
+ 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->encoding == ZYDIS_OPERAND_ENCODING_DISP16_32_64)
+ {
+ ZydisCalcAbsoluteAddress(instruction, dec_op, 0,
+ (ZyanU64 *)&enc_op->mem.displacement);
+ }
+ else
+ {
+ enc_op->mem.displacement = dec_op->mem.disp.has_displacement ?
+ dec_op->mem.disp.value : 0;
+ }
+ 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. 2B "Recommended Multi-Byte Sequence of NOP Instruction"
+ static const ZyanU8 nops[9][9] =
+ {
+ { 0x90 },
+ { 0x66, 0x90 },
+ { 0x0F, 0x1F, 0x00 },
+ { 0x0F, 0x1F, 0x40, 0x00 },
+ { 0x0F, 0x1F, 0x44, 0x00, 0x00 },
+ { 0x66, 0x0F, 0x1F, 0x44, 0x00, 0x00 },
+ { 0x0F, 0x1F, 0x80, 0x00, 0x00, 0x00, 0x00 },
+ { 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 },
+ { 0x66, 0x0F, 0x1F, 0x84, 0x00, 0x00, 0x00, 0x00, 0x00 },
+ };
+
+ 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;
+}
+
+/* ============================================================================================== */