summaryrefslogtreecommitdiffstats
path: root/js/src/zydis/Zydis/Decoder.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--js/src/zydis/Zydis/Decoder.c5074
1 files changed, 5074 insertions, 0 deletions
diff --git a/js/src/zydis/Zydis/Decoder.c b/js/src/zydis/Zydis/Decoder.c
new file mode 100644
index 0000000000..0c3df7f6a7
--- /dev/null
+++ b/js/src/zydis/Zydis/Decoder.c
@@ -0,0 +1,5074 @@
+/***************************************************************************************************
+
+ Zyan Disassembler Library (Zydis)
+
+ Original Author : Florian Bernd
+
+ * 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 CppClangTidyClangDiagnosticImplicitFallthrough
+// ReSharper disable CppClangTidyClangDiagnosticSwitchEnum
+// ReSharper disable CppClangTidyClangDiagnosticCoveredSwitchDefault
+
+// Temporarily disabled due to a LLVM issue:
+// ReSharper disable CppClangTidyBugproneNarrowingConversions
+
+#include "zydis/Zycore/LibC.h"
+#include "zydis/Zydis/Decoder.h"
+#include "zydis/Zydis/Status.h"
+#include "zydis/Zydis/Internal/DecoderData.h"
+#include "zydis/Zydis/Internal/SharedData.h"
+
+/* ============================================================================================== */
+/* Internal enums and types */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Decoder context */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Defines the `ZydisDecoderState` struct.
+ */
+typedef struct ZydisDecoderState_
+{
+ /**
+ * A pointer to the `ZydisDecoder` instance.
+ */
+ const ZydisDecoder* decoder;
+ /**
+ * A pointer to the `ZydisDecoderContext` struct.
+ */
+ ZydisDecoderContext* context;
+ /**
+ * The input buffer.
+ */
+ const ZyanU8* buffer;
+ /**
+ * The input buffer length.
+ */
+ ZyanUSize buffer_len;
+ /**
+ * Prefix information.
+ */
+ struct
+ {
+ /**
+ * Signals, if the instruction has a `LOCK` prefix (`F0`).
+ *
+ * This prefix originally belongs to group 1, but separating it from the other ones makes
+ * parsing easier for us later.
+ */
+ ZyanBool has_lock;
+ /**
+ * The effective prefix of group 1 (either `F2` or `F3`).
+ */
+ ZyanU8 group1;
+ /**
+ * The effective prefix of group 2 (`2E`, `36`, `3E`, `26`, `64` or `65`).
+ */
+ ZyanU8 group2;
+ /**
+ * The effective segment prefix.
+ */
+ ZyanU8 effective_segment;
+ /**
+ * The prefix that should be treated as the mandatory-prefix, if the
+ * current instruction needs one.
+ *
+ * The last `F3`/`F2` prefix has precedence over previous ones and
+ * `F3`/`F2` in general have precedence over `66`.
+ */
+ ZyanU8 mandatory_candidate;
+ /**
+ * The offset of the effective `LOCK` prefix.
+ */
+ ZyanU8 offset_lock;
+ /**
+ * The offset of the effective prefix in group 1.
+ */
+ ZyanU8 offset_group1;
+ /**
+ * The offset of the effective prefix in group 2.
+ */
+ ZyanU8 offset_group2;
+ /**
+ * The offset of the operand-size override prefix (`66`).
+ *
+ * This is the only prefix in group 3.
+ */
+ ZyanU8 offset_osz_override;
+ /**
+ * The offset of the address-size override prefix (`67`).
+ *
+ * This is the only prefix in group 4.
+ */
+ ZyanU8 offset_asz_override;
+ /**
+ * The offset of the effective segment prefix.
+ */
+ ZyanU8 offset_segment;
+ /**
+ * The offset of the mandatory-candidate prefix.
+ */
+ ZyanU8 offset_mandatory;
+ /**
+ * The offset of a possible `CET` `no-lock` prefix.
+ */
+ ZyanI8 offset_notrack;
+ } prefixes;
+} ZydisDecoderState;
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Register encoding */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Defines the `ZydisRegisterEncoding` enum.
+ */
+typedef enum ZydisRegisterEncoding_
+{
+ ZYDIS_REG_ENCODING_INVALID,
+ /**
+ * The register-id is encoded as part of the opcode (bits [3..0]).
+ *
+ * Possible extension by:
+ * - `REX.B`
+ */
+ ZYDIS_REG_ENCODING_OPCODE,
+ /**
+ * The register-id is encoded in `modrm.reg`.
+ *
+ * Possible extension by:
+ * - `.R`
+ * - `.R'` (vector only, EVEX/MVEX)
+ */
+ ZYDIS_REG_ENCODING_REG,
+ /**
+ * The register-id is encoded in `.vvvv`.
+ *
+ * Possible extension by:
+ * - `.v'` (vector only, EVEX/MVEX).
+ */
+ ZYDIS_REG_ENCODING_NDSNDD,
+ /**
+ * The register-id is encoded in `modrm.rm`.
+ *
+ * Possible extension by:
+ * - `.B`
+ * - `.X` (vector only, EVEX/MVEX)`
+ */
+ ZYDIS_REG_ENCODING_RM,
+ /**
+ * The register-id is encoded in `modrm.rm` or `sib.base` (if `SIB` is present).
+ *
+ * Possible extension by:
+ * - `.B`
+ */
+ ZYDIS_REG_ENCODING_BASE,
+ /**
+ * The register-id is encoded in `sib.index`.
+ *
+ * Possible extension by:
+ * - `.X`
+ */
+ ZYDIS_REG_ENCODING_INDEX,
+ /**
+ * The register-id is encoded in `sib.index`.
+ *
+ * Possible extension by:
+ * - `.X`
+ * - `.V'` (vector only, EVEX/MVEX)
+ */
+ ZYDIS_REG_ENCODING_VIDX,
+ /**
+ * The register-id is encoded in an additional 8-bit immediate value.
+ *
+ * Bits [7:4] in 64-bit mode with possible extension by bit [3] (vector only), bits [7:5] for
+ * all other modes.
+ */
+ ZYDIS_REG_ENCODING_IS4,
+ /**
+ * The register-id is encoded in `EVEX.aaa/MVEX.kkk`.
+ */
+ ZYDIS_REG_ENCODING_MASK,
+
+ /**
+ * Maximum value of this enum.
+ */
+ ZYDIS_REG_ENCODING_MAX_VALUE = ZYDIS_REG_ENCODING_MASK,
+ /**
+ * The minimum number of bits required to represent all values of this enum.
+ */
+ ZYDIS_REG_ENCODING_REQUIRED_BITS = ZYAN_BITS_TO_REPRESENT(ZYDIS_REG_ENCODING_MAX_VALUE)
+} ZydisRegisterEncoding;
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+/* Internal functions */
+/* ============================================================================================== */
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Input helper functions */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Reads one byte from the current read-position of the input data-source.
+ *
+ * @param state A pointer to the `ZydisDecoderState` struct.
+ * @param instruction A pointer to the `ZydisDecodedInstruction` struct.
+ * @param value A pointer to the memory that receives the byte from the input data-source.
+ *
+ * @return A zyan status code.
+ *
+ * This function may fail, if the `ZYDIS_MAX_INSTRUCTION_LENGTH` limit got exceeded, or no more
+ * data is available.
+ */
+static ZyanStatus ZydisInputPeek(ZydisDecoderState* state,
+ ZydisDecodedInstruction* instruction, ZyanU8* value)
+{
+ ZYAN_ASSERT(state);
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(value);
+
+ if (instruction->length >= ZYDIS_MAX_INSTRUCTION_LENGTH)
+ {
+ return ZYDIS_STATUS_INSTRUCTION_TOO_LONG;
+ }
+
+ if (state->buffer_len > 0)
+ {
+ *value = state->buffer[0];
+ return ZYAN_STATUS_SUCCESS;
+ }
+
+ return ZYDIS_STATUS_NO_MORE_DATA;
+}
+
+/**
+ * Increases the read-position of the input data-source by one byte.
+ *
+ * @param state A pointer to the `ZydisDecoderState` instance
+ * @param instruction A pointer to the `ZydisDecodedInstruction` struct.
+ *
+ * This function is supposed to get called ONLY after a successful call of `ZydisInputPeek`.
+ *
+ * This function increases the `length` field of the `ZydisDecodedInstruction` struct by one.
+ */
+static void ZydisInputSkip(ZydisDecoderState* state, ZydisDecodedInstruction* instruction)
+{
+ ZYAN_ASSERT(state);
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(instruction->length < ZYDIS_MAX_INSTRUCTION_LENGTH);
+
+ ++instruction->length;
+ ++state->buffer;
+ --state->buffer_len;
+}
+
+/**
+ * Reads one byte from the current read-position of the input data-source and increases
+ * the read-position by one byte afterwards.
+ *
+ * @param state A pointer to the `ZydisDecoderState` struct.
+ * @param instruction A pointer to the `ZydisDecodedInstruction` struct.
+ * @param value A pointer to the memory that receives the byte from the input data-source.
+ *
+ * @return A zyan status code.
+ *
+ * This function acts like a subsequent call of `ZydisInputPeek` and `ZydisInputSkip`.
+ */
+static ZyanStatus ZydisInputNext(ZydisDecoderState* state,
+ ZydisDecodedInstruction* instruction, ZyanU8* value)
+{
+ ZYAN_ASSERT(state);
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(value);
+
+ if (instruction->length >= ZYDIS_MAX_INSTRUCTION_LENGTH)
+ {
+ return ZYDIS_STATUS_INSTRUCTION_TOO_LONG;
+ }
+
+ if (state->buffer_len > 0)
+ {
+ *value = state->buffer++[0];
+ ++instruction->length;
+ --state->buffer_len;
+ return ZYAN_STATUS_SUCCESS;
+ }
+
+ return ZYDIS_STATUS_NO_MORE_DATA;
+}
+
+/**
+ * Reads a variable amount of bytes from the current read-position of the input
+ * data-source and increases the read-position by specified amount of bytes afterwards.
+ *
+ * @param state A pointer to the `ZydisDecoderState` struct.
+ * @param instruction A pointer to the `ZydisDecodedInstruction` struct.
+ * @param value A pointer to the memory that receives the byte from the input
+ * data-source.
+ * @param number_of_bytes The number of bytes to read from the input data-source.
+ *
+ * @return A zyan status code.
+ *
+ * This function acts like a subsequent call of `ZydisInputPeek` and `ZydisInputSkip`.
+ */
+static ZyanStatus ZydisInputNextBytes(ZydisDecoderState* state,
+ ZydisDecodedInstruction* instruction, ZyanU8* value, ZyanU8 number_of_bytes)
+{
+ ZYAN_ASSERT(state);
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(value);
+
+ if (instruction->length + number_of_bytes > ZYDIS_MAX_INSTRUCTION_LENGTH)
+ {
+ return ZYDIS_STATUS_INSTRUCTION_TOO_LONG;
+ }
+
+ if (state->buffer_len >= number_of_bytes)
+ {
+ instruction->length += number_of_bytes;
+
+ ZYAN_MEMCPY(value, state->buffer, number_of_bytes);
+ state->buffer += number_of_bytes;
+ state->buffer_len -= number_of_bytes;
+
+ return ZYAN_STATUS_SUCCESS;
+ }
+
+ return ZYDIS_STATUS_NO_MORE_DATA;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Decode functions */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Decodes the `REX`-prefix.
+ *
+ * @param context A pointer to the `ZydisDecoderContext` struct.
+ * @param instruction A pointer to the `ZydisDecodedInstruction` struct.
+ * @param data The `REX` byte.
+ */
+static void ZydisDecodeREX(ZydisDecoderContext* context, ZydisDecodedInstruction* instruction,
+ ZyanU8 data)
+{
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT((data & 0xF0) == 0x40);
+
+ instruction->attributes |= ZYDIS_ATTRIB_HAS_REX;
+ instruction->raw.rex.W = (data >> 3) & 0x01;
+ instruction->raw.rex.R = (data >> 2) & 0x01;
+ instruction->raw.rex.X = (data >> 1) & 0x01;
+ instruction->raw.rex.B = (data >> 0) & 0x01;
+
+ // Update internal fields
+ context->vector_unified.W = instruction->raw.rex.W;
+ context->vector_unified.R = instruction->raw.rex.R;
+ context->vector_unified.X = instruction->raw.rex.X;
+ context->vector_unified.B = instruction->raw.rex.B;
+}
+
+/**
+ * Decodes the `XOP`-prefix.
+ *
+ * @param context A pointer to the `ZydisDecoderContext` struct.
+ * @param instruction A pointer to the `ZydisDecodedInstruction` struct.
+ * @param data The `XOP` bytes.
+ *
+ * @return A zyan status code.
+ */
+static ZyanStatus ZydisDecodeXOP(ZydisDecoderContext* context,
+ ZydisDecodedInstruction* instruction, const ZyanU8 data[3])
+{
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(data[0] == 0x8F);
+ ZYAN_ASSERT(((data[1] >> 0) & 0x1F) >= 8);
+ ZYAN_ASSERT(instruction->raw.xop.offset == instruction->length - 3);
+
+ if (instruction->machine_mode == ZYDIS_MACHINE_MODE_REAL_16)
+ {
+ // XOP is invalid in 16-bit real mode
+ return ZYDIS_STATUS_DECODING_ERROR;
+ }
+
+ instruction->attributes |= ZYDIS_ATTRIB_HAS_XOP;
+ instruction->raw.xop.R = (data[1] >> 7) & 0x01;
+ instruction->raw.xop.X = (data[1] >> 6) & 0x01;
+ instruction->raw.xop.B = (data[1] >> 5) & 0x01;
+ instruction->raw.xop.m_mmmm = (data[1] >> 0) & 0x1F;
+
+ if ((instruction->raw.xop.m_mmmm < 0x08) || (instruction->raw.xop.m_mmmm > 0x0A))
+ {
+ // Invalid according to the AMD documentation
+ return ZYDIS_STATUS_INVALID_MAP;
+ }
+
+ instruction->raw.xop.W = (data[2] >> 7) & 0x01;
+ instruction->raw.xop.vvvv = (data[2] >> 3) & 0x0F;
+ instruction->raw.xop.L = (data[2] >> 2) & 0x01;
+ instruction->raw.xop.pp = (data[2] >> 0) & 0x03;
+
+ // Update internal fields
+ context->vector_unified.W = instruction->raw.xop.W;
+ context->vector_unified.R = 0x01 & ~instruction->raw.xop.R;
+ context->vector_unified.X = 0x01 & ~instruction->raw.xop.X;
+ context->vector_unified.B = 0x01 & ~instruction->raw.xop.B;
+ context->vector_unified.L = instruction->raw.xop.L;
+ context->vector_unified.LL = instruction->raw.xop.L;
+ context->vector_unified.vvvv = (0x0F & ~instruction->raw.xop.vvvv);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/**
+ * Decodes the `VEX`-prefix.
+ *
+ * @param context A pointer to the `ZydisDecoderContext` struct.
+ * @param instruction A pointer to the `ZydisDecodedInstruction` struct.
+ * @param data The `VEX` bytes.
+ *
+ * @return A zyan status code.
+ */
+static ZyanStatus ZydisDecodeVEX(ZydisDecoderContext* context,
+ ZydisDecodedInstruction* instruction, const ZyanU8 data[3])
+{
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT((data[0] == 0xC4) || (data[0] == 0xC5));
+
+ if (instruction->machine_mode == ZYDIS_MACHINE_MODE_REAL_16)
+ {
+ // VEX is invalid in 16-bit real mode
+ return ZYDIS_STATUS_DECODING_ERROR;
+ }
+
+ instruction->attributes |= ZYDIS_ATTRIB_HAS_VEX;
+ switch (data[0])
+ {
+ case 0xC4:
+ ZYAN_ASSERT(instruction->raw.vex.offset == instruction->length - 3);
+ instruction->raw.vex.size = 3;
+ instruction->raw.vex.R = (data[1] >> 7) & 0x01;
+ instruction->raw.vex.X = (data[1] >> 6) & 0x01;
+ instruction->raw.vex.B = (data[1] >> 5) & 0x01;
+ instruction->raw.vex.m_mmmm = (data[1] >> 0) & 0x1F;
+ instruction->raw.vex.W = (data[2] >> 7) & 0x01;
+ instruction->raw.vex.vvvv = (data[2] >> 3) & 0x0F;
+ instruction->raw.vex.L = (data[2] >> 2) & 0x01;
+ instruction->raw.vex.pp = (data[2] >> 0) & 0x03;
+ break;
+ case 0xC5:
+ ZYAN_ASSERT(instruction->raw.vex.offset == instruction->length - 2);
+ instruction->raw.vex.size = 2;
+ instruction->raw.vex.R = (data[1] >> 7) & 0x01;
+ instruction->raw.vex.X = 1;
+ instruction->raw.vex.B = 1;
+ instruction->raw.vex.m_mmmm = 1;
+ instruction->raw.vex.W = 0;
+ instruction->raw.vex.vvvv = (data[1] >> 3) & 0x0F;
+ instruction->raw.vex.L = (data[1] >> 2) & 0x01;
+ instruction->raw.vex.pp = (data[1] >> 0) & 0x03;
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+
+ // Map 0 is only valid for some KNC instructions
+#ifdef ZYDIS_DISABLE_KNC
+ if ((instruction->raw.vex.m_mmmm == 0) || (instruction->raw.vex.m_mmmm > 0x03))
+#else
+ if (instruction->raw.vex.m_mmmm > 0x03)
+#endif
+ {
+ // Invalid according to the intel documentation
+ return ZYDIS_STATUS_INVALID_MAP;
+ }
+
+ // Update internal fields
+ context->vector_unified.W = instruction->raw.vex.W;
+ context->vector_unified.R = 0x01 & ~instruction->raw.vex.R;
+ context->vector_unified.X = 0x01 & ~instruction->raw.vex.X;
+ context->vector_unified.B = 0x01 & ~instruction->raw.vex.B;
+ context->vector_unified.L = instruction->raw.vex.L;
+ context->vector_unified.LL = instruction->raw.vex.L;
+ context->vector_unified.vvvv = (0x0F & ~instruction->raw.vex.vvvv);
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+#ifndef ZYDIS_DISABLE_AVX512
+/**
+ * Decodes the `EVEX`-prefix.
+ *
+ * @param context A pointer to the `ZydisDecoderContext` struct.
+ * @param instruction A pointer to the `ZydisDecodedInstruction` struct.
+ * @param data The `EVEX` bytes.
+ *
+ * @return A zyan status code.
+ */
+static ZyanStatus ZydisDecodeEVEX(ZydisDecoderContext* context,
+ ZydisDecodedInstruction* instruction, const ZyanU8 data[4])
+{
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(data[0] == 0x62);
+ ZYAN_ASSERT(instruction->raw.evex.offset == instruction->length - 4);
+
+ if (instruction->machine_mode == ZYDIS_MACHINE_MODE_REAL_16)
+ {
+ // EVEX is invalid in 16-bit real mode
+ return ZYDIS_STATUS_DECODING_ERROR;
+ }
+
+ instruction->attributes |= ZYDIS_ATTRIB_HAS_EVEX;
+ instruction->raw.evex.R = (data[1] >> 7) & 0x01;
+ instruction->raw.evex.X = (data[1] >> 6) & 0x01;
+ instruction->raw.evex.B = (data[1] >> 5) & 0x01;
+ instruction->raw.evex.R2 = (data[1] >> 4) & 0x01;
+
+ if (data[1] & 0x08)
+ {
+ // Invalid according to the intel documentation
+ return ZYDIS_STATUS_MALFORMED_EVEX;
+ }
+
+ instruction->raw.evex.mmm = (data[1] >> 0) & 0x07;
+
+ if ((instruction->raw.evex.mmm == 0x00) ||
+ (instruction->raw.evex.mmm == 0x04) ||
+ (instruction->raw.evex.mmm == 0x07))
+ {
+ // Invalid according to the intel documentation
+ return ZYDIS_STATUS_INVALID_MAP;
+ }
+
+ instruction->raw.evex.W = (data[2] >> 7) & 0x01;
+ instruction->raw.evex.vvvv = (data[2] >> 3) & 0x0F;
+
+ ZYAN_ASSERT(((data[2] >> 2) & 0x01) == 0x01);
+
+ instruction->raw.evex.pp = (data[2] >> 0) & 0x03;
+ instruction->raw.evex.z = (data[3] >> 7) & 0x01;
+ instruction->raw.evex.L2 = (data[3] >> 6) & 0x01;
+ instruction->raw.evex.L = (data[3] >> 5) & 0x01;
+ instruction->raw.evex.b = (data[3] >> 4) & 0x01;
+ instruction->raw.evex.V2 = (data[3] >> 3) & 0x01;
+
+ if (!instruction->raw.evex.V2 &&
+ (instruction->machine_mode != ZYDIS_MACHINE_MODE_LONG_64))
+ {
+ return ZYDIS_STATUS_MALFORMED_EVEX;
+ }
+
+ instruction->raw.evex.aaa = (data[3] >> 0) & 0x07;
+
+ if (instruction->raw.evex.z && !instruction->raw.evex.aaa)
+ {
+ return ZYDIS_STATUS_INVALID_MASK; // TODO: Dedicated status code
+ }
+
+ // Update internal fields
+ context->vector_unified.W = instruction->raw.evex.W;
+ context->vector_unified.R = 0x01 & ~instruction->raw.evex.R;
+ context->vector_unified.X = 0x01 & ~instruction->raw.evex.X;
+ context->vector_unified.B = 0x01 & ~instruction->raw.evex.B;
+ context->vector_unified.LL = (data[3] >> 5) & 0x03;
+ context->vector_unified.R2 = 0x01 & ~instruction->raw.evex.R2;
+ context->vector_unified.V2 = 0x01 & ~instruction->raw.evex.V2;
+ context->vector_unified.vvvv = 0x0F & ~instruction->raw.evex.vvvv;
+ context->vector_unified.mask = instruction->raw.evex.aaa;
+
+ if (!instruction->raw.evex.V2 && (instruction->machine_mode != ZYDIS_MACHINE_MODE_LONG_64))
+ {
+ return ZYDIS_STATUS_MALFORMED_EVEX;
+ }
+ if (!instruction->raw.evex.b && (context->vector_unified.LL == 3))
+ {
+ // LL = 3 is only valid for instructions with embedded rounding control
+ return ZYDIS_STATUS_MALFORMED_EVEX;
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+#endif
+
+#ifndef ZYDIS_DISABLE_KNC
+/**
+ * Decodes the `MVEX`-prefix.
+ *
+ * @param context A pointer to the `ZydisDecoderContext` struct.
+ * @param instruction A pointer to the `ZydisDecodedInstruction` struct.
+ * @param data The `MVEX` bytes.
+ *
+ * @return A zyan status code.
+ */
+static ZyanStatus ZydisDecodeMVEX(ZydisDecoderContext* context,
+ ZydisDecodedInstruction* instruction, const ZyanU8 data[4])
+{
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(data[0] == 0x62);
+ ZYAN_ASSERT(instruction->raw.mvex.offset == instruction->length - 4);
+
+ if (instruction->machine_mode != ZYDIS_MACHINE_MODE_LONG_64)
+ {
+ // MVEX is only valid in 64-bit mode
+ return ZYDIS_STATUS_DECODING_ERROR;
+ }
+
+ instruction->attributes |= ZYDIS_ATTRIB_HAS_MVEX;
+ instruction->raw.mvex.R = (data[1] >> 7) & 0x01;
+ instruction->raw.mvex.X = (data[1] >> 6) & 0x01;
+ instruction->raw.mvex.B = (data[1] >> 5) & 0x01;
+ instruction->raw.mvex.R2 = (data[1] >> 4) & 0x01;
+ instruction->raw.mvex.mmmm = (data[1] >> 0) & 0x0F;
+
+ if (instruction->raw.mvex.mmmm > 0x03)
+ {
+ // Invalid according to the intel documentation
+ return ZYDIS_STATUS_INVALID_MAP;
+ }
+
+ instruction->raw.mvex.W = (data[2] >> 7) & 0x01;
+ instruction->raw.mvex.vvvv = (data[2] >> 3) & 0x0F;
+
+ ZYAN_ASSERT(((data[2] >> 2) & 0x01) == 0x00);
+
+ instruction->raw.mvex.pp = (data[2] >> 0) & 0x03;
+ instruction->raw.mvex.E = (data[3] >> 7) & 0x01;
+ instruction->raw.mvex.SSS = (data[3] >> 4) & 0x07;
+ instruction->raw.mvex.V2 = (data[3] >> 3) & 0x01;
+ instruction->raw.mvex.kkk = (data[3] >> 0) & 0x07;
+
+ // Update internal fields
+ context->vector_unified.W = instruction->raw.mvex.W;
+ context->vector_unified.R = 0x01 & ~instruction->raw.mvex.R;
+ context->vector_unified.X = 0x01 & ~instruction->raw.mvex.X;
+ context->vector_unified.B = 0x01 & ~instruction->raw.mvex.B;
+ context->vector_unified.R2 = 0x01 & ~instruction->raw.mvex.R2;
+ context->vector_unified.V2 = 0x01 & ~instruction->raw.mvex.V2;
+ context->vector_unified.LL = 2;
+ context->vector_unified.vvvv = 0x0F & ~instruction->raw.mvex.vvvv;
+ context->vector_unified.mask = instruction->raw.mvex.kkk;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+#endif
+
+/**
+ * Decodes the `ModRM`-byte.
+ *
+ * @param instruction A pointer to the `ZydisDecodedInstruction` struct.
+ * @param data The `ModRM` byte.
+ */
+static void ZydisDecodeModRM(ZydisDecodedInstruction* instruction, ZyanU8 data)
+{
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(!(instruction->attributes & ZYDIS_ATTRIB_HAS_MODRM));
+ ZYAN_ASSERT(instruction->raw.modrm.offset == instruction->length - 1);
+
+ instruction->attributes |= ZYDIS_ATTRIB_HAS_MODRM;
+ instruction->raw.modrm.mod = (data >> 6) & 0x03;
+ instruction->raw.modrm.reg = (data >> 3) & 0x07;
+ instruction->raw.modrm.rm = (data >> 0) & 0x07;
+}
+
+/**
+ * Decodes the `SIB`-byte.
+ *
+ * @param instruction A pointer to the `ZydisDecodedInstruction` struct
+ * @param data The `SIB` byte.
+ */
+static void ZydisDecodeSIB(ZydisDecodedInstruction* instruction, ZyanU8 data)
+{
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_MODRM);
+ ZYAN_ASSERT(instruction->raw.modrm.rm == 4);
+ ZYAN_ASSERT(!(instruction->attributes & ZYDIS_ATTRIB_HAS_SIB));
+ ZYAN_ASSERT(instruction->raw.sib.offset == instruction->length - 1);
+
+ instruction->attributes |= ZYDIS_ATTRIB_HAS_SIB;
+ instruction->raw.sib.scale = (data >> 6) & 0x03;
+ instruction->raw.sib.index = (data >> 3) & 0x07;
+ instruction->raw.sib.base = (data >> 0) & 0x07;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Reads a displacement value.
+ *
+ * @param state A pointer to the `ZydisDecoderState` struct.
+ * @param instruction A pointer to the `ZydisDecodedInstruction` struct.
+ * @param size The physical size of the displacement value.
+ *
+ * @return A zyan status code.
+ */
+static ZyanStatus ZydisReadDisplacement(ZydisDecoderState* state,
+ ZydisDecodedInstruction* instruction, ZyanU8 size)
+{
+ ZYAN_ASSERT(state);
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(instruction->raw.disp.size == 0);
+
+ instruction->raw.disp.size = size;
+ instruction->raw.disp.offset = instruction->length;
+
+ switch (size)
+ {
+ case 8:
+ {
+ ZyanU8 value;
+ ZYAN_CHECK(ZydisInputNext(state, instruction, &value));
+ instruction->raw.disp.value = *(ZyanI8*)&value;
+ break;
+ }
+ case 16:
+ {
+ ZyanU16 value;
+ ZYAN_CHECK(ZydisInputNextBytes(state, instruction, (ZyanU8*)&value, 2));
+ instruction->raw.disp.value = *(ZyanI16*)&value;
+ break;
+ }
+ case 32:
+ {
+ ZyanU32 value;
+ ZYAN_CHECK(ZydisInputNextBytes(state, instruction, (ZyanU8*)&value, 4));
+ instruction->raw.disp.value = *(ZyanI32*)&value;
+ break;
+ }
+ case 64:
+ {
+ ZyanU64 value;
+ ZYAN_CHECK(ZydisInputNextBytes(state, instruction, (ZyanU8*)&value, 8));
+ instruction->raw.disp.value = *(ZyanI64*)&value;
+ break;
+ }
+ default:
+ ZYAN_UNREACHABLE;
+ }
+
+ // TODO: Fix endianess on big-endian systems
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/**
+ * Reads an immediate value.
+ *
+ * @param state A pointer to the `ZydisDecoderState` struct.
+ * @param instruction A pointer to the `ZydisDecodedInstruction` struct.
+ * @param id The immediate id (either `0` or `1`).
+ * @param size The physical size of the immediate value.
+ * @param is_signed Signals, if the immediate value is signed.
+ * @param is_relative Signals, if the immediate value is a relative offset.
+ *
+ * @return A zyan status code.
+ */
+static ZyanStatus ZydisReadImmediate(ZydisDecoderState* state,
+ ZydisDecodedInstruction* instruction, ZyanU8 id, ZyanU8 size, ZyanBool is_signed,
+ ZyanBool is_relative)
+{
+ ZYAN_ASSERT(state);
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT((id == 0) || (id == 1));
+ ZYAN_ASSERT(is_signed || !is_relative);
+ ZYAN_ASSERT(instruction->raw.imm[id].size == 0);
+
+ instruction->raw.imm[id].size = size;
+ instruction->raw.imm[id].offset = instruction->length;
+ instruction->raw.imm[id].is_signed = is_signed;
+ instruction->raw.imm[id].is_relative = is_relative;
+ switch (size)
+ {
+ case 8:
+ {
+ ZyanU8 value;
+ ZYAN_CHECK(ZydisInputNext(state, instruction, &value));
+ if (is_signed)
+ {
+ instruction->raw.imm[id].value.s = (ZyanI8)value;
+ } else
+ {
+ instruction->raw.imm[id].value.u = value;
+ }
+ break;
+ }
+ case 16:
+ {
+ ZyanU16 value;
+ ZYAN_CHECK(ZydisInputNextBytes(state, instruction, (ZyanU8*)&value, 2));
+ if (is_signed)
+ {
+ instruction->raw.imm[id].value.s = (ZyanI16)value;
+ } else
+ {
+ instruction->raw.imm[id].value.u = value;
+ }
+ break;
+ }
+ case 32:
+ {
+ ZyanU32 value;
+ ZYAN_CHECK(ZydisInputNextBytes(state, instruction, (ZyanU8*)&value, 4));
+ if (is_signed)
+ {
+ instruction->raw.imm[id].value.s = (ZyanI32)value;
+ } else
+ {
+ instruction->raw.imm[id].value.u = value;
+ }
+ break;
+ }
+ case 64:
+ {
+ ZyanU64 value;
+ ZYAN_CHECK(ZydisInputNextBytes(state, instruction, (ZyanU8*)&value, 8));
+ if (is_signed)
+ {
+ instruction->raw.imm[id].value.s = (ZyanI64)value;
+ } else
+ {
+ instruction->raw.imm[id].value.u = value;
+ }
+ break;
+ }
+ default:
+ ZYAN_UNREACHABLE;
+ }
+
+ // TODO: Fix endianess on big-endian systems
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Semantic instruction decoding */
+/* ---------------------------------------------------------------------------------------------- */
+
+#ifndef ZYDIS_MINIMAL_MODE
+/**
+ * Calculates the register-id for a specific register-encoding and register-class.
+ *
+ * @param context A pointer to the `ZydisDecoderContext` struct.
+ * @param instruction A pointer to the ` ZydisDecodedInstruction` struct.
+ * @param encoding The register-encoding.
+ * @param register_class The register-class.
+ *
+ * @return A zyan status code.
+ *
+ * This function calculates the register-id by combining different fields and flags of previously
+ * decoded structs.
+ */
+static ZyanU8 ZydisCalcRegisterId(const ZydisDecoderContext* context,
+ const ZydisDecodedInstruction* instruction, ZydisRegisterEncoding encoding,
+ ZydisRegisterClass register_class)
+{
+ ZYAN_ASSERT(context);
+ ZYAN_ASSERT(instruction);
+
+ // TODO: Combine OPCODE and IS4 in `ZydisPopulateRegisterIds` and get rid of this
+ // TODO: function entirely
+
+ switch (encoding)
+ {
+ case ZYDIS_REG_ENCODING_REG:
+ return context->reg_info.id_reg;
+ case ZYDIS_REG_ENCODING_NDSNDD:
+ return context->reg_info.id_ndsndd;
+ case ZYDIS_REG_ENCODING_RM:
+ return context->reg_info.id_rm;
+ case ZYDIS_REG_ENCODING_BASE:
+ return context->reg_info.id_base;
+ case ZYDIS_REG_ENCODING_INDEX:
+ case ZYDIS_REG_ENCODING_VIDX:
+ return context->reg_info.id_index;
+ case ZYDIS_REG_ENCODING_OPCODE:
+ {
+ ZYAN_ASSERT((register_class == ZYDIS_REGCLASS_GPR8) ||
+ (register_class == ZYDIS_REGCLASS_GPR16) ||
+ (register_class == ZYDIS_REGCLASS_GPR32) ||
+ (register_class == ZYDIS_REGCLASS_GPR64));
+ ZyanU8 value = (instruction->opcode & 0x0F);
+ if (value > 7)
+ {
+ value = value - 8;
+ }
+ if (instruction->machine_mode != ZYDIS_MACHINE_MODE_LONG_64)
+ {
+ return value;
+ }
+ return value | (context->vector_unified.B << 3);
+ }
+ case ZYDIS_REG_ENCODING_IS4:
+ {
+ if (instruction->machine_mode != ZYDIS_MACHINE_MODE_LONG_64)
+ {
+ return (instruction->raw.imm[0].value.u >> 4) & 0x07;
+ }
+ ZyanU8 value = (instruction->raw.imm[0].value.u >> 4) & 0x0F;
+ // We have to check the instruction-encoding, because the extension by bit [3] is only
+ // valid for EVEX and MVEX instructions
+ if ((instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_EVEX) ||
+ (instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_MVEX))
+ {
+ switch (register_class)
+ {
+ case ZYDIS_REGCLASS_XMM:
+ case ZYDIS_REGCLASS_YMM:
+ case ZYDIS_REGCLASS_ZMM:
+ value |= ((instruction->raw.imm[0].value.u & 0x08) << 1);
+ default:
+ break;
+ }
+ }
+ return value;
+ }
+ case ZYDIS_REG_ENCODING_MASK:
+ return context->vector_unified.mask;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+}
+#endif
+
+#ifndef ZYDIS_MINIMAL_MODE
+/**
+ * Sets the operand-size and element-specific information for the given operand.
+ *
+ * @param context A pointer to the `ZydisDecoderContext` struct.
+ * @param instruction A pointer to the `ZydisDecodedInstruction` struct.
+ * @param operand A pointer to the `ZydisDecodedOperand` struct.
+ * @param definition A pointer to the `ZydisOperandDefinition` struct.
+ */
+static void ZydisSetOperandSizeAndElementInfo(const ZydisDecoderContext* context,
+ const ZydisDecodedInstruction* instruction, ZydisDecodedOperand* operand,
+ const ZydisOperandDefinition* definition)
+{
+ ZYAN_ASSERT(context);
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(operand);
+ ZYAN_ASSERT(definition);
+
+ // Operand size
+ switch (operand->type)
+ {
+ case ZYDIS_OPERAND_TYPE_REGISTER:
+ {
+ if (definition->size[context->eosz_index])
+ {
+ operand->size = definition->size[context->eosz_index] * 8;
+ } else
+ {
+ operand->size = ZydisRegisterGetWidth(instruction->machine_mode,
+ operand->reg.value);
+ }
+ operand->element_type = ZYDIS_ELEMENT_TYPE_INT;
+ operand->element_size = operand->size;
+ break;
+ }
+ case ZYDIS_OPERAND_TYPE_MEMORY:
+ switch (instruction->encoding)
+ {
+ case ZYDIS_INSTRUCTION_ENCODING_LEGACY:
+ case ZYDIS_INSTRUCTION_ENCODING_3DNOW:
+ case ZYDIS_INSTRUCTION_ENCODING_XOP:
+ case ZYDIS_INSTRUCTION_ENCODING_VEX:
+ if (operand->mem.type == ZYDIS_MEMOP_TYPE_AGEN)
+ {
+ ZYAN_ASSERT(definition->size[context->eosz_index] == 0);
+ operand->size = instruction->address_width;
+ operand->element_type = ZYDIS_ELEMENT_TYPE_INT;
+ } else
+ {
+ ZYAN_ASSERT(definition->size[context->eosz_index] ||
+ (instruction->meta.category == ZYDIS_CATEGORY_AMX_TILE));
+ operand->size = definition->size[context->eosz_index] * 8;
+ }
+ break;
+ case ZYDIS_INSTRUCTION_ENCODING_EVEX:
+#ifndef ZYDIS_DISABLE_AVX512
+ if (definition->size[context->eosz_index])
+ {
+ // Operand size is hardcoded
+ operand->size = definition->size[context->eosz_index] * 8;
+ } else
+ {
+ // Operand size depends on the tuple-type, the element-size and the number of
+ // elements
+ ZYAN_ASSERT(instruction->avx.vector_length);
+ ZYAN_ASSERT(context->evex.element_size);
+ switch (context->evex.tuple_type)
+ {
+ case ZYDIS_TUPLETYPE_FV:
+ if (instruction->avx.broadcast.mode)
+ {
+ operand->size = context->evex.element_size;
+ } else
+ {
+ operand->size = instruction->avx.vector_length;
+ }
+ break;
+ case ZYDIS_TUPLETYPE_HV:
+ if (instruction->avx.broadcast.mode)
+ {
+ operand->size = context->evex.element_size;
+ } else
+ {
+ operand->size = (ZyanU16)instruction->avx.vector_length / 2;
+ }
+ break;
+ case ZYDIS_TUPLETYPE_QUARTER:
+ if (instruction->avx.broadcast.mode)
+ {
+ operand->size = context->evex.element_size;
+ }
+ else
+ {
+ operand->size = (ZyanU16)instruction->avx.vector_length / 4;
+ }
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ }
+ ZYAN_ASSERT(operand->size);
+#else
+ ZYAN_UNREACHABLE;
+#endif
+ break;
+ case ZYDIS_INSTRUCTION_ENCODING_MVEX:
+#ifndef ZYDIS_DISABLE_KNC
+ if (definition->size[context->eosz_index])
+ {
+ // Operand size is hardcoded
+ operand->size = definition->size[context->eosz_index] * 8;
+ } else
+ {
+ ZYAN_ASSERT(definition->element_type == ZYDIS_IELEMENT_TYPE_VARIABLE);
+ ZYAN_ASSERT(instruction->avx.vector_length == 512);
+
+ switch (instruction->avx.conversion.mode)
+ {
+ case ZYDIS_CONVERSION_MODE_INVALID:
+ operand->size = 512;
+ switch (context->mvex.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:
+ operand->element_type = ZYDIS_ELEMENT_TYPE_FLOAT32;
+ operand->element_size = 32;
+ break;
+ case ZYDIS_MVEX_FUNC_SF_32_BCST:
+ operand->size = 256;
+ operand->element_type = ZYDIS_ELEMENT_TYPE_FLOAT32;
+ operand->element_size = 32;
+ break;
+ 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:
+ operand->element_type = ZYDIS_ELEMENT_TYPE_INT;
+ operand->element_size = 32;
+ break;
+ case ZYDIS_MVEX_FUNC_SI_32_BCST:
+ operand->size = 256;
+ operand->element_type = ZYDIS_ELEMENT_TYPE_INT;
+ operand->element_size = 32;
+ break;
+ case ZYDIS_MVEX_FUNC_SF_64:
+ case ZYDIS_MVEX_FUNC_UF_64:
+ case ZYDIS_MVEX_FUNC_DF_64:
+ operand->element_type = ZYDIS_ELEMENT_TYPE_FLOAT64;
+ operand->element_size = 64;
+ break;
+ case ZYDIS_MVEX_FUNC_SI_64:
+ case ZYDIS_MVEX_FUNC_UI_64:
+ case ZYDIS_MVEX_FUNC_DI_64:
+ operand->element_type = ZYDIS_ELEMENT_TYPE_INT;
+ operand->element_size = 64;
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ break;
+ case ZYDIS_CONVERSION_MODE_FLOAT16:
+ operand->size = 256;
+ operand->element_type = ZYDIS_ELEMENT_TYPE_FLOAT16;
+ operand->element_size = 16;
+ break;
+ case ZYDIS_CONVERSION_MODE_SINT16:
+ operand->size = 256;
+ operand->element_type = ZYDIS_ELEMENT_TYPE_INT;
+ operand->element_size = 16;
+ break;
+ case ZYDIS_CONVERSION_MODE_UINT16:
+ operand->size = 256;
+ operand->element_type = ZYDIS_ELEMENT_TYPE_UINT;
+ operand->element_size = 16;
+ break;
+ case ZYDIS_CONVERSION_MODE_SINT8:
+ operand->size = 128;
+ operand->element_type = ZYDIS_ELEMENT_TYPE_INT;
+ operand->element_size = 8;
+ break;
+ case ZYDIS_CONVERSION_MODE_UINT8:
+ operand->size = 128;
+ operand->element_type = ZYDIS_ELEMENT_TYPE_UINT;
+ operand->element_size = 8;
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+
+ switch (instruction->avx.broadcast.mode)
+ {
+ case ZYDIS_BROADCAST_MODE_INVALID:
+ // Nothing to do here
+ break;
+ case ZYDIS_BROADCAST_MODE_1_TO_8:
+ case ZYDIS_BROADCAST_MODE_1_TO_16:
+ operand->size = operand->element_size;
+ break;
+ case ZYDIS_BROADCAST_MODE_4_TO_8:
+ case ZYDIS_BROADCAST_MODE_4_TO_16:
+ operand->size = operand->element_size * 4;
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ }
+#else
+ ZYAN_UNREACHABLE;
+#endif
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ break;
+ case ZYDIS_OPERAND_TYPE_POINTER:
+ ZYAN_ASSERT((instruction->raw.imm[0].size == 16) ||
+ (instruction->raw.imm[0].size == 32));
+ ZYAN_ASSERT( instruction->raw.imm[1].size == 16);
+ operand->size = instruction->raw.imm[0].size + instruction->raw.imm[1].size;
+ break;
+ case ZYDIS_OPERAND_TYPE_IMMEDIATE:
+ operand->size = definition->size[context->eosz_index] * 8;
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+
+ // Element-type and -size
+ if (definition->element_type && (definition->element_type != ZYDIS_IELEMENT_TYPE_VARIABLE))
+ {
+ ZydisGetElementInfo(definition->element_type, &operand->element_type,
+ &operand->element_size);
+ if (!operand->element_size)
+ {
+ // The element size is the same as the operand size. This is used for single element
+ // scaling operands
+ operand->element_size = operand->size;
+ }
+ }
+
+ // Element count
+ if (operand->element_size && operand->size && (operand->element_type != ZYDIS_ELEMENT_TYPE_CC))
+ {
+ operand->element_count = operand->size / operand->element_size;
+ } else
+ {
+ operand->element_count = 1;
+ }
+}
+#endif
+
+#ifndef ZYDIS_MINIMAL_MODE
+/**
+ * Decodes an register-operand.
+ *
+ * @param instruction A pointer to the `ZydisDecodedInstruction` struct.
+ * @param operand A pointer to the `ZydisDecodedOperand` struct.
+ * @param register_class The register class.
+ * @param register_id The register id.
+ *
+ * @return A zyan status code.
+ */
+static ZyanStatus ZydisDecodeOperandRegister(const ZydisDecodedInstruction* instruction,
+ ZydisDecodedOperand* operand, ZydisRegisterClass register_class, ZyanU8 register_id)
+{
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(operand);
+
+ operand->type = ZYDIS_OPERAND_TYPE_REGISTER;
+
+ if (register_class == ZYDIS_REGCLASS_GPR8)
+ {
+ if ((instruction->attributes & ZYDIS_ATTRIB_HAS_REX) && (register_id >= 4))
+ {
+ operand->reg.value = ZYDIS_REGISTER_SPL + (register_id - 4);
+ } else
+ {
+ operand->reg.value = ZYDIS_REGISTER_AL + register_id;
+ }
+ } else
+ {
+ operand->reg.value = ZydisRegisterEncode(register_class, register_id);
+ ZYAN_ASSERT(operand->reg.value);
+ /*if (!operand->reg.value)
+ {
+ return ZYAN_STATUS_BAD_REGISTER;
+ }*/
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+#endif
+
+#ifndef ZYDIS_MINIMAL_MODE
+/**
+ * Decodes a memory operand.
+ *
+ * @param context A pointer to the `ZydisDecoderContext` struct.
+ * @param instruction A pointer to the `ZydisDecodedInstruction` struct.
+ * @param operand A pointer to the `ZydisDecodedOperand` struct.
+ * @param vidx_register_class The register-class to use as the index register-class for
+ * instructions with `VSIB` addressing.
+ *
+ * @return A zyan status code.
+ */
+static ZyanStatus ZydisDecodeOperandMemory(const ZydisDecoderContext* context,
+ const ZydisDecodedInstruction* instruction, ZydisDecodedOperand* operand,
+ ZydisRegisterClass vidx_register_class)
+{
+ ZYAN_ASSERT(context);
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(operand);
+ ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_MODRM);
+ ZYAN_ASSERT(instruction->raw.modrm.mod != 3);
+ ZYAN_ASSERT(!vidx_register_class || ((instruction->raw.modrm.rm == 4) &&
+ ((instruction->address_width == 32) || (instruction->address_width == 64))));
+
+ operand->type = ZYDIS_OPERAND_TYPE_MEMORY;
+ operand->mem.type = ZYDIS_MEMOP_TYPE_MEM;
+
+ const ZyanU8 modrm_rm = instruction->raw.modrm.rm;
+ ZyanU8 displacement_size = 0;
+ switch (instruction->address_width)
+ {
+ case 16:
+ {
+ static const ZydisRegister bases[] =
+ {
+ ZYDIS_REGISTER_BX, ZYDIS_REGISTER_BX, ZYDIS_REGISTER_BP, ZYDIS_REGISTER_BP,
+ ZYDIS_REGISTER_SI, ZYDIS_REGISTER_DI, ZYDIS_REGISTER_BP, ZYDIS_REGISTER_BX
+ };
+ static const ZydisRegister indices[] =
+ {
+ ZYDIS_REGISTER_SI, ZYDIS_REGISTER_DI, ZYDIS_REGISTER_SI, ZYDIS_REGISTER_DI,
+ ZYDIS_REGISTER_NONE, ZYDIS_REGISTER_NONE, ZYDIS_REGISTER_NONE, ZYDIS_REGISTER_NONE
+ };
+ operand->mem.base = bases[modrm_rm];
+ operand->mem.index = indices[modrm_rm];
+ operand->mem.scale = (operand->mem.index == ZYDIS_REGISTER_NONE) ? 0 : 1;
+ switch (instruction->raw.modrm.mod)
+ {
+ case 0:
+ if (modrm_rm == 6)
+ {
+ displacement_size = 16;
+ operand->mem.base = ZYDIS_REGISTER_NONE;
+ }
+ break;
+ case 1:
+ displacement_size = 8;
+ break;
+ case 2:
+ displacement_size = 16;
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ break;
+ }
+ case 32:
+ {
+ operand->mem.base = ZYDIS_REGISTER_EAX + ZydisCalcRegisterId(context, instruction,
+ ZYDIS_REG_ENCODING_BASE, ZYDIS_REGCLASS_GPR32);
+ switch (instruction->raw.modrm.mod)
+ {
+ case 0:
+ if (modrm_rm == 5)
+ {
+ if (instruction->machine_mode == ZYDIS_MACHINE_MODE_LONG_64)
+ {
+ operand->mem.base = ZYDIS_REGISTER_EIP;
+ } else
+ {
+ operand->mem.base = ZYDIS_REGISTER_NONE;
+ }
+ displacement_size = 32;
+ }
+ break;
+ case 1:
+ displacement_size = 8;
+ break;
+ case 2:
+ displacement_size = 32;
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ if (modrm_rm == 4)
+ {
+ ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_SIB);
+ operand->mem.index =
+ ZydisRegisterEncode(vidx_register_class ? vidx_register_class : ZYDIS_REGCLASS_GPR32,
+ ZydisCalcRegisterId(context, instruction,
+ vidx_register_class ? ZYDIS_REG_ENCODING_VIDX : ZYDIS_REG_ENCODING_INDEX,
+ vidx_register_class ? vidx_register_class : ZYDIS_REGCLASS_GPR32));
+ operand->mem.scale = (1 << instruction->raw.sib.scale);
+ if (operand->mem.index == ZYDIS_REGISTER_ESP)
+ {
+ operand->mem.index = ZYDIS_REGISTER_NONE;
+ operand->mem.scale = 0;
+ }
+ if (operand->mem.base == ZYDIS_REGISTER_EBP)
+ {
+ if (instruction->raw.modrm.mod == 0)
+ {
+ operand->mem.base = ZYDIS_REGISTER_NONE;
+ }
+ displacement_size = (instruction->raw.modrm.mod == 1) ? 8 : 32;
+ }
+ } else
+ {
+ operand->mem.index = ZYDIS_REGISTER_NONE;
+ operand->mem.scale = 0;
+ }
+ break;
+ }
+ case 64:
+ {
+ operand->mem.base = ZYDIS_REGISTER_RAX + ZydisCalcRegisterId(context, instruction,
+ ZYDIS_REG_ENCODING_BASE, ZYDIS_REGCLASS_GPR64);
+ switch (instruction->raw.modrm.mod)
+ {
+ case 0:
+ if (modrm_rm == 5)
+ {
+ if (instruction->machine_mode == ZYDIS_MACHINE_MODE_LONG_64)
+ {
+ operand->mem.base = ZYDIS_REGISTER_RIP;
+ } else
+ {
+ operand->mem.base = ZYDIS_REGISTER_NONE;
+ }
+ displacement_size = 32;
+ }
+ break;
+ case 1:
+ displacement_size = 8;
+ break;
+ case 2:
+ displacement_size = 32;
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ if ((modrm_rm & 0x07) == 4)
+ {
+ ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_SIB);
+ operand->mem.index =
+ ZydisRegisterEncode(vidx_register_class ? vidx_register_class : ZYDIS_REGCLASS_GPR64,
+ ZydisCalcRegisterId(context, instruction,
+ vidx_register_class ? ZYDIS_REG_ENCODING_VIDX : ZYDIS_REG_ENCODING_INDEX,
+ vidx_register_class ? vidx_register_class : ZYDIS_REGCLASS_GPR64));
+ operand->mem.scale = (1 << instruction->raw.sib.scale);
+ if (operand->mem.index == ZYDIS_REGISTER_RSP)
+ {
+ operand->mem.index = ZYDIS_REGISTER_NONE;
+ operand->mem.scale = 0;
+ }
+ if ((operand->mem.base == ZYDIS_REGISTER_RBP) ||
+ (operand->mem.base == ZYDIS_REGISTER_R13))
+ {
+ if (instruction->raw.modrm.mod == 0)
+ {
+ operand->mem.base = ZYDIS_REGISTER_NONE;
+ }
+ displacement_size = (instruction->raw.modrm.mod == 1) ? 8 : 32;
+ }
+ } else
+ {
+ operand->mem.index = ZYDIS_REGISTER_NONE;
+ operand->mem.scale = 0;
+ }
+ break;
+ }
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ if (displacement_size)
+ {
+ ZYAN_ASSERT(instruction->raw.disp.size == displacement_size);
+ operand->mem.disp.has_displacement = ZYAN_TRUE;
+ operand->mem.disp.value = instruction->raw.disp.value;
+ }
+ return ZYAN_STATUS_SUCCESS;
+}
+#endif
+
+#ifndef ZYDIS_MINIMAL_MODE
+/**
+ * Decodes an implicit register operand.
+ *
+ * @param decoder A pointer to the `ZydisDecoder` instance.
+ * @param context A pointer to the `ZydisDecoderContext` struct.
+ * @param instruction A pointer to the `ZydisDecodedInstruction` struct.
+ * @param operand A pointer to the `ZydisDecodedOperand` struct.
+ * @param definition A pointer to the `ZydisOperandDefinition` struct.
+ */
+static void ZydisDecodeOperandImplicitRegister(const ZydisDecoder* decoder,
+ const ZydisDecoderContext* context, const ZydisDecodedInstruction* instruction,
+ ZydisDecodedOperand* operand, const ZydisOperandDefinition* definition)
+{
+ ZYAN_ASSERT(context);
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(operand);
+ ZYAN_ASSERT(definition);
+
+ operand->type = ZYDIS_OPERAND_TYPE_REGISTER;
+
+ switch (definition->op.reg.type)
+ {
+ case ZYDIS_IMPLREG_TYPE_STATIC:
+ operand->reg.value = definition->op.reg.reg.reg;
+ break;
+ case ZYDIS_IMPLREG_TYPE_GPR_OSZ:
+ {
+ static const ZydisRegisterClass lookup[3] =
+ {
+ ZYDIS_REGCLASS_GPR16,
+ ZYDIS_REGCLASS_GPR32,
+ ZYDIS_REGCLASS_GPR64
+ };
+ operand->reg.value =
+ ZydisRegisterEncode(lookup[context->eosz_index], definition->op.reg.reg.id);
+ break;
+ }
+ case ZYDIS_IMPLREG_TYPE_GPR_ASZ:
+ operand->reg.value = ZydisRegisterEncode(
+ (instruction->address_width == 16) ? ZYDIS_REGCLASS_GPR16 :
+ (instruction->address_width == 32) ? ZYDIS_REGCLASS_GPR32 : ZYDIS_REGCLASS_GPR64,
+ definition->op.reg.reg.id);
+ break;
+ case ZYDIS_IMPLREG_TYPE_IP_ASZ:
+ operand->reg.value =
+ (instruction->address_width == 16) ? ZYDIS_REGISTER_IP :
+ (instruction->address_width == 32) ? ZYDIS_REGISTER_EIP : ZYDIS_REGISTER_RIP;
+ break;
+ case ZYDIS_IMPLREG_TYPE_GPR_SSZ:
+ operand->reg.value = ZydisRegisterEncode(
+ (decoder->stack_width == ZYDIS_STACK_WIDTH_16) ? ZYDIS_REGCLASS_GPR16 :
+ (decoder->stack_width == ZYDIS_STACK_WIDTH_32) ? ZYDIS_REGCLASS_GPR32 :
+ ZYDIS_REGCLASS_GPR64,
+ definition->op.reg.reg.id);
+ break;
+ case ZYDIS_IMPLREG_TYPE_IP_SSZ:
+ operand->reg.value =
+ (decoder->stack_width == ZYDIS_STACK_WIDTH_16) ? ZYDIS_REGISTER_EIP :
+ (decoder->stack_width == ZYDIS_STACK_WIDTH_32) ? ZYDIS_REGISTER_EIP :
+ ZYDIS_REGISTER_RIP;
+ break;
+ case ZYDIS_IMPLREG_TYPE_FLAGS_SSZ:
+ operand->reg.value =
+ (decoder->stack_width == ZYDIS_STACK_WIDTH_16) ? ZYDIS_REGISTER_FLAGS :
+ (decoder->stack_width == ZYDIS_STACK_WIDTH_32) ? ZYDIS_REGISTER_EFLAGS :
+ ZYDIS_REGISTER_RFLAGS;
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+}
+#endif
+
+#ifndef ZYDIS_MINIMAL_MODE
+/**
+ * Decodes an implicit memory operand.
+ *
+ * @param decoder A pointer to the `ZydisDecoder` instance.
+ * @param context A pointer to the `ZydisDecoderContext` struct.
+ * @param instruction A pointer to the `ZydisDecodedInstruction` struct.
+ * @param operand A pointer to the `ZydisDecodedOperand` struct.
+ * @param definition A pointer to the `ZydisOperandDefinition` struct.
+ */
+static void ZydisDecodeOperandImplicitMemory(const ZydisDecoder* decoder,
+ const ZydisDecoderContext* context, const ZydisDecodedInstruction* instruction,
+ ZydisDecodedOperand* operand, const ZydisOperandDefinition* definition)
+{
+ ZYAN_ASSERT(context);
+ ZYAN_ASSERT(operand);
+ ZYAN_ASSERT(definition);
+
+ static const ZydisRegisterClass lookup[3] =
+ {
+ ZYDIS_REGCLASS_GPR16,
+ ZYDIS_REGCLASS_GPR32,
+ ZYDIS_REGCLASS_GPR64
+ };
+
+ operand->type = ZYDIS_OPERAND_TYPE_MEMORY;
+ operand->mem.type = ZYDIS_MEMOP_TYPE_MEM;
+
+ switch (definition->op.mem.base)
+ {
+ case ZYDIS_IMPLMEM_BASE_AGPR_REG:
+ operand->mem.base = ZydisRegisterEncode(lookup[context->easz_index],
+ ZydisCalcRegisterId(context, instruction, ZYDIS_REG_ENCODING_REG,
+ lookup[context->easz_index]));
+ break;
+ case ZYDIS_IMPLMEM_BASE_AGPR_RM:
+ operand->mem.base = ZydisRegisterEncode(lookup[context->easz_index],
+ ZydisCalcRegisterId(context, instruction, ZYDIS_REG_ENCODING_RM,
+ lookup[context->easz_index]));
+ break;
+ case ZYDIS_IMPLMEM_BASE_AAX:
+ operand->mem.base = ZydisRegisterEncode(lookup[context->easz_index], 0);
+ break;
+ case ZYDIS_IMPLMEM_BASE_ADX:
+ operand->mem.base = ZydisRegisterEncode(lookup[context->easz_index], 2);
+ break;
+ case ZYDIS_IMPLMEM_BASE_ABX:
+ operand->mem.base = ZydisRegisterEncode(lookup[context->easz_index], 3);
+ break;
+ case ZYDIS_IMPLMEM_BASE_ASI:
+ operand->mem.base = ZydisRegisterEncode(lookup[context->easz_index], 6);
+ break;
+ case ZYDIS_IMPLMEM_BASE_ADI:
+ operand->mem.base = ZydisRegisterEncode(lookup[context->easz_index], 7);
+ break;
+ case ZYDIS_IMPLMEM_BASE_SSP:
+ operand->mem.base = ZydisRegisterEncode(lookup[decoder->stack_width], 4);
+ break;
+ case ZYDIS_IMPLMEM_BASE_SBP:
+ operand->mem.base = ZydisRegisterEncode(lookup[decoder->stack_width], 5);
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+
+ if (definition->op.mem.seg)
+ {
+ operand->mem.segment =
+ ZydisRegisterEncode(ZYDIS_REGCLASS_SEGMENT, definition->op.mem.seg - 1);
+ ZYAN_ASSERT(operand->mem.segment);
+ }
+}
+#endif
+
+#ifndef ZYDIS_MINIMAL_MODE
+ZyanStatus ZydisDecodeOperands(const ZydisDecoder* decoder, const ZydisDecoderContext* context,
+ const ZydisDecodedInstruction* instruction, ZydisDecodedOperand* operands, ZyanU8 operand_count)
+{
+ ZYAN_ASSERT(decoder);
+ ZYAN_ASSERT(context);
+ ZYAN_ASSERT(context->definition);
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(operands);
+ ZYAN_ASSERT(operand_count);
+ ZYAN_ASSERT(operand_count <= instruction->operand_count);
+
+ const ZydisInstructionDefinition* definition = context->definition;
+ const ZydisOperandDefinition* operand = ZydisGetOperandDefinitions(definition);
+
+ ZYAN_MEMSET(operands, 0, sizeof(ZydisDecodedOperand) * operand_count);
+
+ ZyanU8 imm_id = 0;
+ for (ZyanU8 i = 0; i < operand_count; ++i)
+ {
+ ZydisRegisterClass register_class = ZYDIS_REGCLASS_INVALID;
+
+ operands[i].id = i;
+ operands[i].visibility = operand->visibility;
+ operands[i].actions = operand->actions;
+ ZYAN_ASSERT(!(operand->actions &
+ ZYDIS_OPERAND_ACTION_READ & ZYDIS_OPERAND_ACTION_CONDREAD) ||
+ (operand->actions & ZYDIS_OPERAND_ACTION_READ) ^
+ (operand->actions & ZYDIS_OPERAND_ACTION_CONDREAD));
+ ZYAN_ASSERT(!(operand->actions &
+ ZYDIS_OPERAND_ACTION_WRITE & ZYDIS_OPERAND_ACTION_CONDWRITE) ||
+ (operand->actions & ZYDIS_OPERAND_ACTION_WRITE) ^
+ (operand->actions & ZYDIS_OPERAND_ACTION_CONDWRITE));
+
+ // Implicit operands
+ switch (operand->type)
+ {
+ case ZYDIS_SEMANTIC_OPTYPE_IMPLICIT_REG:
+ ZydisDecodeOperandImplicitRegister(decoder, context, instruction, &operands[i], operand);
+ break;
+ case ZYDIS_SEMANTIC_OPTYPE_IMPLICIT_MEM:
+ ZydisDecodeOperandImplicitMemory(decoder, context, instruction, &operands[i], operand);
+ break;
+ case ZYDIS_SEMANTIC_OPTYPE_IMPLICIT_IMM1:
+ operands[i].type = ZYDIS_OPERAND_TYPE_IMMEDIATE;
+ operands[i].size = 8;
+ operands[i].imm.value.u = 1;
+ operands[i].imm.is_signed = ZYAN_FALSE;
+ operands[i].imm.is_relative = ZYAN_FALSE;
+ break;
+ default:
+ break;
+ }
+ if (operands[i].type)
+ {
+ goto FinalizeOperand;
+ }
+
+ operands[i].encoding = operand->op.encoding;
+
+ // Register operands
+ switch (operand->type)
+ {
+ case ZYDIS_SEMANTIC_OPTYPE_GPR8:
+ register_class = ZYDIS_REGCLASS_GPR8;
+ break;
+ case ZYDIS_SEMANTIC_OPTYPE_GPR16:
+ register_class = ZYDIS_REGCLASS_GPR16;
+ break;
+ case ZYDIS_SEMANTIC_OPTYPE_GPR32:
+ register_class = ZYDIS_REGCLASS_GPR32;
+ break;
+ case ZYDIS_SEMANTIC_OPTYPE_GPR64:
+ register_class = ZYDIS_REGCLASS_GPR64;
+ break;
+ case ZYDIS_SEMANTIC_OPTYPE_GPR16_32_64:
+ ZYAN_ASSERT((instruction->operand_width == 16) || (instruction->operand_width == 32) ||
+ (instruction->operand_width == 64));
+ register_class =
+ (instruction->operand_width == 16) ? ZYDIS_REGCLASS_GPR16 : (
+ (instruction->operand_width == 32) ? ZYDIS_REGCLASS_GPR32 : ZYDIS_REGCLASS_GPR64);
+ break;
+ case ZYDIS_SEMANTIC_OPTYPE_GPR32_32_64:
+ ZYAN_ASSERT((instruction->operand_width == 16) || (instruction->operand_width == 32) ||
+ (instruction->operand_width == 64));
+ register_class =
+ (instruction->operand_width == 16) ? ZYDIS_REGCLASS_GPR32 : (
+ (instruction->operand_width == 32) ? ZYDIS_REGCLASS_GPR32 : ZYDIS_REGCLASS_GPR64);
+ break;
+ case ZYDIS_SEMANTIC_OPTYPE_GPR16_32_32:
+ ZYAN_ASSERT((instruction->operand_width == 16) || (instruction->operand_width == 32) ||
+ (instruction->operand_width == 64));
+ register_class =
+ (instruction->operand_width == 16) ? ZYDIS_REGCLASS_GPR16 : ZYDIS_REGCLASS_GPR32;
+ break;
+ case ZYDIS_SEMANTIC_OPTYPE_GPR_ASZ:
+ ZYAN_ASSERT((instruction->address_width == 16) || (instruction->address_width == 32) ||
+ (instruction->address_width == 64));
+ register_class =
+ (instruction->address_width == 16) ? ZYDIS_REGCLASS_GPR16 : (
+ (instruction->address_width == 32) ? ZYDIS_REGCLASS_GPR32 : ZYDIS_REGCLASS_GPR64);
+ break;
+ case ZYDIS_SEMANTIC_OPTYPE_FPR:
+ register_class = ZYDIS_REGCLASS_X87;
+ break;
+ case ZYDIS_SEMANTIC_OPTYPE_MMX:
+ register_class = ZYDIS_REGCLASS_MMX;
+ break;
+ case ZYDIS_SEMANTIC_OPTYPE_XMM:
+ register_class = ZYDIS_REGCLASS_XMM;
+ break;
+ case ZYDIS_SEMANTIC_OPTYPE_YMM:
+ register_class = ZYDIS_REGCLASS_YMM;
+ break;
+ case ZYDIS_SEMANTIC_OPTYPE_ZMM:
+ register_class = ZYDIS_REGCLASS_ZMM;
+ break;
+ case ZYDIS_SEMANTIC_OPTYPE_TMM:
+ register_class = ZYDIS_REGCLASS_TMM;
+ break;
+ case ZYDIS_SEMANTIC_OPTYPE_BND:
+ register_class = ZYDIS_REGCLASS_BOUND;
+ break;
+ case ZYDIS_SEMANTIC_OPTYPE_SREG:
+ register_class = ZYDIS_REGCLASS_SEGMENT;
+ break;
+ case ZYDIS_SEMANTIC_OPTYPE_CR:
+ register_class = ZYDIS_REGCLASS_CONTROL;
+ break;
+ case ZYDIS_SEMANTIC_OPTYPE_DR:
+ register_class = ZYDIS_REGCLASS_DEBUG;
+ break;
+ case ZYDIS_SEMANTIC_OPTYPE_MASK:
+ register_class = ZYDIS_REGCLASS_MASK;
+ break;
+ default:
+ break;
+ }
+ if (register_class)
+ {
+ switch (operand->op.encoding)
+ {
+ case ZYDIS_OPERAND_ENCODING_MODRM_REG:
+ ZYAN_CHECK(
+ ZydisDecodeOperandRegister(
+ instruction, &operands[i], register_class,
+ ZydisCalcRegisterId(
+ context, instruction, ZYDIS_REG_ENCODING_REG, register_class)));
+ break;
+ case ZYDIS_OPERAND_ENCODING_MODRM_RM:
+ ZYAN_CHECK(
+ ZydisDecodeOperandRegister(
+ instruction, &operands[i], register_class,
+ ZydisCalcRegisterId(
+ context, instruction, ZYDIS_REG_ENCODING_RM, register_class)));
+ break;
+ case ZYDIS_OPERAND_ENCODING_OPCODE:
+ ZYAN_CHECK(
+ ZydisDecodeOperandRegister(
+ instruction, &operands[i], register_class,
+ ZydisCalcRegisterId(
+ context, instruction, ZYDIS_REG_ENCODING_OPCODE, register_class)));
+ break;
+ case ZYDIS_OPERAND_ENCODING_NDSNDD:
+ ZYAN_CHECK(
+ ZydisDecodeOperandRegister(
+ instruction, &operands[i], register_class,
+ ZydisCalcRegisterId(
+ context, instruction, ZYDIS_REG_ENCODING_NDSNDD, register_class)));
+ break;
+ case ZYDIS_OPERAND_ENCODING_MASK:
+ ZYAN_CHECK(
+ ZydisDecodeOperandRegister(
+ instruction, &operands[i], register_class,
+ ZydisCalcRegisterId(
+ context, instruction, ZYDIS_REG_ENCODING_MASK, register_class)));
+ break;
+ case ZYDIS_OPERAND_ENCODING_IS4:
+ ZYAN_CHECK(
+ ZydisDecodeOperandRegister(
+ instruction, &operands[i], register_class,
+ ZydisCalcRegisterId(
+ context, instruction, ZYDIS_REG_ENCODING_IS4, register_class)));
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+
+ if (operand->is_multisource4)
+ {
+ operands[i].attributes |= ZYDIS_OATTRIB_IS_MULTISOURCE4;
+ }
+
+ goto FinalizeOperand;
+ }
+
+ // Memory operands
+ switch (operand->type)
+ {
+ case ZYDIS_SEMANTIC_OPTYPE_MEM:
+ ZYAN_CHECK(
+ ZydisDecodeOperandMemory(
+ context, instruction, &operands[i], ZYDIS_REGCLASS_INVALID));
+ break;
+ case ZYDIS_SEMANTIC_OPTYPE_MEM_VSIBX:
+ ZYAN_CHECK(
+ ZydisDecodeOperandMemory(
+ context, instruction, &operands[i], ZYDIS_REGCLASS_XMM));
+ operands[i].mem.type = ZYDIS_MEMOP_TYPE_VSIB;
+ break;
+ case ZYDIS_SEMANTIC_OPTYPE_MEM_VSIBY:
+ ZYAN_CHECK(
+ ZydisDecodeOperandMemory(
+ context, instruction, &operands[i], ZYDIS_REGCLASS_YMM));
+ operands[i].mem.type = ZYDIS_MEMOP_TYPE_VSIB;
+ break;
+ case ZYDIS_SEMANTIC_OPTYPE_MEM_VSIBZ:
+ ZYAN_CHECK(
+ ZydisDecodeOperandMemory(
+ context, instruction, &operands[i], ZYDIS_REGCLASS_ZMM));
+ operands[i].mem.type = ZYDIS_MEMOP_TYPE_VSIB;
+ break;
+ case ZYDIS_SEMANTIC_OPTYPE_PTR:
+ ZYAN_ASSERT((instruction->raw.imm[0].size == 16) ||
+ (instruction->raw.imm[0].size == 32));
+ ZYAN_ASSERT(instruction->raw.imm[1].size == 16);
+ operands[i].type = ZYDIS_OPERAND_TYPE_POINTER;
+ operands[i].ptr.offset = (ZyanU32)instruction->raw.imm[0].value.u;
+ operands[i].ptr.segment = (ZyanU16)instruction->raw.imm[1].value.u;
+ break;
+ case ZYDIS_SEMANTIC_OPTYPE_AGEN:
+ operands[i].actions = 0; // TODO: Remove after generator update
+ ZYAN_CHECK(
+ ZydisDecodeOperandMemory(
+ context, instruction, &operands[i], ZYDIS_REGCLASS_INVALID));
+ operands[i].mem.type = ZYDIS_MEMOP_TYPE_AGEN;
+ break;
+ case ZYDIS_SEMANTIC_OPTYPE_MOFFS:
+ ZYAN_ASSERT(instruction->raw.disp.size);
+ operands[i].type = ZYDIS_OPERAND_TYPE_MEMORY;
+ operands[i].mem.type = ZYDIS_MEMOP_TYPE_MEM;
+ operands[i].mem.disp.has_displacement = ZYAN_TRUE;
+ operands[i].mem.disp.value = instruction->raw.disp.value;
+ break;
+ case ZYDIS_SEMANTIC_OPTYPE_MIB:
+ operands[i].actions = 0; // TODO: Remove after generator update
+ ZYAN_CHECK(
+ ZydisDecodeOperandMemory(
+ context, instruction, &operands[i], ZYDIS_REGCLASS_INVALID));
+ operands[i].mem.type = ZYDIS_MEMOP_TYPE_MIB;
+ break;
+ default:
+ break;
+ }
+ if (operands[i].type)
+ {
+#if !defined(ZYDIS_DISABLE_AVX512) || !defined(ZYDIS_DISABLE_KNC)
+ // Handle compressed 8-bit displacement
+ if (((instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_EVEX) ||
+ (instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_MVEX)) &&
+ (instruction->raw.disp.size == 8))
+ {
+ operands[i].mem.disp.value *= context->cd8_scale;
+ }
+#endif
+
+ goto FinalizeOperand;
+ }
+
+ // Immediate operands
+ switch (operand->type)
+ {
+ case ZYDIS_SEMANTIC_OPTYPE_REL:
+ ZYAN_ASSERT(instruction->raw.imm[imm_id].is_relative);
+ ZYAN_FALLTHROUGH;
+ case ZYDIS_SEMANTIC_OPTYPE_IMM:
+ ZYAN_ASSERT((imm_id == 0) || (imm_id == 1));
+ operands[i].type = ZYDIS_OPERAND_TYPE_IMMEDIATE;
+ operands[i].size = operand->size[context->eosz_index] * 8;
+ if (operand->op.encoding == ZYDIS_OPERAND_ENCODING_IS4)
+ {
+ // The upper half of the 8-bit immediate is used to encode a register specifier
+ ZYAN_ASSERT(instruction->raw.imm[imm_id].size == 8);
+ operands[i].imm.value.u = (ZyanU8)instruction->raw.imm[imm_id].value.u & 0x0F;
+ }
+ else
+ {
+ operands[i].imm.value.u = instruction->raw.imm[imm_id].value.u;
+ }
+ operands[i].imm.is_signed = instruction->raw.imm[imm_id].is_signed;
+ operands[i].imm.is_relative = instruction->raw.imm[imm_id].is_relative;
+ ++imm_id;
+ break;
+ default:
+ break;
+ }
+ ZYAN_ASSERT(operands[i].type == ZYDIS_OPERAND_TYPE_IMMEDIATE);
+
+ FinalizeOperand:
+ // Set segment-register for memory operands
+ if (operands[i].type == ZYDIS_OPERAND_TYPE_MEMORY)
+ {
+ if (!operand->ignore_seg_override &&
+ instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT_CS)
+ {
+ operands[i].mem.segment = ZYDIS_REGISTER_CS;
+ }
+ else
+ if (!operand->ignore_seg_override &&
+ instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT_SS)
+ {
+ operands[i].mem.segment = ZYDIS_REGISTER_SS;
+ }
+ else
+ if (!operand->ignore_seg_override &&
+ instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT_DS)
+ {
+ operands[i].mem.segment = ZYDIS_REGISTER_DS;
+ }
+ else
+ if (!operand->ignore_seg_override &&
+ instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT_ES)
+ {
+ operands[i].mem.segment = ZYDIS_REGISTER_ES;
+ }
+ else
+ if (!operand->ignore_seg_override &&
+ instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT_FS)
+ {
+ operands[i].mem.segment = ZYDIS_REGISTER_FS;
+ }
+ else
+ if (!operand->ignore_seg_override &&
+ instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT_GS)
+ {
+ operands[i].mem.segment = ZYDIS_REGISTER_GS;
+ }
+ else
+ {
+ if (operands[i].mem.segment == ZYDIS_REGISTER_NONE)
+ {
+ if ((operands[i].mem.base == ZYDIS_REGISTER_RSP) ||
+ (operands[i].mem.base == ZYDIS_REGISTER_RBP) ||
+ (operands[i].mem.base == ZYDIS_REGISTER_ESP) ||
+ (operands[i].mem.base == ZYDIS_REGISTER_EBP) ||
+ (operands[i].mem.base == ZYDIS_REGISTER_SP) ||
+ (operands[i].mem.base == ZYDIS_REGISTER_BP))
+ {
+ operands[i].mem.segment = ZYDIS_REGISTER_SS;
+ }
+ else
+ {
+ operands[i].mem.segment = ZYDIS_REGISTER_DS;
+ }
+ }
+ }
+ }
+
+ ZydisSetOperandSizeAndElementInfo(context, instruction, &operands[i], operand);
+ ++operand;
+ }
+
+#if !defined(ZYDIS_DISABLE_AVX512) || !defined(ZYDIS_DISABLE_KNC)
+ // Fix operand-action for EVEX/MVEX instructions with merge-mask
+ if (instruction->avx.mask.mode == ZYDIS_MASK_MODE_MERGING)
+ {
+ ZYAN_ASSERT(operand_count >= 1);
+ switch (operands[0].actions)
+ {
+ case ZYDIS_OPERAND_ACTION_WRITE:
+ if (operands[0].type == ZYDIS_OPERAND_TYPE_MEMORY)
+ {
+ operands[0].actions = ZYDIS_OPERAND_ACTION_CONDWRITE;
+ }
+ else
+ {
+ operands[0].actions = ZYDIS_OPERAND_ACTION_READ_CONDWRITE;
+ }
+ break;
+ case ZYDIS_OPERAND_ACTION_READWRITE:
+ operands[0].actions = ZYDIS_OPERAND_ACTION_READ_CONDWRITE;
+ break;
+ default:
+ break;
+ }
+ }
+#endif
+
+ return ZYAN_STATUS_SUCCESS;
+}
+#endif
+
+/* ---------------------------------------------------------------------------------------------- */
+
+#ifndef ZYDIS_MINIMAL_MODE
+/**
+ * Sets attributes for the given instruction.
+ *
+ * @param state A pointer to the `ZydisDecoderState` struct.
+ * @param instruction A pointer to the `ZydisDecodedInstruction` struct.
+ * @param definition A pointer to the `ZydisInstructionDefinition` struct.
+ */
+static void ZydisSetAttributes(ZydisDecoderState* state, ZydisDecodedInstruction* instruction,
+ const ZydisInstructionDefinition* definition)
+{
+ ZYAN_ASSERT(state);
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(definition);
+
+ if (definition->cpu_state != ZYDIS_RW_ACTION_NONE)
+ {
+ static const ZydisInstructionAttributes mapping[ZYDIS_RW_ACTION_MAX_VALUE + 1] =
+ {
+ /* NONE */ 0,
+ /* READ */ ZYDIS_ATTRIB_CPU_STATE_CR,
+ /* WRITE */ ZYDIS_ATTRIB_CPU_STATE_CW,
+ /* READWRITE */ ZYDIS_ATTRIB_CPU_STATE_CR | ZYDIS_ATTRIB_CPU_STATE_CW
+ };
+ ZYAN_ASSERT(definition->cpu_state < ZYAN_ARRAY_LENGTH(mapping));
+ instruction->attributes |= mapping[definition->cpu_state];
+ }
+
+ if (definition->fpu_state != ZYDIS_RW_ACTION_NONE)
+ {
+ static const ZydisInstructionAttributes mapping[ZYDIS_RW_ACTION_MAX_VALUE + 1] =
+ {
+ /* NONE */ 0,
+ /* READ */ ZYDIS_ATTRIB_FPU_STATE_CR,
+ /* WRITE */ ZYDIS_ATTRIB_FPU_STATE_CW,
+ /* READWRITE */ ZYDIS_ATTRIB_FPU_STATE_CR | ZYDIS_ATTRIB_FPU_STATE_CW
+ };
+ ZYAN_ASSERT(definition->fpu_state < ZYAN_ARRAY_LENGTH(mapping));
+ instruction->attributes |= mapping[definition->fpu_state];
+ }
+
+ if (definition->xmm_state != ZYDIS_RW_ACTION_NONE)
+ {
+ static const ZydisInstructionAttributes mapping[ZYDIS_RW_ACTION_MAX_VALUE + 1] =
+ {
+ /* NONE */ 0,
+ /* READ */ ZYDIS_ATTRIB_XMM_STATE_CR,
+ /* WRITE */ ZYDIS_ATTRIB_XMM_STATE_CW,
+ /* READWRITE */ ZYDIS_ATTRIB_XMM_STATE_CR | ZYDIS_ATTRIB_XMM_STATE_CW
+ };
+ ZYAN_ASSERT(definition->xmm_state < ZYAN_ARRAY_LENGTH(mapping));
+ instruction->attributes |= mapping[definition->xmm_state];
+ }
+
+ switch (instruction->encoding)
+ {
+ case ZYDIS_INSTRUCTION_ENCODING_LEGACY:
+ {
+ const ZydisInstructionDefinitionLEGACY* def =
+ (const ZydisInstructionDefinitionLEGACY*)definition;
+
+ if (def->is_privileged)
+ {
+ instruction->attributes |= ZYDIS_ATTRIB_IS_PRIVILEGED;
+ }
+ if (def->accepts_LOCK)
+ {
+ instruction->attributes |= ZYDIS_ATTRIB_ACCEPTS_LOCK;
+ if (state->prefixes.has_lock)
+ {
+ instruction->attributes |= ZYDIS_ATTRIB_HAS_LOCK;
+ instruction->raw.prefixes[state->prefixes.offset_lock].type =
+ ZYDIS_PREFIX_TYPE_EFFECTIVE;
+ }
+ }
+ if (def->accepts_REP)
+ {
+ instruction->attributes |= ZYDIS_ATTRIB_ACCEPTS_REP;
+ }
+ if (def->accepts_REPEREPZ)
+ {
+ instruction->attributes |= ZYDIS_ATTRIB_ACCEPTS_REPE;
+ }
+ if (def->accepts_REPNEREPNZ)
+ {
+ instruction->attributes |= ZYDIS_ATTRIB_ACCEPTS_REPNE;
+ }
+ if (def->accepts_BOUND)
+ {
+ instruction->attributes |= ZYDIS_ATTRIB_ACCEPTS_BND;
+ }
+ if (def->accepts_XACQUIRE)
+ {
+ instruction->attributes |= ZYDIS_ATTRIB_ACCEPTS_XACQUIRE;
+ }
+ if (def->accepts_XRELEASE)
+ {
+ instruction->attributes |= ZYDIS_ATTRIB_ACCEPTS_XRELEASE;
+ }
+ if (def->accepts_hle_without_lock)
+ {
+ instruction->attributes |= ZYDIS_ATTRIB_ACCEPTS_HLE_WITHOUT_LOCK;
+ }
+
+ switch (state->prefixes.group1)
+ {
+ case 0xF2:
+ if (instruction->attributes & ZYDIS_ATTRIB_ACCEPTS_REPNE)
+ {
+ instruction->attributes |= ZYDIS_ATTRIB_HAS_REPNE;
+ break;
+ }
+ if (instruction->attributes & ZYDIS_ATTRIB_ACCEPTS_XACQUIRE)
+ {
+ if ((instruction->attributes & ZYDIS_ATTRIB_HAS_LOCK) ||
+ (def->accepts_hle_without_lock))
+ {
+ instruction->attributes |= ZYDIS_ATTRIB_HAS_XACQUIRE;
+ break;
+ }
+ }
+ if (state->decoder->decoder_mode[ZYDIS_DECODER_MODE_MPX] &&
+ instruction->attributes & ZYDIS_ATTRIB_ACCEPTS_BND)
+ {
+ instruction->attributes |= ZYDIS_ATTRIB_HAS_BND;
+ break;
+ }
+ break;
+ case 0xF3:
+ if (instruction->attributes & ZYDIS_ATTRIB_ACCEPTS_REP)
+ {
+ instruction->attributes |= ZYDIS_ATTRIB_HAS_REP;
+ break;
+ }
+ if (instruction->attributes & ZYDIS_ATTRIB_ACCEPTS_REPE)
+ {
+ instruction->attributes |= ZYDIS_ATTRIB_HAS_REPE;
+ break;
+ }
+ if (instruction->attributes & ZYDIS_ATTRIB_ACCEPTS_XRELEASE)
+ {
+ if ((instruction->attributes & ZYDIS_ATTRIB_HAS_LOCK) ||
+ (def->accepts_hle_without_lock))
+ {
+ instruction->attributes |= ZYDIS_ATTRIB_HAS_XRELEASE;
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ if ((instruction->raw.prefixes[state->prefixes.offset_group1].type ==
+ ZYDIS_PREFIX_TYPE_IGNORED) &&
+ (instruction->attributes & (
+ ZYDIS_ATTRIB_HAS_REP | ZYDIS_ATTRIB_HAS_REPE | ZYDIS_ATTRIB_HAS_REPNE |
+ ZYDIS_ATTRIB_HAS_BND | ZYDIS_ATTRIB_HAS_XACQUIRE | ZYDIS_ATTRIB_HAS_XRELEASE)))
+ {
+ instruction->raw.prefixes[state->prefixes.offset_group1].type =
+ ZYDIS_PREFIX_TYPE_EFFECTIVE;
+ }
+
+ if (def->accepts_branch_hints)
+ {
+ instruction->attributes |= ZYDIS_ATTRIB_ACCEPTS_BRANCH_HINTS;
+ switch (state->prefixes.group2)
+ {
+ case 0x2E:
+ instruction->attributes |= ZYDIS_ATTRIB_HAS_BRANCH_NOT_TAKEN;
+ instruction->raw.prefixes[state->prefixes.offset_group2].type =
+ ZYDIS_PREFIX_TYPE_EFFECTIVE;
+ break;
+ case 0x3E:
+ instruction->attributes |= ZYDIS_ATTRIB_HAS_BRANCH_TAKEN;
+ instruction->raw.prefixes[state->prefixes.offset_group2].type =
+ ZYDIS_PREFIX_TYPE_EFFECTIVE;
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (def->accepts_NOTRACK)
+ {
+ instruction->attributes |= ZYDIS_ATTRIB_ACCEPTS_NOTRACK;
+ if (state->decoder->decoder_mode[ZYDIS_DECODER_MODE_CET] &&
+ (state->prefixes.offset_notrack >= 0))
+ {
+ instruction->attributes |= ZYDIS_ATTRIB_HAS_NOTRACK;
+ instruction->raw.prefixes[state->prefixes.offset_notrack].type =
+ ZYDIS_PREFIX_TYPE_EFFECTIVE;
+ }
+ }
+
+ if (def->accepts_segment && !def->accepts_branch_hints)
+ {
+ instruction->attributes |= ZYDIS_ATTRIB_ACCEPTS_SEGMENT;
+ if (state->prefixes.effective_segment &&
+ !(instruction->attributes & ZYDIS_ATTRIB_HAS_NOTRACK))
+ {
+ switch (state->prefixes.effective_segment)
+ {
+ case 0x2E:
+ instruction->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_CS;
+ break;
+ case 0x36:
+ instruction->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_SS;
+ break;
+ case 0x3E:
+ instruction->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_DS;
+ break;
+ case 0x26:
+ instruction->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_ES;
+ break;
+ case 0x64:
+ instruction->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_FS;
+ break;
+ case 0x65:
+ instruction->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_GS;
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ }
+ if (instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT)
+ {
+ instruction->raw.prefixes[state->prefixes.offset_segment].type =
+ ZYDIS_PREFIX_TYPE_EFFECTIVE;
+ }
+ }
+
+ break;
+ }
+ case ZYDIS_INSTRUCTION_ENCODING_3DNOW:
+ case ZYDIS_INSTRUCTION_ENCODING_XOP:
+ case ZYDIS_INSTRUCTION_ENCODING_VEX:
+ case ZYDIS_INSTRUCTION_ENCODING_EVEX:
+ case ZYDIS_INSTRUCTION_ENCODING_MVEX:
+ if (definition->accepts_segment)
+ {
+ instruction->attributes |= ZYDIS_ATTRIB_ACCEPTS_SEGMENT;
+ if (state->prefixes.effective_segment)
+ {
+ switch (state->prefixes.effective_segment)
+ {
+ case 0x2E:
+ instruction->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_CS;
+ break;
+ case 0x36:
+ instruction->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_SS;
+ break;
+ case 0x3E:
+ instruction->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_DS;
+ break;
+ case 0x26:
+ instruction->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_ES;
+ break;
+ case 0x64:
+ instruction->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_FS;
+ break;
+ case 0x65:
+ instruction->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_GS;
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ }
+ if (instruction->attributes & ZYDIS_ATTRIB_HAS_SEGMENT)
+ {
+ instruction->raw.prefixes[state->prefixes.offset_segment].type =
+ ZYDIS_PREFIX_TYPE_EFFECTIVE;
+ }
+ }
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+}
+#endif
+
+#ifndef ZYDIS_MINIMAL_MODE
+/**
+ * Sets AVX-specific information for the given instruction.
+ *
+ * @param context A pointer to the `ZydisDecoderContext` struct.
+ * @param instruction A pointer to the `ZydisDecodedInstruction` struct.
+ * @param definition A pointer to the `ZydisInstructionDefinition` struct.
+ *
+ * Information set for `XOP`:
+ * - Vector Length
+ *
+ * Information set for `VEX`:
+ * - Vector length
+ * - Static broadcast-factor
+ *
+ * Information set for `EVEX`:
+ * - Vector length
+ * - Broadcast-factor (static and dynamic)
+ * - Rounding-mode and SAE
+ * - Mask mode
+ * - Compressed 8-bit displacement scale-factor
+ *
+ * Information set for `MVEX`:
+ * - Vector length
+ * - Broadcast-factor (static and dynamic)
+ * - Rounding-mode and SAE
+ * - Swizzle- and conversion-mode
+ * - Mask mode
+ * - Eviction hint
+ * - Compressed 8-bit displacement scale-factor
+ */
+static void ZydisSetAVXInformation(ZydisDecoderContext* context,
+ ZydisDecodedInstruction* instruction, const ZydisInstructionDefinition* definition)
+{
+ ZYAN_ASSERT(context);
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(definition);
+
+ switch (instruction->encoding)
+ {
+ case ZYDIS_INSTRUCTION_ENCODING_XOP:
+ {
+ // Vector length
+ static const ZyanU16 lookup[2] =
+ {
+ 128,
+ 256
+ };
+ ZYAN_ASSERT(context->vector_unified.LL < ZYAN_ARRAY_LENGTH(lookup));
+ instruction->avx.vector_length = lookup[context->vector_unified.LL];
+ break;
+ }
+ case ZYDIS_INSTRUCTION_ENCODING_VEX:
+ {
+ // Vector length
+ static const ZyanU16 lookup[2] =
+ {
+ 128,
+ 256
+ };
+ ZYAN_ASSERT(context->vector_unified.LL < ZYAN_ARRAY_LENGTH(lookup));
+ instruction->avx.vector_length = lookup[context->vector_unified.LL];
+
+ // Static broadcast-factor
+ const ZydisInstructionDefinitionVEX* def =
+ (const ZydisInstructionDefinitionVEX*)definition;
+ if (def->broadcast)
+ {
+ instruction->avx.broadcast.is_static = ZYAN_TRUE;
+ static ZydisBroadcastMode broadcasts[ZYDIS_VEX_STATIC_BROADCAST_MAX_VALUE + 1] =
+ {
+ ZYDIS_BROADCAST_MODE_INVALID,
+ ZYDIS_BROADCAST_MODE_1_TO_2,
+ ZYDIS_BROADCAST_MODE_1_TO_4,
+ ZYDIS_BROADCAST_MODE_1_TO_8,
+ ZYDIS_BROADCAST_MODE_1_TO_16,
+ ZYDIS_BROADCAST_MODE_1_TO_32,
+ ZYDIS_BROADCAST_MODE_2_TO_4
+ };
+ instruction->avx.broadcast.mode = broadcasts[def->broadcast];
+ }
+ break;
+ }
+ case ZYDIS_INSTRUCTION_ENCODING_EVEX:
+ {
+#ifndef ZYDIS_DISABLE_AVX512
+ const ZydisInstructionDefinitionEVEX* def =
+ (const ZydisInstructionDefinitionEVEX*)definition;
+
+ // Vector length
+ ZyanU8 vector_length = context->vector_unified.LL;
+ if (def->vector_length)
+ {
+ vector_length = def->vector_length - 1;
+ }
+ static const ZyanU16 lookup[3] =
+ {
+ 128,
+ 256,
+ 512
+ };
+ ZYAN_ASSERT(vector_length < ZYAN_ARRAY_LENGTH(lookup));
+ instruction->avx.vector_length = lookup[vector_length];
+
+ context->evex.tuple_type = def->tuple_type;
+ if (def->tuple_type)
+ {
+ ZYAN_ASSERT(instruction->raw.modrm.mod != 3);
+ ZYAN_ASSERT(def->element_size);
+
+ // Element size
+ static const ZyanU8 element_sizes[ZYDIS_IELEMENT_SIZE_MAX_VALUE + 1] =
+ {
+ 0, 8, 16, 32, 64, 128
+ };
+ ZYAN_ASSERT(def->element_size < ZYAN_ARRAY_LENGTH(element_sizes));
+ context->evex.element_size = element_sizes[def->element_size];
+
+ // Compressed disp8 scale and broadcast-factor
+ switch (def->tuple_type)
+ {
+ case ZYDIS_TUPLETYPE_FV:
+ {
+ const ZyanU8 evex_b = instruction->raw.evex.b;
+ ZYAN_ASSERT(evex_b < 2);
+ ZYAN_ASSERT(!evex_b || ((!context->vector_unified.W && (context->evex.element_size == 16 ||
+ context->evex.element_size == 32)) ||
+ ( context->vector_unified.W && context->evex.element_size == 64)));
+ ZYAN_ASSERT(!evex_b || def->functionality == ZYDIS_EVEX_FUNC_BC);
+
+ static const ZyanU8 scales[2][3][3] =
+ {
+ /*B0*/ { /*16*/ { 16, 32, 64 }, /*32*/ { 16, 32, 64 }, /*64*/ { 16, 32, 64 } },
+ /*B1*/ { /*16*/ { 2, 2, 2 }, /*32*/ { 4, 4, 4 }, /*64*/ { 8, 8, 8 } }
+ };
+ static const ZydisBroadcastMode broadcasts[2][3][3] =
+ {
+ /*B0*/
+ {
+ /*16*/
+ {
+ ZYDIS_BROADCAST_MODE_INVALID,
+ ZYDIS_BROADCAST_MODE_INVALID,
+ ZYDIS_BROADCAST_MODE_INVALID
+ },
+ /*32*/
+ {
+ ZYDIS_BROADCAST_MODE_INVALID,
+ ZYDIS_BROADCAST_MODE_INVALID,
+ ZYDIS_BROADCAST_MODE_INVALID
+ },
+ /*64*/
+ {
+ ZYDIS_BROADCAST_MODE_INVALID,
+ ZYDIS_BROADCAST_MODE_INVALID,
+ ZYDIS_BROADCAST_MODE_INVALID
+ }
+ },
+ /*B1*/
+ {
+ /*16*/
+ {
+ ZYDIS_BROADCAST_MODE_1_TO_8,
+ ZYDIS_BROADCAST_MODE_1_TO_16,
+ ZYDIS_BROADCAST_MODE_1_TO_32
+ },
+ /*32*/
+ {
+ ZYDIS_BROADCAST_MODE_1_TO_4,
+ ZYDIS_BROADCAST_MODE_1_TO_8,
+ ZYDIS_BROADCAST_MODE_1_TO_16
+ },
+ /*64*/
+ {
+ ZYDIS_BROADCAST_MODE_1_TO_2,
+ ZYDIS_BROADCAST_MODE_1_TO_4,
+ ZYDIS_BROADCAST_MODE_1_TO_8
+ }
+ }
+ };
+
+ const ZyanU8 size_index = context->evex.element_size >> 5;
+ ZYAN_ASSERT(size_index < 3);
+
+ context->cd8_scale = scales[evex_b][size_index][vector_length];
+ instruction->avx.broadcast.mode = broadcasts[evex_b][size_index][vector_length];
+ break;
+ }
+ case ZYDIS_TUPLETYPE_HV:
+ {
+ const ZyanU8 evex_b = instruction->raw.evex.b;
+ ZYAN_ASSERT(evex_b < 2);
+ ZYAN_ASSERT(!context->vector_unified.W);
+ ZYAN_ASSERT((context->evex.element_size == 16) ||
+ (context->evex.element_size == 32));
+ ZYAN_ASSERT(!evex_b || def->functionality == ZYDIS_EVEX_FUNC_BC);
+
+ static const ZyanU8 scales[2][2][3] =
+ {
+ /*B0*/ { /*16*/ { 8, 16, 32 }, /*32*/ { 8, 16, 32 } },
+ /*B1*/ { /*16*/ { 2, 2, 2 }, /*32*/ { 4, 4, 4 } }
+ };
+ static const ZydisBroadcastMode broadcasts[2][2][3] =
+ {
+ /*B0*/
+ {
+ /*16*/
+ {
+ ZYDIS_BROADCAST_MODE_INVALID,
+ ZYDIS_BROADCAST_MODE_INVALID,
+ ZYDIS_BROADCAST_MODE_INVALID
+ },
+ /*32*/
+ {
+ ZYDIS_BROADCAST_MODE_INVALID,
+ ZYDIS_BROADCAST_MODE_INVALID,
+ ZYDIS_BROADCAST_MODE_INVALID
+ }
+ },
+ /*B1*/
+ {
+ /*16*/
+ {
+ ZYDIS_BROADCAST_MODE_1_TO_4,
+ ZYDIS_BROADCAST_MODE_1_TO_8,
+ ZYDIS_BROADCAST_MODE_1_TO_16
+ },
+ /*32*/
+ {
+ ZYDIS_BROADCAST_MODE_1_TO_2,
+ ZYDIS_BROADCAST_MODE_1_TO_4,
+ ZYDIS_BROADCAST_MODE_1_TO_8
+ }
+ }
+ };
+
+ const ZyanU8 size_index = context->evex.element_size >> 5;
+ ZYAN_ASSERT(size_index < 3);
+
+ context->cd8_scale = scales[evex_b][size_index][vector_length];
+ instruction->avx.broadcast.mode = broadcasts[evex_b][size_index][vector_length];
+ break;
+ }
+ case ZYDIS_TUPLETYPE_FVM:
+ {
+ static const ZyanU8 scales[3] =
+ {
+ 16, 32, 64
+ };
+ context->cd8_scale = scales[vector_length];
+ break;
+ }
+ case ZYDIS_TUPLETYPE_GSCAT:
+ switch (context->vector_unified.W)
+ {
+ case 0:
+ ZYAN_ASSERT(context->evex.element_size == 32);
+ break;
+ case 1:
+ ZYAN_ASSERT(context->evex.element_size == 64);
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ ZYAN_FALLTHROUGH;
+ case ZYDIS_TUPLETYPE_T1S:
+ {
+ static const ZyanU8 scales[6] =
+ {
+ /* */ 0,
+ /* 8*/ 1,
+ /* 16*/ 2,
+ /* 32*/ 4,
+ /* 64*/ 8,
+ /*128*/ 16,
+ };
+ ZYAN_ASSERT(def->element_size < ZYAN_ARRAY_LENGTH(scales));
+ context->cd8_scale = scales[def->element_size];
+ break;
+ };
+ case ZYDIS_TUPLETYPE_T1F:
+ {
+ static const ZyanU8 scales[3] =
+ {
+ /* 16*/ 2,
+ /* 32*/ 4,
+ /* 64*/ 8
+ };
+
+ const ZyanU8 size_index = context->evex.element_size >> 5;
+ ZYAN_ASSERT(size_index < 3);
+
+ context->cd8_scale = scales[size_index];
+ break;
+ }
+ case ZYDIS_TUPLETYPE_T1_4X:
+ ZYAN_ASSERT(context->evex.element_size == 32);
+ ZYAN_ASSERT(context->vector_unified.W == 0);
+ context->cd8_scale = 16;
+ break;
+ case ZYDIS_TUPLETYPE_T2:
+ switch (context->vector_unified.W)
+ {
+ case 0:
+ ZYAN_ASSERT(context->evex.element_size == 32);
+ context->cd8_scale = 8;
+ break;
+ case 1:
+ ZYAN_ASSERT(context->evex.element_size == 64);
+ ZYAN_ASSERT((instruction->avx.vector_length == 256) ||
+ (instruction->avx.vector_length == 512));
+ context->cd8_scale = 16;
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ break;
+ case ZYDIS_TUPLETYPE_T4:
+ switch (context->vector_unified.W)
+ {
+ case 0:
+ ZYAN_ASSERT(context->evex.element_size == 32);
+ ZYAN_ASSERT((instruction->avx.vector_length == 256) ||
+ (instruction->avx.vector_length == 512));
+ context->cd8_scale = 16;
+ break;
+ case 1:
+ ZYAN_ASSERT(context->evex.element_size == 64);
+ ZYAN_ASSERT(instruction->avx.vector_length == 512);
+ context->cd8_scale = 32;
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ break;
+ case ZYDIS_TUPLETYPE_T8:
+ ZYAN_ASSERT(!context->vector_unified.W);
+ ZYAN_ASSERT(instruction->avx.vector_length == 512);
+ ZYAN_ASSERT(context->evex.element_size == 32);
+ context->cd8_scale = 32;
+ break;
+ case ZYDIS_TUPLETYPE_HVM:
+ {
+ static const ZyanU8 scales[3] =
+ {
+ 8, 16, 32
+ };
+ context->cd8_scale = scales[vector_length];
+ break;
+ }
+ case ZYDIS_TUPLETYPE_QVM:
+ {
+ static const ZyanU8 scales[3] =
+ {
+ 4, 8, 16
+ };
+ context->cd8_scale = scales[vector_length];
+ break;
+ }
+ case ZYDIS_TUPLETYPE_OVM:
+ {
+ static const ZyanU8 scales[3] =
+ {
+ 2, 4, 8
+ };
+ context->cd8_scale = scales[vector_length];
+ break;
+ }
+ case ZYDIS_TUPLETYPE_M128:
+ context->cd8_scale = 16;
+ break;
+ case ZYDIS_TUPLETYPE_DUP:
+ {
+ static const ZyanU8 scales[3] =
+ {
+ 8, 32, 64
+ };
+ context->cd8_scale = scales[vector_length];
+ break;
+ }
+ case ZYDIS_TUPLETYPE_QUARTER:
+ {
+ const ZyanU8 evex_b = instruction->raw.evex.b;
+ ZYAN_ASSERT(evex_b < 2);
+ ZYAN_ASSERT(!context->vector_unified.W);
+ ZYAN_ASSERT(context->evex.element_size == 16);
+ ZYAN_ASSERT(!evex_b || def->functionality == ZYDIS_EVEX_FUNC_BC);
+
+ static const ZyanU8 scales[2][3] =
+ {
+ /*B0*/ { 4, 8, 16 },
+ /*B1*/ { 2, 2, 2 }
+ };
+ static const ZydisBroadcastMode broadcasts[2][3] =
+ {
+ /*B0*/
+ {
+ ZYDIS_BROADCAST_MODE_INVALID,
+ ZYDIS_BROADCAST_MODE_INVALID,
+ ZYDIS_BROADCAST_MODE_INVALID
+ },
+ /*B1*/
+ {
+ ZYDIS_BROADCAST_MODE_1_TO_2,
+ ZYDIS_BROADCAST_MODE_1_TO_4,
+ ZYDIS_BROADCAST_MODE_1_TO_8
+ }
+ };
+ context->cd8_scale = scales[evex_b][vector_length];
+ instruction->avx.broadcast.mode = broadcasts[evex_b][vector_length];
+ break;
+ }
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ } else
+ {
+ ZYAN_ASSERT(instruction->raw.modrm.mod == 3);
+ }
+
+ // Static broadcast-factor
+ if (def->broadcast)
+ {
+ ZYAN_ASSERT(!instruction->avx.broadcast.mode);
+ instruction->avx.broadcast.is_static = ZYAN_TRUE;
+ static const ZydisBroadcastMode broadcasts[ZYDIS_EVEX_STATIC_BROADCAST_MAX_VALUE + 1] =
+ {
+ ZYDIS_BROADCAST_MODE_INVALID,
+ ZYDIS_BROADCAST_MODE_1_TO_2,
+ ZYDIS_BROADCAST_MODE_1_TO_4,
+ ZYDIS_BROADCAST_MODE_1_TO_8,
+ ZYDIS_BROADCAST_MODE_1_TO_16,
+ ZYDIS_BROADCAST_MODE_1_TO_32,
+ ZYDIS_BROADCAST_MODE_1_TO_64,
+ ZYDIS_BROADCAST_MODE_2_TO_4,
+ ZYDIS_BROADCAST_MODE_2_TO_8,
+ ZYDIS_BROADCAST_MODE_2_TO_16,
+ ZYDIS_BROADCAST_MODE_4_TO_8,
+ ZYDIS_BROADCAST_MODE_4_TO_16,
+ ZYDIS_BROADCAST_MODE_8_TO_16
+ };
+ ZYAN_ASSERT(def->broadcast < ZYAN_ARRAY_LENGTH(broadcasts));
+ instruction->avx.broadcast.mode = broadcasts[def->broadcast];
+ }
+
+ // Rounding mode and SAE
+ if (instruction->raw.evex.b)
+ {
+ switch (def->functionality)
+ {
+ case ZYDIS_EVEX_FUNC_INVALID:
+ case ZYDIS_EVEX_FUNC_BC:
+ // Noting to do here
+ break;
+ case ZYDIS_EVEX_FUNC_RC:
+ instruction->avx.rounding.mode = ZYDIS_ROUNDING_MODE_RN + context->vector_unified.LL;
+ ZYAN_FALLTHROUGH;
+ case ZYDIS_EVEX_FUNC_SAE:
+ instruction->avx.has_sae = ZYAN_TRUE;
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ }
+
+ // Mask
+ instruction->avx.mask.reg = ZYDIS_REGISTER_K0 + instruction->raw.evex.aaa;
+ switch (def->mask_override)
+ {
+ case ZYDIS_MASK_OVERRIDE_DEFAULT:
+ instruction->avx.mask.mode = ZYDIS_MASK_MODE_MERGING + instruction->raw.evex.z;
+ break;
+ case ZYDIS_MASK_OVERRIDE_ZEROING:
+ instruction->avx.mask.mode = ZYDIS_MASK_MODE_ZEROING;
+ break;
+ case ZYDIS_MASK_OVERRIDE_CONTROL:
+ instruction->avx.mask.mode = ZYDIS_MASK_MODE_CONTROL + instruction->raw.evex.z;
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ if (!instruction->raw.evex.aaa)
+ {
+ instruction->avx.mask.mode = ZYDIS_MASK_MODE_DISABLED;
+ }
+#else
+ ZYAN_UNREACHABLE;
+#endif
+ break;
+ }
+ case ZYDIS_INSTRUCTION_ENCODING_MVEX:
+ {
+#ifndef ZYDIS_DISABLE_KNC
+ // Vector length
+ instruction->avx.vector_length = 512;
+
+ const ZydisInstructionDefinitionMVEX* def =
+ (const ZydisInstructionDefinitionMVEX*)definition;
+
+ // Static broadcast-factor
+ ZyanU8 index = def->has_element_granularity;
+ ZYAN_ASSERT(!index || !def->broadcast);
+ if (!index && def->broadcast)
+ {
+ instruction->avx.broadcast.is_static = ZYAN_TRUE;
+ switch (def->broadcast)
+ {
+ case ZYDIS_MVEX_STATIC_BROADCAST_1_TO_8:
+ instruction->avx.broadcast.mode = ZYDIS_BROADCAST_MODE_1_TO_8;
+ index = 1;
+ break;
+ case ZYDIS_MVEX_STATIC_BROADCAST_1_TO_16:
+ instruction->avx.broadcast.mode = ZYDIS_BROADCAST_MODE_1_TO_16;
+ index = 1;
+ break;
+ case ZYDIS_MVEX_STATIC_BROADCAST_4_TO_8:
+ instruction->avx.broadcast.mode = ZYDIS_BROADCAST_MODE_4_TO_8;
+ index = 2;
+ break;
+ case ZYDIS_MVEX_STATIC_BROADCAST_4_TO_16:
+ instruction->avx.broadcast.mode = ZYDIS_BROADCAST_MODE_4_TO_16;
+ index = 2;
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ }
+
+ // Compressed disp8 scale and broadcast-factor
+ switch (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:
+ // Nothing to do here
+ break;
+ case ZYDIS_MVEX_FUNC_F_32:
+ case ZYDIS_MVEX_FUNC_I_32:
+ case ZYDIS_MVEX_FUNC_F_64:
+ case ZYDIS_MVEX_FUNC_I_64:
+ context->cd8_scale = 64;
+ break;
+ 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] =
+ {
+ { 64, 4, 16, 32, 16, 16, 32, 32 },
+ { 4, 0, 0, 2, 1, 1, 2, 2 },
+ { 16, 0, 0, 8, 4, 4, 8, 8 }
+ };
+ ZYAN_ASSERT(instruction->raw.mvex.SSS < ZYAN_ARRAY_LENGTH(lookup[index]));
+ context->cd8_scale = lookup[index][instruction->raw.mvex.SSS];
+ break;
+ }
+ 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] =
+ {
+ { 64, 4, 16, 0, 16, 16, 32, 32 },
+ { 4, 0, 0, 0, 1, 1, 2, 2 },
+ { 16, 0, 0, 0, 4, 4, 8, 8 }
+ };
+ ZYAN_ASSERT(instruction->raw.mvex.SSS < ZYAN_ARRAY_LENGTH(lookup[index]));
+ context->cd8_scale = lookup[index][instruction->raw.mvex.SSS];
+ break;
+ }
+ 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] =
+ {
+ { 64, 8, 32 },
+ { 8, 0, 0 },
+ { 32, 0, 0 }
+ };
+ ZYAN_ASSERT(instruction->raw.mvex.SSS < ZYAN_ARRAY_LENGTH(lookup[index]));
+ context->cd8_scale = lookup[index][instruction->raw.mvex.SSS];
+ break;
+ }
+ case ZYDIS_MVEX_FUNC_DF_32:
+ case ZYDIS_MVEX_FUNC_DI_32:
+ {
+ static const ZyanU8 lookup[2][8] =
+ {
+ { 64, 0, 0, 32, 16, 16, 32, 32 },
+ { 4, 0, 0, 2, 1, 1, 2, 2 }
+ };
+ ZYAN_ASSERT(index < 2);
+ ZYAN_ASSERT(instruction->raw.mvex.SSS < ZYAN_ARRAY_LENGTH(lookup[index]));
+ context->cd8_scale = lookup[index][instruction->raw.mvex.SSS];
+ break;
+ }
+ case ZYDIS_MVEX_FUNC_DF_64:
+ case ZYDIS_MVEX_FUNC_DI_64:
+ {
+ static const ZyanU8 lookup[2][1] =
+ {
+ { 64 },
+ { 8 }
+ };
+ ZYAN_ASSERT(index < 2);
+ ZYAN_ASSERT(instruction->raw.mvex.SSS < ZYAN_ARRAY_LENGTH(lookup[index]));
+ context->cd8_scale = lookup[index][instruction->raw.mvex.SSS];
+ break;
+ }
+ default:
+ ZYAN_UNREACHABLE;
+ }
+
+ // Rounding mode, sae, swizzle, convert
+ context->mvex.functionality = def->functionality;
+ switch (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:
+ // Nothing to do here
+ break;
+ case ZYDIS_MVEX_FUNC_RC:
+ instruction->avx.rounding.mode = ZYDIS_ROUNDING_MODE_RN + (instruction->raw.mvex.SSS & 3);
+ ZYAN_FALLTHROUGH;
+ case ZYDIS_MVEX_FUNC_SAE:
+ if (instruction->raw.mvex.SSS >= 4)
+ {
+ instruction->avx.has_sae = ZYAN_TRUE;
+ }
+ break;
+ case ZYDIS_MVEX_FUNC_SWIZZLE_32:
+ case ZYDIS_MVEX_FUNC_SWIZZLE_64:
+ instruction->avx.swizzle.mode = ZYDIS_SWIZZLE_MODE_DCBA + instruction->raw.mvex.SSS;
+ break;
+ case ZYDIS_MVEX_FUNC_SF_32:
+ case ZYDIS_MVEX_FUNC_SF_32_BCST:
+ case ZYDIS_MVEX_FUNC_SF_32_BCST_4TO16:
+ switch (instruction->raw.mvex.SSS)
+ {
+ case 0:
+ break;
+ case 1:
+ instruction->avx.broadcast.mode = ZYDIS_BROADCAST_MODE_1_TO_16;
+ break;
+ case 2:
+ instruction->avx.broadcast.mode = ZYDIS_BROADCAST_MODE_4_TO_16;
+ break;
+ case 3:
+ instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_FLOAT16;
+ break;
+ case 4:
+ instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_UINT8;
+ break;
+ case 5:
+ instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_SINT8;
+ break;
+ case 6:
+ instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_UINT16;
+ break;
+ case 7:
+ instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_SINT16;
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ break;
+ case ZYDIS_MVEX_FUNC_SI_32:
+ case ZYDIS_MVEX_FUNC_SI_32_BCST:
+ case ZYDIS_MVEX_FUNC_SI_32_BCST_4TO16:
+ switch (instruction->raw.mvex.SSS)
+ {
+ case 0:
+ break;
+ case 1:
+ instruction->avx.broadcast.mode = ZYDIS_BROADCAST_MODE_1_TO_16;
+ break;
+ case 2:
+ instruction->avx.broadcast.mode = ZYDIS_BROADCAST_MODE_4_TO_16;
+ break;
+ case 4:
+ instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_UINT8;
+ break;
+ case 5:
+ instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_SINT8;
+ break;
+ case 6:
+ instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_UINT16;
+ break;
+ case 7:
+ instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_SINT16;
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ break;
+ case ZYDIS_MVEX_FUNC_SF_64:
+ case ZYDIS_MVEX_FUNC_SI_64:
+ switch (instruction->raw.mvex.SSS)
+ {
+ case 0:
+ break;
+ case 1:
+ instruction->avx.broadcast.mode = ZYDIS_BROADCAST_MODE_1_TO_8;
+ break;
+ case 2:
+ instruction->avx.broadcast.mode = ZYDIS_BROADCAST_MODE_4_TO_8;
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ break;
+ case ZYDIS_MVEX_FUNC_UF_32:
+ case ZYDIS_MVEX_FUNC_DF_32:
+ switch (instruction->raw.mvex.SSS)
+ {
+ case 0:
+ break;
+ case 3:
+ instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_FLOAT16;
+ break;
+ case 4:
+ instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_UINT8;
+ break;
+ case 5:
+ instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_SINT8;
+ break;
+ case 6:
+ instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_UINT16;
+ break;
+ case 7:
+ instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_SINT16;
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ break;
+ case ZYDIS_MVEX_FUNC_UF_64:
+ case ZYDIS_MVEX_FUNC_DF_64:
+ break;
+ case ZYDIS_MVEX_FUNC_UI_32:
+ case ZYDIS_MVEX_FUNC_DI_32:
+ switch (instruction->raw.mvex.SSS)
+ {
+ case 0:
+ break;
+ case 4:
+ instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_UINT8;
+ break;
+ case 5:
+ instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_SINT8;
+ break;
+ case 6:
+ instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_UINT16;
+ break;
+ case 7:
+ instruction->avx.conversion.mode = ZYDIS_CONVERSION_MODE_SINT16;
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ break;
+ case ZYDIS_MVEX_FUNC_UI_64:
+ case ZYDIS_MVEX_FUNC_DI_64:
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+
+ // Eviction hint
+ if ((instruction->raw.modrm.mod != 3) && instruction->raw.mvex.E)
+ {
+ instruction->avx.has_eviction_hint = ZYAN_TRUE;
+ }
+
+ // Mask
+ instruction->avx.mask.mode = ZYDIS_MASK_MODE_MERGING;
+ instruction->avx.mask.reg = ZYDIS_REGISTER_K0 + instruction->raw.mvex.kkk;
+#else
+ ZYAN_UNREACHABLE;
+#endif
+ break;
+ }
+ default:
+ // Nothing to do here
+ break;
+ }
+}
+#endif
+
+/* ---------------------------------------------------------------------------------------------- */
+/* Physical instruction decoding */
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Collects optional instruction prefixes.
+ *
+ * @param state A pointer to the `ZydisDecoderState` struct.
+ * @param instruction A pointer to the `ZydisDecodedInstruction` struct.
+ *
+ * @return A zyan status code.
+ *
+ * This function sets the corresponding flag for each prefix and automatically decodes the last
+ * `REX`-prefix (if exists).
+ */
+static ZyanStatus ZydisCollectOptionalPrefixes(ZydisDecoderState* state,
+ ZydisDecodedInstruction* instruction)
+{
+ ZYAN_ASSERT(state);
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(instruction->raw.prefix_count == 0);
+
+ ZyanU8 rex = 0x00;
+ ZyanU8 offset = 0;
+ ZyanBool done = ZYAN_FALSE;
+ do
+ {
+ ZyanU8 prefix_byte;
+ ZYAN_CHECK(ZydisInputPeek(state, instruction, &prefix_byte));
+ switch (prefix_byte)
+ {
+ case 0xF0:
+ state->prefixes.has_lock = ZYAN_TRUE;
+ state->prefixes.offset_lock = offset;
+ break;
+ case 0xF2:
+ ZYAN_FALLTHROUGH;
+ case 0xF3:
+ state->prefixes.group1 = prefix_byte;
+ state->prefixes.mandatory_candidate = prefix_byte;
+ state->prefixes.offset_group1 = offset;
+ state->prefixes.offset_mandatory = offset;
+ break;
+ case 0x2E:
+ ZYAN_FALLTHROUGH;
+ case 0x36:
+ ZYAN_FALLTHROUGH;
+ case 0x3E:
+ ZYAN_FALLTHROUGH;
+ case 0x26:
+ if (state->decoder->machine_mode == ZYDIS_MACHINE_MODE_LONG_64)
+ {
+ if ((prefix_byte == 0x3E) &&
+ (state->prefixes.effective_segment != 0x64) &&
+ (state->prefixes.effective_segment != 0x65))
+ {
+ state->prefixes.offset_notrack = offset;
+ }
+ state->prefixes.group2 = prefix_byte;
+ state->prefixes.offset_group2 = offset;
+ break;
+ }
+ ZYAN_FALLTHROUGH;
+ case 0x64:
+ ZYAN_FALLTHROUGH;
+ case 0x65:
+ state->prefixes.group2 = prefix_byte;
+ state->prefixes.offset_group2 = offset;
+ state->prefixes.effective_segment = prefix_byte;
+ state->prefixes.offset_segment = offset;
+ state->prefixes.offset_notrack = -1;
+ break;
+ case 0x66:
+ // context->prefixes.has_osz_override = ZYAN_TRUE;
+ state->prefixes.offset_osz_override = offset;
+ if (!state->prefixes.mandatory_candidate)
+ {
+ state->prefixes.mandatory_candidate = 0x66;
+ state->prefixes.offset_mandatory = offset;
+ }
+ instruction->attributes |= ZYDIS_ATTRIB_HAS_OPERANDSIZE;
+ break;
+ case 0x67:
+ // context->prefixes.has_asz_override = ZYAN_TRUE;
+ state->prefixes.offset_asz_override = offset;
+ instruction->attributes |= ZYDIS_ATTRIB_HAS_ADDRESSSIZE;
+ break;
+ default:
+ if ((state->decoder->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) &&
+ (prefix_byte & 0xF0) == 0x40)
+ {
+ rex = prefix_byte;
+ instruction->raw.rex.offset = offset;
+ } else
+ {
+ done = ZYAN_TRUE;
+ }
+ break;
+ }
+ if (!done)
+ {
+ // Invalidate `REX`, if it's not the last legacy prefix
+ if (rex && (rex != prefix_byte))
+ {
+ rex = 0x00;
+ instruction->raw.rex.offset = 0;
+ }
+ instruction->raw.prefixes[instruction->raw.prefix_count++].value = prefix_byte;
+ ZydisInputSkip(state, instruction);
+ ++offset;
+ }
+ } while (!done);
+
+ if (instruction->attributes & ZYDIS_ATTRIB_HAS_OPERANDSIZE)
+ {
+ instruction->raw.prefixes[state->prefixes.offset_osz_override].type =
+ ZYDIS_PREFIX_TYPE_EFFECTIVE;
+ }
+ if (instruction->attributes & ZYDIS_ATTRIB_HAS_ADDRESSSIZE)
+ {
+ instruction->raw.prefixes[state->prefixes.offset_asz_override].type =
+ ZYDIS_PREFIX_TYPE_EFFECTIVE;
+ }
+ if (rex)
+ {
+ instruction->raw.prefixes[instruction->raw.rex.offset].type = ZYDIS_PREFIX_TYPE_EFFECTIVE;
+ ZydisDecodeREX(state->context, instruction, rex);
+ }
+ if ((state->decoder->machine_mode != ZYDIS_MACHINE_MODE_LONG_64) &&
+ (state->prefixes.group2 == 0x3E))
+ {
+ state->prefixes.offset_notrack = state->prefixes.offset_group2;
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/**
+ * Decodes optional instruction parts like the ModRM byte, the SIB byte and
+ * additional displacements and/or immediate values.
+ *
+ * @param state A pointer to the `ZydisDecoderState` struct.
+ * @param instruction A pointer to the `ZydisDecodedInstruction` struct.
+ * @param info A pointer to the `ZydisInstructionEncodingInfo` struct.
+ *
+ * @return A zyan status code.
+ */
+static ZyanStatus ZydisDecodeOptionalInstructionParts(ZydisDecoderState* state,
+ ZydisDecodedInstruction* instruction, const ZydisInstructionEncodingInfo* info)
+{
+ ZYAN_ASSERT(state);
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(info);
+
+ ZydisDecoderContext* context = state->context;
+
+ if (info->flags & ZYDIS_INSTR_ENC_FLAG_HAS_MODRM)
+ {
+ if (!instruction->raw.modrm.offset)
+ {
+ instruction->raw.modrm.offset = instruction->length;
+ ZyanU8 modrm_byte;
+ ZYAN_CHECK(ZydisInputNext(state, instruction, &modrm_byte));
+ ZydisDecodeModRM(instruction, modrm_byte);
+ }
+
+ if (!(info->flags & ZYDIS_INSTR_ENC_FLAG_FORCE_REG_FORM))
+ {
+ ZyanU8 has_sib = 0;
+ ZyanU8 displacement_size = 0;
+ switch (instruction->address_width)
+ {
+ case 16:
+ switch (instruction->raw.modrm.mod)
+ {
+ case 0:
+ if (instruction->raw.modrm.rm == 6)
+ {
+ displacement_size = 16;
+ }
+ break;
+ case 1:
+ displacement_size = 8;
+ break;
+ case 2:
+ displacement_size = 16;
+ break;
+ case 3:
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ break;
+ case 32:
+ case 64:
+ has_sib =
+ (instruction->raw.modrm.mod != 3) && (instruction->raw.modrm.rm == 4);
+ switch (instruction->raw.modrm.mod)
+ {
+ case 0:
+ if (instruction->raw.modrm.rm == 5)
+ {
+ if (instruction->machine_mode == ZYDIS_MACHINE_MODE_LONG_64)
+ {
+ instruction->attributes |= ZYDIS_ATTRIB_IS_RELATIVE;
+ }
+ displacement_size = 32;
+ }
+ break;
+ case 1:
+ displacement_size = 8;
+ break;
+ case 2:
+ displacement_size = 32;
+ break;
+ case 3:
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ if (has_sib)
+ {
+ instruction->raw.sib.offset = instruction->length;
+ ZyanU8 sib_byte;
+ ZYAN_CHECK(ZydisInputNext(state, instruction, &sib_byte));
+ ZydisDecodeSIB(instruction, sib_byte);
+ if (instruction->raw.sib.base == 5)
+ {
+ displacement_size = (instruction->raw.modrm.mod == 1) ? 8 : 32;
+ }
+ }
+ if (displacement_size)
+ {
+ ZYAN_CHECK(ZydisReadDisplacement(state, instruction, displacement_size));
+ }
+ }
+
+ context->reg_info.is_mod_reg = (instruction->raw.modrm.mod == 3) ||
+ (info->flags & ZYDIS_INSTR_ENC_FLAG_FORCE_REG_FORM);
+ }
+
+ if (info->flags & ZYDIS_INSTR_ENC_FLAG_HAS_DISP)
+ {
+ ZYAN_CHECK(ZydisReadDisplacement(
+ state, instruction, info->disp.size[context->easz_index]));
+ }
+
+ if (info->flags & ZYDIS_INSTR_ENC_FLAG_HAS_IMM0)
+ {
+ if (info->imm[0].is_relative)
+ {
+ instruction->attributes |= ZYDIS_ATTRIB_IS_RELATIVE;
+ }
+ ZYAN_CHECK(ZydisReadImmediate(state, instruction, 0,
+ info->imm[0].size[context->eosz_index], info->imm[0].is_signed,
+ info->imm[0].is_relative));
+ }
+
+ if (info->flags & ZYDIS_INSTR_ENC_FLAG_HAS_IMM1)
+ {
+ ZYAN_ASSERT(!(info->flags & ZYDIS_INSTR_ENC_FLAG_HAS_DISP));
+ ZYAN_CHECK(ZydisReadImmediate(state, instruction, 1,
+ info->imm[1].size[context->eosz_index], info->imm[1].is_signed,
+ info->imm[1].is_relative));
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Sets the effective operand size for the given instruction.
+ *
+ * @param context A pointer to the `ZydisDecoderContext` struct
+ * @param instruction A pointer to the `ZydisDecodedInstruction` struct.
+ * @param definition A pointer to the `ZydisInstructionDefinition` struct.
+ */
+static void ZydisSetEffectiveOperandWidth(ZydisDecoderContext* context,
+ ZydisDecodedInstruction* instruction, const ZydisInstructionDefinition* definition)
+{
+ ZYAN_ASSERT(context);
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(definition);
+
+ static const ZyanU8 operand_size_map[8][8] =
+ {
+ // Default for most instructions
+ {
+ 16, // 16 __ W0
+ 32, // 16 66 W0
+ 32, // 32 __ W0
+ 16, // 32 66 W0
+ 32, // 64 __ W0
+ 16, // 64 66 W0
+ 64, // 64 __ W1
+ 64 // 64 66 W1
+ },
+ // Operand size is forced to 8-bit (this is done later to preserve the `eosz_index`)
+ {
+ 16, // 16 __ W0
+ 32, // 16 66 W0
+ 32, // 32 __ W0
+ 16, // 32 66 W0
+ 32, // 64 __ W0
+ 16, // 64 66 W0
+ 64, // 64 __ W1
+ 64 // 64 66 W1
+ },
+ // Operand size override 0x66 is ignored
+ {
+ 16, // 16 __ W0
+ 16, // 16 66 W0
+ 32, // 32 __ W0
+ 32, // 32 66 W0
+ 32, // 64 __ W0
+ 32, // 64 66 W0
+ 64, // 64 __ W1
+ 64 // 64 66 W1
+ },
+ // REX.W promotes to 32-bit instead of 64-bit
+ {
+ 16, // 16 __ W0
+ 32, // 16 66 W0
+ 32, // 32 __ W0
+ 16, // 32 66 W0
+ 32, // 64 __ W0
+ 16, // 64 66 W0
+ 32, // 64 __ W1
+ 32 // 64 66 W1
+ },
+ // Operand size defaults to 64-bit in 64-bit mode
+ {
+ 16, // 16 __ W0
+ 32, // 16 66 W0
+ 32, // 32 __ W0
+ 16, // 32 66 W0
+ 64, // 64 __ W0
+ 16, // 64 66 W0
+ 64, // 64 __ W1
+ 64 // 64 66 W1
+ },
+ // Operand size is forced to 64-bit in 64-bit mode
+ {
+ 16, // 16 __ W0
+ 32, // 16 66 W0
+ 32, // 32 __ W0
+ 16, // 32 66 W0
+ 64, // 64 __ W0
+ 64, // 64 66 W0
+ 64, // 64 __ W1
+ 64 // 64 66 W1
+ },
+ // Operand size is forced to 32-bit, if no REX.W is present.
+ {
+ 32, // 16 __ W0
+ 32, // 16 66 W0
+ 32, // 32 __ W0
+ 32, // 32 66 W0
+ 32, // 64 __ W0
+ 32, // 64 66 W0
+ 64, // 64 __ W1
+ 64 // 64 66 W1
+ },
+ // Operand size is forced to 64-bit in 64-bit mode and forced to 32-bit in all other modes.
+ // This is used for e.g. `mov CR, GPR` and `mov GPR, CR`.
+ {
+ 32, // 16 __ W0
+ 32, // 16 66 W0
+ 32, // 32 __ W0
+ 32, // 32 66 W0
+ 64, // 64 __ W0
+ 64, // 64 66 W0
+ 64, // 64 __ W1
+ 64 // 64 66 W1
+ }
+ };
+
+ ZyanU8 index = (instruction->attributes & ZYDIS_ATTRIB_HAS_OPERANDSIZE) ? 1 : 0;
+ if ((instruction->machine_mode == ZYDIS_MACHINE_MODE_LONG_COMPAT_32) ||
+ (instruction->machine_mode == ZYDIS_MACHINE_MODE_LEGACY_32))
+ {
+ index += 2;
+ }
+ else if (instruction->machine_mode == ZYDIS_MACHINE_MODE_LONG_64)
+ {
+ index += 4;
+ index += (context->vector_unified.W & 0x01) << 1;
+ }
+
+ ZYAN_ASSERT(definition->operand_size_map < ZYAN_ARRAY_LENGTH(operand_size_map));
+ ZYAN_ASSERT(index < ZYAN_ARRAY_LENGTH(operand_size_map[definition->operand_size_map]));
+
+ instruction->operand_width = operand_size_map[definition->operand_size_map][index];
+ context->eosz_index = instruction->operand_width >> 5;
+
+ // TODO: Cleanup code and remove hardcoded condition
+ if (definition->operand_size_map == 1)
+ {
+ instruction->operand_width = 8;
+ }
+}
+
+/**
+ * Sets the effective address width for the given instruction.
+ *
+ * @param context A pointer to the `ZydisDecoderContext` struct.
+ * @param instruction A pointer to the `ZydisDecodedInstruction` struct.
+ * @param definition A pointer to the `ZydisInstructionDefinition` struct.
+ */
+static void ZydisSetEffectiveAddressWidth(ZydisDecoderContext* context,
+ ZydisDecodedInstruction* instruction, const ZydisInstructionDefinition* definition)
+{
+ ZYAN_ASSERT(context);
+ ZYAN_ASSERT(instruction);
+
+ static const ZyanU8 address_size_map[3][8] =
+ {
+ // Default for most instructions
+ {
+ 16, // 16 __
+ 32, // 16 67
+ 32, // 32 __
+ 16, // 32 67
+ 64, // 64 __
+ 32 // 64 67
+ },
+ // The address-size override is ignored
+ {
+ 16, // 16 __
+ 16, // 16 67
+ 32, // 32 __
+ 32, // 32 67
+ 64, // 64 __
+ 64 // 64 67
+ },
+ // The address-size is forced to 64-bit in 64-bit mode and 32-bit in non 64-bit mode. This
+ // is used by e.g. `ENCLS`, `ENCLV`, `ENCLU`.
+ {
+ 32, // 16 __
+ 32, // 16 67
+ 32, // 32 __
+ 32, // 32 67
+ 64, // 64 __
+ 64 // 64 67
+ }
+ };
+
+ ZyanU8 index = (instruction->attributes & ZYDIS_ATTRIB_HAS_ADDRESSSIZE) ? 1 : 0;
+ if ((instruction->machine_mode == ZYDIS_MACHINE_MODE_LONG_COMPAT_32) ||
+ (instruction->machine_mode == ZYDIS_MACHINE_MODE_LEGACY_32))
+ {
+ index += 2;
+ }
+ else if (instruction->machine_mode == ZYDIS_MACHINE_MODE_LONG_64)
+ {
+ index += 4;
+ }
+
+ ZYAN_ASSERT(definition->address_size_map < ZYAN_ARRAY_LENGTH(address_size_map));
+ ZYAN_ASSERT(index < ZYAN_ARRAY_LENGTH(address_size_map[definition->address_size_map]));
+
+ instruction->address_width = address_size_map[definition->address_size_map][index];
+ context->easz_index = instruction->address_width >> 5;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+
+static ZyanStatus ZydisNodeHandlerXOP(const ZydisDecodedInstruction* instruction, ZyanU16* index)
+{
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(index);
+
+ switch (instruction->encoding)
+ {
+ case ZYDIS_INSTRUCTION_ENCODING_LEGACY:
+ *index = 0;
+ break;
+ case ZYDIS_INSTRUCTION_ENCODING_XOP:
+ ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_XOP);
+ *index = (instruction->raw.xop.m_mmmm - 0x08) + (instruction->raw.xop.pp * 3) + 1;
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ return ZYAN_STATUS_SUCCESS;
+}
+
+static ZyanStatus ZydisNodeHandlerVEX(const ZydisDecodedInstruction* instruction, ZyanU16* index)
+{
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(index);
+
+ switch (instruction->encoding)
+ {
+ case ZYDIS_INSTRUCTION_ENCODING_LEGACY:
+ *index = 0;
+ break;
+ case ZYDIS_INSTRUCTION_ENCODING_VEX:
+ ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_VEX);
+ *index = instruction->raw.vex.m_mmmm + (instruction->raw.vex.pp << 2) + 1;
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ return ZYAN_STATUS_SUCCESS;
+}
+
+static ZyanStatus ZydisNodeHandlerEMVEX(const ZydisDecodedInstruction* instruction, ZyanU16* index)
+{
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(index);
+
+ switch (instruction->encoding)
+ {
+ case ZYDIS_INSTRUCTION_ENCODING_LEGACY:
+ *index = 0;
+ break;
+ case ZYDIS_INSTRUCTION_ENCODING_EVEX:
+ ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_EVEX);
+ *index = instruction->raw.evex.mmm + (instruction->raw.evex.pp << 3) + 1;
+ break;
+ case ZYDIS_INSTRUCTION_ENCODING_MVEX:
+ ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_MVEX);
+ *index = instruction->raw.mvex.mmmm + (instruction->raw.mvex.pp << 2) + 33;
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ return ZYAN_STATUS_SUCCESS;
+}
+
+static ZyanStatus ZydisNodeHandlerOpcode(ZydisDecoderState* state,
+ ZydisDecodedInstruction* instruction, ZyanU16* index)
+{
+ ZYAN_ASSERT(state);
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(index);
+
+ // Handle possible encoding-prefix and opcode-map changes
+ switch (instruction->encoding)
+ {
+ case ZYDIS_INSTRUCTION_ENCODING_LEGACY:
+ ZYAN_CHECK(ZydisInputNext(state, instruction, &instruction->opcode));
+ switch (instruction->opcode_map)
+ {
+ case ZYDIS_OPCODE_MAP_DEFAULT:
+ switch (instruction->opcode)
+ {
+ case 0x0F:
+ instruction->opcode_map = ZYDIS_OPCODE_MAP_0F;
+ break;
+ case 0xC4:
+ case 0xC5:
+ case 0x62:
+ {
+ ZyanU8 next_input;
+ ZYAN_CHECK(ZydisInputPeek(state, instruction, &next_input));
+ if (((next_input & 0xF0) >= 0xC0) ||
+ (instruction->machine_mode == ZYDIS_MACHINE_MODE_LONG_64))
+ {
+ if (instruction->attributes & ZYDIS_ATTRIB_HAS_REX)
+ {
+ return ZYDIS_STATUS_ILLEGAL_REX;
+ }
+ if (state->prefixes.has_lock)
+ {
+ return ZYDIS_STATUS_ILLEGAL_LOCK;
+ }
+ if (state->prefixes.mandatory_candidate)
+ {
+ return ZYDIS_STATUS_ILLEGAL_LEGACY_PFX;
+ }
+ ZyanU8 prefix_bytes[4] = { 0, 0, 0, 0 };
+ prefix_bytes[0] = instruction->opcode;
+ switch (instruction->opcode)
+ {
+ case 0xC4:
+ instruction->raw.vex.offset = instruction->length - 1;
+ // Read additional 3-byte VEX-prefix data
+ ZYAN_ASSERT(!(instruction->attributes & ZYDIS_ATTRIB_HAS_VEX));
+ ZYAN_CHECK(ZydisInputNextBytes(state, instruction, &prefix_bytes[1], 2));
+ break;
+ case 0xC5:
+ instruction->raw.vex.offset = instruction->length - 1;
+ // Read additional 2-byte VEX-prefix data
+ ZYAN_ASSERT(!(instruction->attributes & ZYDIS_ATTRIB_HAS_VEX));
+ ZYAN_CHECK(ZydisInputNext(state, instruction, &prefix_bytes[1]));
+ break;
+ case 0x62:
+#if !defined(ZYDIS_DISABLE_AVX512) || !defined(ZYDIS_DISABLE_KNC)
+ // Read additional EVEX/MVEX-prefix data
+ ZYAN_ASSERT(!(instruction->attributes & ZYDIS_ATTRIB_HAS_EVEX));
+ ZYAN_ASSERT(!(instruction->attributes & ZYDIS_ATTRIB_HAS_MVEX));
+ ZYAN_CHECK(ZydisInputNextBytes(state, instruction, &prefix_bytes[1], 3));
+ break;
+#else
+ return ZYDIS_STATUS_DECODING_ERROR;
+#endif
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ switch (instruction->opcode)
+ {
+ case 0xC4:
+ case 0xC5:
+ // Decode VEX-prefix
+ instruction->encoding = ZYDIS_INSTRUCTION_ENCODING_VEX;
+ ZYAN_CHECK(ZydisDecodeVEX(state->context, instruction, prefix_bytes));
+ instruction->opcode_map =
+ ZYDIS_OPCODE_MAP_DEFAULT + instruction->raw.vex.m_mmmm;
+ break;
+ case 0x62:
+#if defined(ZYDIS_DISABLE_AVX512) && defined(ZYDIS_DISABLE_KNC)
+ return ZYDIS_STATUS_DECODING_ERROR;
+#else
+ switch ((prefix_bytes[2] >> 2) & 0x01)
+ {
+ case 0:
+#ifndef ZYDIS_DISABLE_KNC
+ instruction->raw.mvex.offset = instruction->length - 4;
+ // `KNC` instructions are only valid in 64-bit mode.
+ // This condition catches the `MVEX` encoded ones to save a bunch of
+ // `mode` filters in the data-tables.
+ // `KNC` instructions with `VEX` encoding still require a `mode` filter.
+ if (state->decoder->machine_mode != ZYDIS_MACHINE_MODE_LONG_64)
+ {
+ return ZYDIS_STATUS_DECODING_ERROR;
+ }
+ // Decode MVEX-prefix
+ instruction->encoding = ZYDIS_INSTRUCTION_ENCODING_MVEX;
+ ZYAN_CHECK(ZydisDecodeMVEX(state->context, instruction, prefix_bytes));
+ instruction->opcode_map =
+ ZYDIS_OPCODE_MAP_DEFAULT + instruction->raw.mvex.mmmm;
+ break;
+#else
+ return ZYDIS_STATUS_DECODING_ERROR;
+#endif
+ case 1:
+#ifndef ZYDIS_DISABLE_AVX512
+ instruction->raw.evex.offset = instruction->length - 4;
+ // Decode EVEX-prefix
+ instruction->encoding = ZYDIS_INSTRUCTION_ENCODING_EVEX;
+ ZYAN_CHECK(ZydisDecodeEVEX(state->context, instruction, prefix_bytes));
+ instruction->opcode_map =
+ ZYDIS_OPCODE_MAP_DEFAULT + instruction->raw.evex.mmm;
+ break;
+#else
+ return ZYDIS_STATUS_DECODING_ERROR;
+#endif
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ break;
+#endif
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ }
+ break;
+ }
+ case 0x8F:
+ {
+ ZyanU8 next_input;
+ ZYAN_CHECK(ZydisInputPeek(state, instruction, &next_input));
+ if ((next_input & 0x1F) >= 8)
+ {
+ if (instruction->attributes & ZYDIS_ATTRIB_HAS_REX)
+ {
+ return ZYDIS_STATUS_ILLEGAL_REX;
+ }
+ if (state->prefixes.has_lock)
+ {
+ return ZYDIS_STATUS_ILLEGAL_LOCK;
+ }
+ if (state->prefixes.mandatory_candidate)
+ {
+ return ZYDIS_STATUS_ILLEGAL_LEGACY_PFX;
+ }
+ instruction->raw.xop.offset = instruction->length - 1;
+ ZyanU8 prefixBytes[3] = { 0x8F, 0x00, 0x00 };
+ // Read additional xop-prefix data
+ ZYAN_CHECK(ZydisInputNextBytes(state, instruction, &prefixBytes[1], 2));
+ // Decode xop-prefix
+ instruction->encoding = ZYDIS_INSTRUCTION_ENCODING_XOP;
+ ZYAN_CHECK(ZydisDecodeXOP(state->context, instruction, prefixBytes));
+ instruction->opcode_map =
+ ZYDIS_OPCODE_MAP_XOP8 + instruction->raw.xop.m_mmmm - 0x08;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ break;
+ case ZYDIS_OPCODE_MAP_0F:
+ switch (instruction->opcode)
+ {
+ case 0x0F:
+ if (state->prefixes.has_lock)
+ {
+ return ZYDIS_STATUS_ILLEGAL_LOCK;
+ }
+ instruction->encoding = ZYDIS_INSTRUCTION_ENCODING_3DNOW;
+ instruction->opcode_map = ZYDIS_OPCODE_MAP_0F0F;
+ break;
+ case 0x38:
+ instruction->opcode_map = ZYDIS_OPCODE_MAP_0F38;
+ break;
+ case 0x3A:
+ instruction->opcode_map = ZYDIS_OPCODE_MAP_0F3A;
+ break;
+ default:
+ break;
+ }
+ break;
+ case ZYDIS_OPCODE_MAP_0F38:
+ case ZYDIS_OPCODE_MAP_0F3A:
+ case ZYDIS_OPCODE_MAP_XOP8:
+ case ZYDIS_OPCODE_MAP_XOP9:
+ case ZYDIS_OPCODE_MAP_XOPA:
+ // Nothing to do here
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ break;
+ case ZYDIS_INSTRUCTION_ENCODING_3DNOW:
+ // All 3DNOW (0x0F 0x0F) instructions are using the same operand encoding. We just
+ // decode a random (pi2fw) instruction and extract the actual opcode later.
+ *index = 0x0C;
+ return ZYAN_STATUS_SUCCESS;
+ default:
+ ZYAN_CHECK(ZydisInputNext(state, instruction, &instruction->opcode));
+ break;
+ }
+
+ *index = instruction->opcode;
+ return ZYAN_STATUS_SUCCESS;
+}
+
+static ZyanStatus ZydisNodeHandlerMode(const ZydisDecodedInstruction* instruction, ZyanU16* index)
+{
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(index);
+
+ switch (instruction->machine_mode)
+ {
+ case ZYDIS_MACHINE_MODE_LONG_COMPAT_16:
+ case ZYDIS_MACHINE_MODE_LEGACY_16:
+ case ZYDIS_MACHINE_MODE_REAL_16:
+ *index = 0;
+ break;
+ case ZYDIS_MACHINE_MODE_LONG_COMPAT_32:
+ case ZYDIS_MACHINE_MODE_LEGACY_32:
+ *index = 1;
+ break;
+ case ZYDIS_MACHINE_MODE_LONG_64:
+ *index = 2;
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ return ZYAN_STATUS_SUCCESS;
+}
+
+static ZyanStatus ZydisNodeHandlerModeCompact(const ZydisDecodedInstruction* instruction,
+ ZyanU16* index)
+{
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(index);
+
+ *index = (instruction->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) ? 0 : 1;
+ return ZYAN_STATUS_SUCCESS;
+}
+
+static ZyanStatus ZydisNodeHandlerModrmMod(ZydisDecoderState* state,
+ ZydisDecodedInstruction* instruction, ZyanU16* index)
+{
+ ZYAN_ASSERT(state);
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(index);
+
+ if (!instruction->raw.modrm.offset)
+ {
+ instruction->raw.modrm.offset = instruction->length;
+ ZyanU8 modrm_byte;
+ ZYAN_CHECK(ZydisInputNext(state, instruction, &modrm_byte));
+ ZydisDecodeModRM(instruction, modrm_byte);
+ }
+ *index = instruction->raw.modrm.mod;
+ return ZYAN_STATUS_SUCCESS;
+}
+
+static ZyanStatus ZydisNodeHandlerModrmModCompact(ZydisDecoderState* state,
+ ZydisDecodedInstruction* instruction, ZyanU16* index)
+{
+ ZYAN_CHECK(ZydisNodeHandlerModrmMod(state, instruction, index));
+ *index = (*index == 0x3) ? 0 : 1;
+ return ZYAN_STATUS_SUCCESS;
+}
+
+static ZyanStatus ZydisNodeHandlerModrmReg(ZydisDecoderState* state,
+ ZydisDecodedInstruction* instruction, ZyanU16* index)
+{
+ ZYAN_ASSERT(state);
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(index);
+
+ if (!instruction->raw.modrm.offset)
+ {
+ instruction->raw.modrm.offset = instruction->length;
+ ZyanU8 modrm_byte;
+ ZYAN_CHECK(ZydisInputNext(state, instruction, &modrm_byte));
+ ZydisDecodeModRM(instruction, modrm_byte);
+ }
+ *index = instruction->raw.modrm.reg;
+ return ZYAN_STATUS_SUCCESS;
+}
+
+static ZyanStatus ZydisNodeHandlerModrmRm(ZydisDecoderState* state,
+ ZydisDecodedInstruction* instruction, ZyanU16* index)
+{
+ ZYAN_ASSERT(state);
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(index);
+
+ if (!instruction->raw.modrm.offset)
+ {
+ instruction->raw.modrm.offset = instruction->length;
+ ZyanU8 modrm_byte;
+ ZYAN_CHECK(ZydisInputNext(state, instruction, &modrm_byte));
+ ZydisDecodeModRM(instruction, modrm_byte);
+ }
+ *index = instruction->raw.modrm.rm;
+ return ZYAN_STATUS_SUCCESS;
+}
+
+static ZyanStatus ZydisNodeHandlerMandatoryPrefix(const ZydisDecoderState* state,
+ ZydisDecodedInstruction* instruction, ZyanU16* index)
+{
+ ZYAN_ASSERT(state);
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(index);
+
+ switch (state->prefixes.mandatory_candidate)
+ {
+ case 0x66:
+ instruction->raw.prefixes[state->prefixes.offset_mandatory].type =
+ ZYDIS_PREFIX_TYPE_MANDATORY;
+ instruction->attributes &= ~ZYDIS_ATTRIB_HAS_OPERANDSIZE;
+ *index = 2;
+ break;
+ case 0xF3:
+ instruction->raw.prefixes[state->prefixes.offset_mandatory].type =
+ ZYDIS_PREFIX_TYPE_MANDATORY;
+ *index = 3;
+ break;
+ case 0xF2:
+ instruction->raw.prefixes[state->prefixes.offset_mandatory].type =
+ ZYDIS_PREFIX_TYPE_MANDATORY;
+ *index = 4;
+ break;
+ default:
+ *index = 1;
+ break;
+ }
+ // TODO: Consume prefix and make sure it's available again, if we need to fallback
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+static ZyanStatus ZydisNodeHandlerOperandSize(const ZydisDecoderState* state,
+ ZydisDecodedInstruction* instruction, ZyanU16* index)
+{
+ ZYAN_ASSERT(state);
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(index);
+
+ if ((instruction->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) &&
+ (state->context->vector_unified.W))
+ {
+ *index = 2;
+ } else
+ {
+ if (instruction->attributes & ZYDIS_ATTRIB_HAS_OPERANDSIZE)
+ {
+ instruction->raw.prefixes[state->prefixes.offset_osz_override].type =
+ ZYDIS_PREFIX_TYPE_EFFECTIVE;
+ }
+ switch (instruction->machine_mode)
+ {
+ case ZYDIS_MACHINE_MODE_LONG_COMPAT_16:
+ case ZYDIS_MACHINE_MODE_LEGACY_16:
+ case ZYDIS_MACHINE_MODE_REAL_16:
+ *index = (instruction->attributes & ZYDIS_ATTRIB_HAS_OPERANDSIZE) ? 1 : 0;
+ break;
+ case ZYDIS_MACHINE_MODE_LONG_COMPAT_32:
+ case ZYDIS_MACHINE_MODE_LEGACY_32:
+ case ZYDIS_MACHINE_MODE_LONG_64:
+ *index = (instruction->attributes & ZYDIS_ATTRIB_HAS_OPERANDSIZE) ? 0 : 1;
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+static ZyanStatus ZydisNodeHandlerAddressSize(ZydisDecodedInstruction* instruction, ZyanU16* index)
+{
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(index);
+
+ /*if (instruction->attributes & ZYDIS_ATTRIB_HAS_ADDRESSSIZE)
+ {
+ instruction->raw.prefixes[context->prefixes.offset_asz_override].type =
+ ZYDIS_PREFIX_TYPE_EFFECTIVE;
+ }*/
+ switch (instruction->machine_mode)
+ {
+ case ZYDIS_MACHINE_MODE_LONG_COMPAT_16:
+ case ZYDIS_MACHINE_MODE_LEGACY_16:
+ case ZYDIS_MACHINE_MODE_REAL_16:
+ *index = (instruction->attributes & ZYDIS_ATTRIB_HAS_ADDRESSSIZE) ? 1 : 0;
+ break;
+ case ZYDIS_MACHINE_MODE_LONG_COMPAT_32:
+ case ZYDIS_MACHINE_MODE_LEGACY_32:
+ *index = (instruction->attributes & ZYDIS_ATTRIB_HAS_ADDRESSSIZE) ? 0 : 1;
+ break;
+ case ZYDIS_MACHINE_MODE_LONG_64:
+ *index = (instruction->attributes & ZYDIS_ATTRIB_HAS_ADDRESSSIZE) ? 1 : 2;
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+static ZyanStatus ZydisNodeHandlerVectorLength(const ZydisDecoderContext* context,
+ const ZydisDecodedInstruction* instruction, ZyanU16* index)
+{
+ ZYAN_ASSERT(context);
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(index);
+
+ switch (instruction->encoding)
+ {
+ case ZYDIS_INSTRUCTION_ENCODING_XOP:
+ ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_XOP);
+ break;
+ case ZYDIS_INSTRUCTION_ENCODING_VEX:
+ ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_VEX);
+ break;
+ case ZYDIS_INSTRUCTION_ENCODING_EVEX:
+ ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_EVEX);
+ break;
+ case ZYDIS_INSTRUCTION_ENCODING_MVEX:
+ ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_MVEX);
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+
+ *index = context->vector_unified.LL;
+ if (*index == 3)
+ {
+ return ZYDIS_STATUS_DECODING_ERROR;
+ }
+ return ZYAN_STATUS_SUCCESS;
+}
+
+static ZyanStatus ZydisNodeHandlerRexW(const ZydisDecoderContext* context,
+ const ZydisDecodedInstruction* instruction, ZyanU16* index)
+{
+ ZYAN_ASSERT(context);
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(index);
+
+ switch (instruction->encoding)
+ {
+ case ZYDIS_INSTRUCTION_ENCODING_LEGACY:
+ // nothing to do here
+ break;
+ case ZYDIS_INSTRUCTION_ENCODING_XOP:
+ ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_XOP);
+ break;
+ case ZYDIS_INSTRUCTION_ENCODING_VEX:
+ ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_VEX);
+ break;
+ case ZYDIS_INSTRUCTION_ENCODING_EVEX:
+ ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_EVEX);
+ break;
+ case ZYDIS_INSTRUCTION_ENCODING_MVEX:
+ ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_MVEX);
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ *index = context->vector_unified.W;
+ return ZYAN_STATUS_SUCCESS;
+}
+
+static ZyanStatus ZydisNodeHandlerRexB(const ZydisDecoderContext* context,
+ const ZydisDecodedInstruction* instruction, ZyanU16* index)
+{
+ ZYAN_ASSERT(context);
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(index);
+
+ switch (instruction->encoding)
+ {
+ case ZYDIS_INSTRUCTION_ENCODING_LEGACY:
+ // nothing to do here
+ break;
+ case ZYDIS_INSTRUCTION_ENCODING_XOP:
+ ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_XOP);
+ break;
+ case ZYDIS_INSTRUCTION_ENCODING_VEX:
+ ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_VEX);
+ break;
+ case ZYDIS_INSTRUCTION_ENCODING_EVEX:
+ ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_EVEX);
+ break;
+ case ZYDIS_INSTRUCTION_ENCODING_MVEX:
+ ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_MVEX);
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+ *index = context->vector_unified.B;
+ return ZYAN_STATUS_SUCCESS;
+}
+
+#ifndef ZYDIS_DISABLE_AVX512
+static ZyanStatus ZydisNodeHandlerEvexB(const ZydisDecodedInstruction* instruction, ZyanU16* index)
+{
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(index);
+
+ ZYAN_ASSERT(instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_EVEX);
+ ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_EVEX);
+ *index = instruction->raw.evex.b;
+ return ZYAN_STATUS_SUCCESS;
+}
+#endif
+
+#ifndef ZYDIS_DISABLE_KNC
+static ZyanStatus ZydisNodeHandlerMvexE(const ZydisDecodedInstruction* instruction, ZyanU16* index)
+{
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(index);
+
+ ZYAN_ASSERT(instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_MVEX);
+ ZYAN_ASSERT(instruction->attributes & ZYDIS_ATTRIB_HAS_MVEX);
+ *index = instruction->raw.mvex.E;
+ return ZYAN_STATUS_SUCCESS;
+}
+#endif
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Populates the internal register id fields for `REG`, `RM`, `NDSNDD`, `BASE` and `INDEX`/`VIDX`
+ * encoded operands and performs sanity checks.
+ *
+ * @param context A pointer to the `ZydisDecoderContext` struct.
+ * @param instruction A pointer to the `ZydisDecodedInstruction` struct.
+ * @param def_reg The type definition for the `.reg` encoded operand.
+ * @param def_rm The type definition for the `.rm` encoded operand.
+ * @param def_ndsndd The type definition for the `.vvvv` encoded operand.
+ *
+ * @return A zyan status code.
+ *
+ * This function sets all unused register ids to `-1`. This rule does currently not apply to
+ * `base` and `index`.
+ *
+ * Definition encoding:
+ * - `def_reg` -> `ZydisRegisterKind`
+ * - `def_ndsndd` -> `ZydisRegisterKind`
+ * - `def_rm` -> `ZydisRegisterKind` (`.mod == 3`) or ZydisMemoryOperandType (`.mod != 3`)
+ */
+static ZyanStatus ZydisPopulateRegisterIds(ZydisDecoderContext* context,
+ const ZydisDecodedInstruction* instruction, ZyanU8 def_reg, ZyanU8 def_rm, ZyanU8 def_ndsndd)
+{
+ ZYAN_ASSERT(context);
+ ZYAN_ASSERT(instruction);
+
+ const ZyanBool is_64_bit = (instruction->machine_mode == ZYDIS_MACHINE_MODE_LONG_64);
+ const ZyanBool is_reg = context->reg_info.is_mod_reg;
+ const ZyanBool has_sib = !is_reg && (instruction->raw.modrm.rm == 4);
+ const ZyanBool has_vsib = has_sib && (def_rm == ZYDIS_MEMOP_TYPE_VSIB);
+
+ ZyanU8 id_reg = instruction->raw.modrm.reg;
+ ZyanU8 id_rm = instruction->raw.modrm.rm;
+ ZyanU8 id_ndsndd = is_64_bit ? context->vector_unified.vvvv : context->vector_unified.vvvv & 0x07;
+ ZyanU8 id_base = has_sib ? instruction->raw.sib.base : instruction->raw.modrm.rm;
+ ZyanU8 id_index = instruction->raw.sib.index;
+
+ if (instruction->machine_mode == ZYDIS_MACHINE_MODE_LONG_64)
+ {
+ const ZyanBool is_emvex = (instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_EVEX) ||
+ (instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_MVEX);
+
+ // The `index` extension by `.v'` is only valid for VSIB operands
+ const ZyanU8 vsib_v2 = has_vsib ? context->vector_unified.V2 : 0;
+ // The `rm` extension by `.X` is only valid for EVEX/MVEX instructions
+ const ZyanU8 evex_x = is_emvex ? context->vector_unified.X : 0;
+
+ id_reg |= (context->vector_unified.R2 << 4) | (context->vector_unified.R << 3);
+ id_rm |= (evex_x << 4) | (context->vector_unified.B << 3);
+ id_ndsndd |= (context->vector_unified.V2 << 4) ;
+ id_base |= (context->vector_unified.B << 3);
+ id_index |= (vsib_v2 << 4) | (context->vector_unified.X << 3);
+
+ // The masking emulates the actual CPU behavior and does not verify if the resulting ids
+ // are actually valid for the given register kind.
+
+ static const ZyanU8 mask_reg[ZYDIS_REGKIND_MAX_VALUE + 1] =
+ {
+ /* INVALID */ 0,
+ /* GPR */ (1 << 5) - 1,
+ /* X87 */ (1 << 3) - 1, // ignore `.R`, ignore `.R'`
+ /* MMX */ (1 << 3) - 1, // ignore `.R`, ignore `.R'`
+ /* VR */ (1 << 5) - 1,
+ /* TMM */ (1 << 5) - 1,
+ /* SEGMENT */ (1 << 3) - 1, // ignore `.R`, ignore `.R'`
+ /* TEST */ (1 << 3) - 1, // ignore `.R`, ignore `.R'`
+ /* CONTROL */ (1 << 4) - 1, // ignore `.R'`
+ /* DEBUG */ (1 << 4) - 1, // ignore `.R'`
+ /* MASK */ (1 << 5) - 1,
+ /* BOUND */ (1 << 4) - 1 // ignore `.R'`
+ };
+ id_reg &= mask_reg[def_reg];
+
+ static const ZyanU8 mask_rm[ZYDIS_REGKIND_MAX_VALUE + 1] =
+ {
+ /* INVALID */ 0,
+ /* GPR */ (1 << 4) - 1, // ignore `.X`
+ /* X87 */ (1 << 3) - 1, // ignore `.B`, ignore `.X`
+ /* MMX */ (1 << 3) - 1, // ignore `.B`, ignore `.X`
+ /* VR */ (1 << 5) - 1,
+ /* TMM */ (1 << 4) - 1, // ignore `.X`
+ /* SEGMENT */ (1 << 3) - 1, // ignore `.B`, ignore `.X`
+ /* TEST */ (1 << 3) - 1, // ignore `.B`, ignore `.X`
+ /* CONTROL */ (1 << 4) - 1, // ignore `.X`
+ /* DEBUG */ (1 << 4) - 1, // ignore `.X`
+ /* MASK */ (1 << 3) - 1, // ignore `.B`, ignore `.X`
+ /* BOUND */ (1 << 4) - 1 // ignore `.X`
+ };
+ id_rm &= (is_reg ? mask_rm[def_rm] : 0xFF);
+
+ // Commented out for future reference. Not required at the moment as it's always either
+ // a "take all" or "take nothing" situation.
+
+ //static const ZyanU8 mask_ndsndd[ZYDIS_REGKIND_MAX_VALUE + 1] =
+ //{
+ // /* INVALID */ 0,
+ // /* GPR */ (1 << 5) - 1,
+ // /* X87 */ 0, // never encoded in `.vvvv`
+ // /* MMX */ 0, // never encoded in `.vvvv`
+ // /* VR */ (1 << 5) - 1,
+ // /* TMM */ (1 << 5) - 1,
+ // /* SEGMENT */ 0, // never encoded in `.vvvv`
+ // /* TEST */ 0, // never encoded in `.vvvv`
+ // /* CONTROL */ 0, // never encoded in `.vvvv`
+ // /* DEBUG */ 0, // never encoded in `.vvvv`
+ // /* MASK */ (1 << 5) - 1,
+ // /* BOUND */ 0 // never encoded in `.vvvv`
+ //};
+ }
+
+ // Validate
+
+ // `.vvvv` is not allowed, if the instruction does not encode a NDS/NDD operand
+ if (!def_ndsndd && context->vector_unified.vvvv)
+ {
+ return ZYDIS_STATUS_BAD_REGISTER;
+ }
+ // `.v'` is not allowed, if the instruction does not encode a NDS/NDD or VSIB operand
+ if (!def_ndsndd && !has_vsib && context->vector_unified.V2)
+ {
+ return ZYDIS_STATUS_BAD_REGISTER;
+ }
+
+ static const ZyanU8 available_regs[2][ZYDIS_REGKIND_MAX_VALUE + 1] =
+ {
+ // 16/32 bit mode
+ {
+ /* INVALID */ 255,
+ /* GPR */ 8,
+ /* X87 */ 8,
+ /* MMX */ 8,
+ /* VR */ 8,
+ /* TMM */ 8,
+ /* SEGMENT */ 6,
+ /* TEST */ 8,
+ /* CONTROL */ 8,
+ /* DEBUG */ 8,
+ /* MASK */ 8,
+ /* BOUND */ 4
+ },
+ // 64 bit mode
+ {
+ /* INVALID */ 255,
+ /* GPR */ 16,
+ /* X87 */ 8,
+ /* MMX */ 8,
+ /* VR */ 32,
+ /* TMM */ 8,
+ /* SEGMENT */ 6,
+ /* TEST */ 8,
+ /* CONTROL */ 16,
+ // Attempts to reference DR8..DR15 result in undefined opcode (#UD) exceptions. DR4 and
+ // DR5 are only valid, if the debug extension (DE) flag in CR4 is set. As we can't
+ // check this at runtime we just allow them.
+ /* DEBUG */ 8,
+ /* MASK */ 8,
+ /* BOUND */ 4
+ }
+ };
+
+ if ((id_reg >= available_regs[is_64_bit][def_reg]) ||
+ (id_ndsndd >= available_regs[is_64_bit][def_ndsndd]) ||
+ (is_reg && (id_rm >= available_regs[is_64_bit][def_rm])))
+ {
+ return ZYDIS_STATUS_BAD_REGISTER;
+ }
+
+ ZyanI8 id_cr = -1;
+ if (def_reg == ZYDIS_REGKIND_CONTROL)
+ {
+ id_cr = id_reg;
+ }
+ if (is_reg && (def_rm == ZYDIS_REGKIND_CONTROL))
+ {
+ id_cr = id_rm;
+ }
+ if (id_cr >= 0)
+ {
+ // Attempts to reference CR1, CR5, CR6, CR7, and CR9..CR15 result in undefined opcode (#UD)
+ // exceptions
+ static const ZyanU8 lookup[16] =
+ {
+ 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0
+ };
+ ZYAN_ASSERT((ZyanUSize)id_cr < ZYAN_ARRAY_LENGTH(lookup));
+ if (!lookup[id_cr])
+ {
+ return ZYDIS_STATUS_BAD_REGISTER;
+ }
+ }
+
+ // Assign to context
+
+ context->reg_info.id_reg = def_reg ? id_reg : -1;
+ context->reg_info.id_rm = def_rm && is_reg ? id_rm : -1;
+ context->reg_info.id_ndsndd = def_ndsndd ? id_ndsndd : -1;
+ context->reg_info.id_base = id_base; // TODO: Set unused register to -1 as well
+ context->reg_info.id_index = id_index; // TODO: Set unused register to -1 as well
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/**
+ * Checks for certain post-decode error-conditions.
+ *
+ * @param state A pointer to the `ZydisDecoderState` struct.
+ * @param instruction A pointer to the `ZydisDecodedInstruction` struct.
+ * @param definition A pointer to the `ZydisInstructionDefinition` struct.
+ *
+ * @return A zyan status code.
+ *
+ * This function is called immediately after a valid instruction-definition was found.
+ */
+static ZyanStatus ZydisCheckErrorConditions(ZydisDecoderState* state,
+ const ZydisDecodedInstruction* instruction, const ZydisInstructionDefinition* definition)
+{
+ ZYAN_ASSERT(state);
+ ZYAN_ASSERT(instruction);
+ ZYAN_ASSERT(definition);
+
+ ZyanU8 def_reg = definition->op_reg;
+ ZyanU8 def_rm = definition->op_rm;
+ ZyanU8 def_ndsndd = ZYDIS_REGKIND_INVALID;
+ ZyanBool is_gather = ZYAN_FALSE;
+ ZyanBool no_source_dest_match = ZYAN_FALSE;
+ ZyanBool no_source_source_match = ZYAN_FALSE;
+#if !defined(ZYDIS_DISABLE_AVX512) || !defined(ZYDIS_DISABLE_KNC)
+ ZydisMaskPolicy mask_policy = ZYDIS_MASK_POLICY_INVALID;
+#endif
+
+ switch (instruction->encoding)
+ {
+ case ZYDIS_INSTRUCTION_ENCODING_LEGACY:
+ {
+ const ZydisInstructionDefinitionLEGACY* def =
+ (const ZydisInstructionDefinitionLEGACY*)definition;
+
+ if (def->requires_protected_mode &&
+ (instruction->machine_mode == ZYDIS_MACHINE_MODE_REAL_16))
+ {
+ return ZYDIS_STATUS_DECODING_ERROR;
+ }
+
+ if (def->no_compat_mode &&
+ ((instruction->machine_mode == ZYDIS_MACHINE_MODE_LONG_COMPAT_16) ||
+ (instruction->machine_mode == ZYDIS_MACHINE_MODE_LONG_COMPAT_32)))
+ {
+ return ZYDIS_STATUS_DECODING_ERROR;
+ }
+
+ if (state->prefixes.has_lock && !def->accepts_LOCK)
+ {
+ return ZYDIS_STATUS_ILLEGAL_LOCK;
+ }
+ break;
+ }
+ case ZYDIS_INSTRUCTION_ENCODING_3DNOW:
+ {
+ break;
+ }
+ case ZYDIS_INSTRUCTION_ENCODING_XOP:
+ {
+ const ZydisInstructionDefinitionXOP* def =
+ (const ZydisInstructionDefinitionXOP*)definition;
+ def_ndsndd = def->op_ndsndd;
+ break;
+ }
+ case ZYDIS_INSTRUCTION_ENCODING_VEX:
+ {
+ const ZydisInstructionDefinitionVEX* def =
+ (const ZydisInstructionDefinitionVEX*)definition;
+ def_ndsndd = def->op_ndsndd;
+ is_gather = def->is_gather;
+ no_source_source_match = def->no_source_source_match;
+ break;
+ }
+ case ZYDIS_INSTRUCTION_ENCODING_EVEX:
+ {
+#ifndef ZYDIS_DISABLE_AVX512
+ const ZydisInstructionDefinitionEVEX* def =
+ (const ZydisInstructionDefinitionEVEX*)definition;
+ def_ndsndd = def->op_ndsndd;
+ is_gather = def->is_gather;
+ no_source_dest_match = def->no_source_dest_match;
+ mask_policy = def->mask_policy;
+
+ // Check for invalid zero-mask
+ if ((instruction->raw.evex.z) && (!def->accepts_zero_mask))
+ {
+ return ZYDIS_STATUS_INVALID_MASK; // TODO: Dedicated status code
+ }
+#else
+ ZYAN_UNREACHABLE;
+#endif
+ break;
+ }
+ case ZYDIS_INSTRUCTION_ENCODING_MVEX:
+ {
+#ifndef ZYDIS_DISABLE_KNC
+ const ZydisInstructionDefinitionMVEX* def =
+ (const ZydisInstructionDefinitionMVEX*)definition;
+ def_ndsndd = def->op_ndsndd;
+ is_gather = def->is_gather;
+ mask_policy = def->mask_policy;
+
+ // Check for invalid MVEX.SSS values
+ static const ZyanU8 lookup[26][8] =
+ {
+ // ZYDIS_MVEX_FUNC_IGNORED
+ { 1, 1, 1, 1, 1, 1, 1, 1 },
+ // ZYDIS_MVEX_FUNC_INVALID
+ { 1, 0, 0, 0, 0, 0, 0, 0 },
+ // ZYDIS_MVEX_FUNC_RC
+ { 1, 1, 1, 1, 1, 1, 1, 1 },
+ // ZYDIS_MVEX_FUNC_SAE
+ { 1, 1, 1, 1, 1, 1, 1, 1 },
+ // ZYDIS_MVEX_FUNC_F_32
+ { 1, 0, 0, 0, 0, 0, 0, 0 },
+ // ZYDIS_MVEX_FUNC_I_32
+ { 1, 0, 0, 0, 0, 0, 0, 0 },
+ // ZYDIS_MVEX_FUNC_F_64
+ { 1, 0, 0, 0, 0, 0, 0, 0 },
+ // ZYDIS_MVEX_FUNC_I_64
+ { 1, 0, 0, 0, 0, 0, 0, 0 },
+ // ZYDIS_MVEX_FUNC_SWIZZLE_32
+ { 1, 1, 1, 1, 1, 1, 1, 1 },
+ // ZYDIS_MVEX_FUNC_SWIZZLE_64
+ { 1, 1, 1, 1, 1, 1, 1, 1 },
+ // ZYDIS_MVEX_FUNC_SF_32
+ { 1, 1, 1, 1, 1, 0, 1, 1 },
+ // ZYDIS_MVEX_FUNC_SF_32_BCST
+ { 1, 1, 1, 0, 0, 0, 0, 0 },
+ // ZYDIS_MVEX_FUNC_SF_32_BCST_4TO16
+ { 1, 0, 1, 0, 0, 0, 0, 0 },
+ // ZYDIS_MVEX_FUNC_SF_64
+ { 1, 1, 1, 0, 0, 0, 0, 0 },
+ // ZYDIS_MVEX_FUNC_SI_32
+ { 1, 1, 1, 0, 1, 1, 1, 1 },
+ // ZYDIS_MVEX_FUNC_SI_32_BCST
+ { 1, 1, 1, 0, 0, 0, 0, 0 },
+ // ZYDIS_MVEX_FUNC_SI_32_BCST_4TO16
+ { 1, 0, 1, 0, 0, 0, 0, 0 },
+ // ZYDIS_MVEX_FUNC_SI_64
+ { 1, 1, 1, 0, 0, 0, 0, 0 },
+ // ZYDIS_MVEX_FUNC_UF_32
+ { 1, 0, 0, 1, 1, 1, 1, 1 },
+ // ZYDIS_MVEX_FUNC_UF_64
+ { 1, 0, 0, 0, 0, 0, 0, 0 },
+ // ZYDIS_MVEX_FUNC_UI_32
+ { 1, 0, 0, 0, 1, 1, 1, 1 },
+ // ZYDIS_MVEX_FUNC_UI_64
+ { 1, 0, 0, 0, 0, 0, 0, 0 },
+ // ZYDIS_MVEX_FUNC_DF_32
+ { 1, 0, 0, 1, 1, 1, 1, 1 },
+ // ZYDIS_MVEX_FUNC_DF_64
+ { 1, 0, 0, 0, 0, 0, 0, 0 },
+ // ZYDIS_MVEX_FUNC_DI_32
+ { 1, 0, 0, 0, 1, 1, 1, 1 },
+ // ZYDIS_MVEX_FUNC_DI_64
+ { 1, 0, 0, 0, 0, 0, 0, 0 }
+ };
+ ZYAN_ASSERT(def->functionality < ZYAN_ARRAY_LENGTH(lookup));
+ ZYAN_ASSERT(instruction->raw.mvex.SSS < 8);
+ if (!lookup[def->functionality][instruction->raw.mvex.SSS])
+ {
+ return ZYDIS_STATUS_DECODING_ERROR;
+ }
+#else
+ ZYAN_UNREACHABLE;
+#endif
+ break;
+ }
+ default:
+ ZYAN_UNREACHABLE;
+ }
+
+ ZydisDecoderContext* context = state->context;
+ const ZyanBool is_reg = context->reg_info.is_mod_reg;
+
+ ZyanU8 no_rip_rel = ZYAN_FALSE;
+ ZyanU8 is_sr_dest_reg = ZYAN_FALSE;
+ ZyanU8 is_sr_dest_rm = ZYAN_FALSE;
+ if (def_reg)
+ {
+ is_sr_dest_reg = ZYDIS_OPDEF_GET_REG_HIGH_BIT(def_reg);
+ def_reg = ZYDIS_OPDEF_GET_REG(def_reg);
+ }
+ if (def_rm)
+ {
+ if (is_reg)
+ {
+ is_sr_dest_rm = ZYDIS_OPDEF_GET_REG_HIGH_BIT(def_rm);
+ def_rm = ZYDIS_OPDEF_GET_REG(def_rm);
+ }
+ else
+ {
+ no_rip_rel = ZYDIS_OPDEF_GET_MEM_HIGH_BIT(def_rm);
+ def_rm = ZYDIS_OPDEF_GET_MEM(def_rm);
+ }
+ }
+
+ // Check RIP-relative memory addressing
+ if (no_rip_rel)
+ {
+ const ZyanBool is_rip_rel =
+ (state->decoder->machine_mode == ZYDIS_MACHINE_MODE_LONG_64) &&
+ (instruction->raw.modrm.mod == 0) && (instruction->raw.modrm.rm == 5);
+ if (is_rip_rel)
+ {
+ return ZYDIS_STATUS_BAD_REGISTER;
+ }
+ }
+
+ // Populate- and validate register constraints
+ ZYAN_CHECK(ZydisPopulateRegisterIds(context, instruction, def_reg, def_rm, def_ndsndd));
+
+ // `ZYDIS_REGISTER_CS` is not allowed as `MOV` target
+ if (is_sr_dest_reg && (context->reg_info.id_reg == 1))
+ {
+ return ZYDIS_STATUS_BAD_REGISTER;
+ }
+ if (is_sr_dest_rm && (context->reg_info.id_rm == 1))
+ {
+ return ZYDIS_STATUS_BAD_REGISTER;
+ }
+
+ // Check gather registers
+ if (is_gather)
+ {
+ // ZYAN_ASSERT(has_VSIB);
+ ZYAN_ASSERT(instruction->raw.modrm.mod != 3);
+ ZYAN_ASSERT(instruction->raw.modrm.rm == 4);
+
+ const ZyanU8 index = context->reg_info.id_index;
+ ZyanU8 dest = context->reg_info.id_reg;
+ ZyanU8 mask = 0xF0;
+
+ if (instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_VEX)
+ {
+ ZYAN_ASSERT((def_reg == ZYDIS_REGKIND_VR) &&
+ (def_rm == ZYDIS_MEMOP_TYPE_VSIB) &&
+ (def_ndsndd == ZYDIS_REGKIND_VR));
+ mask = context->reg_info.id_ndsndd;
+ }
+
+ if ((instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_EVEX) ||
+ (instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_MVEX))
+ {
+ ZYAN_ASSERT(((def_reg == ZYDIS_REGKIND_INVALID) ||
+ (def_reg == ZYDIS_REGKIND_VR)) &&
+ (def_rm == ZYDIS_MEMOP_TYPE_VSIB) &&
+ (def_ndsndd == ZYDIS_REGKIND_INVALID));
+
+ // Some gather instructions (like `VGATHERPF0{D|Q}{PS|PD}`) do not have a destination
+ // operand
+ if (!def_reg)
+ {
+ dest = 0xF1;
+ }
+ }
+
+ // 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 ZYDIS_STATUS_BAD_REGISTER;
+ }
+ }
+
+ // Check if any source register matches the destination register
+ if (no_source_dest_match)
+ {
+ ZYAN_ASSERT((instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_EVEX) ||
+ (instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_VEX));
+
+ const ZyanU8 dest = context->reg_info.id_reg;
+ const ZyanU8 source1 = context->reg_info.id_ndsndd;
+ const ZyanU8 source2 = context->reg_info.id_rm;
+
+ if ((dest == source1) || (is_reg && (dest == source2)))
+ {
+ return ZYDIS_STATUS_BAD_REGISTER;
+ }
+ }
+
+ // If any pair of the source or destination registers are the same, the instruction results a
+ // UD fault
+ if (no_source_source_match) // TODO: Find better name
+ {
+ ZYAN_ASSERT(instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_VEX);
+ ZYAN_ASSERT(is_reg);
+
+ const ZyanU8 dest = context->reg_info.id_reg;
+ const ZyanU8 source1 = context->reg_info.id_ndsndd;
+ const ZyanU8 source2 = context->reg_info.id_rm;
+
+ if ((dest == source1) || (dest == source2) || (source1 == source2))
+ {
+ return ZYDIS_STATUS_BAD_REGISTER;
+ }
+ }
+
+#if !defined(ZYDIS_DISABLE_AVX512) || !defined(ZYDIS_DISABLE_KNC)
+ // Check for invalid MASK registers
+ switch (mask_policy)
+ {
+ case ZYDIS_MASK_POLICY_INVALID:
+ case ZYDIS_MASK_POLICY_ALLOWED:
+ // Nothing to do here
+ break;
+ case ZYDIS_MASK_POLICY_REQUIRED:
+ if (!context->vector_unified.mask)
+ {
+ return ZYDIS_STATUS_INVALID_MASK;
+ }
+ break;
+ case ZYDIS_MASK_POLICY_FORBIDDEN:
+ if (context->vector_unified.mask)
+ {
+ return ZYDIS_STATUS_INVALID_MASK;
+ }
+ break;
+ default:
+ ZYAN_UNREACHABLE;
+ }
+#endif
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/**
+ * Uses the decoder-tree to decode the current instruction.
+ *
+ * @param state A pointer to the `ZydisDecoderState` struct.
+ * @param instruction A pointer to the `ZydisDecodedInstruction` struct.
+ *
+ * @return A zyan status code.
+ */
+static ZyanStatus ZydisDecodeInstruction(ZydisDecoderState* state,
+ ZydisDecodedInstruction* instruction)
+{
+ ZYAN_ASSERT(state);
+ ZYAN_ASSERT(instruction);
+
+ // Iterate through the decoder tree
+ const ZydisDecoderTreeNode* node = ZydisDecoderTreeGetRootNode();
+ const ZydisDecoderTreeNode* temp = ZYAN_NULL;
+ ZydisDecoderTreeNodeType node_type;
+ do
+ {
+ node_type = node->type;
+ ZyanU16 index = 0;
+ ZyanStatus status = 0;
+ switch (node_type)
+ {
+ case ZYDIS_NODETYPE_INVALID:
+ if (temp)
+ {
+ node = temp;
+ temp = ZYAN_NULL;
+ node_type = ZYDIS_NODETYPE_FILTER_MANDATORY_PREFIX;
+ if (state->prefixes.mandatory_candidate != 0x00)
+ {
+ instruction->raw.prefixes[state->prefixes.offset_mandatory].type =
+ ZYDIS_PREFIX_TYPE_IGNORED;
+ }
+ if (state->prefixes.mandatory_candidate == 0x66)
+ {
+ if (state->prefixes.offset_osz_override ==
+ state->prefixes.offset_mandatory)
+ {
+ instruction->raw.prefixes[state->prefixes.offset_mandatory].type =
+ ZYDIS_PREFIX_TYPE_EFFECTIVE;
+ }
+ instruction->attributes |= ZYDIS_ATTRIB_HAS_OPERANDSIZE;
+ }
+ continue;
+ }
+ return ZYDIS_STATUS_DECODING_ERROR;
+ case ZYDIS_NODETYPE_FILTER_XOP:
+ status = ZydisNodeHandlerXOP(instruction, &index);
+ break;
+ case ZYDIS_NODETYPE_FILTER_VEX:
+ status = ZydisNodeHandlerVEX(instruction, &index);
+ break;
+ case ZYDIS_NODETYPE_FILTER_EMVEX:
+ status = ZydisNodeHandlerEMVEX(instruction, &index);
+ break;
+ case ZYDIS_NODETYPE_FILTER_OPCODE:
+ status = ZydisNodeHandlerOpcode(state, instruction, &index);
+ break;
+ case ZYDIS_NODETYPE_FILTER_MODE:
+ status = ZydisNodeHandlerMode(instruction, &index);
+ break;
+ case ZYDIS_NODETYPE_FILTER_MODE_COMPACT:
+ status = ZydisNodeHandlerModeCompact(instruction, &index);
+ break;
+ case ZYDIS_NODETYPE_FILTER_MODRM_MOD:
+ status = ZydisNodeHandlerModrmMod(state, instruction, &index);
+ break;
+ case ZYDIS_NODETYPE_FILTER_MODRM_MOD_COMPACT:
+ status = ZydisNodeHandlerModrmModCompact(state, instruction, &index);
+ break;
+ case ZYDIS_NODETYPE_FILTER_MODRM_REG:
+ status = ZydisNodeHandlerModrmReg(state, instruction, &index);
+ break;
+ case ZYDIS_NODETYPE_FILTER_MODRM_RM:
+ status = ZydisNodeHandlerModrmRm(state, instruction, &index);
+ break;
+ case ZYDIS_NODETYPE_FILTER_PREFIX_GROUP1:
+ index = state->prefixes.group1 ? 1 : 0;
+ break;
+ case ZYDIS_NODETYPE_FILTER_MANDATORY_PREFIX:
+ status = ZydisNodeHandlerMandatoryPrefix(state, instruction, &index);
+ temp = ZydisDecoderTreeGetChildNode(node, 0);
+ // TODO: Return to this point, if index == 0 contains a value and the previous path
+ // TODO: was not successful
+ // TODO: Restore consumed prefix
+ break;
+ case ZYDIS_NODETYPE_FILTER_OPERAND_SIZE:
+ status = ZydisNodeHandlerOperandSize(state, instruction, &index);
+ break;
+ case ZYDIS_NODETYPE_FILTER_ADDRESS_SIZE:
+ status = ZydisNodeHandlerAddressSize(instruction, &index);
+ break;
+ case ZYDIS_NODETYPE_FILTER_VECTOR_LENGTH:
+ status = ZydisNodeHandlerVectorLength(state->context, instruction, &index);
+ break;
+ case ZYDIS_NODETYPE_FILTER_REX_W:
+ status = ZydisNodeHandlerRexW(state->context, instruction, &index);
+ break;
+ case ZYDIS_NODETYPE_FILTER_REX_B:
+ status = ZydisNodeHandlerRexB(state->context, instruction, &index);
+ break;
+#ifndef ZYDIS_DISABLE_AVX512
+ case ZYDIS_NODETYPE_FILTER_EVEX_B:
+ status = ZydisNodeHandlerEvexB(instruction, &index);
+ break;
+#endif
+#ifndef ZYDIS_DISABLE_KNC
+ case ZYDIS_NODETYPE_FILTER_MVEX_E:
+ status = ZydisNodeHandlerMvexE(instruction, &index);
+ break;
+#endif
+ case ZYDIS_NODETYPE_FILTER_MODE_AMD:
+ index = state->decoder->decoder_mode[ZYDIS_DECODER_MODE_AMD_BRANCHES] ? 1 : 0;
+ break;
+ case ZYDIS_NODETYPE_FILTER_MODE_KNC:
+ index = state->decoder->decoder_mode[ZYDIS_DECODER_MODE_KNC] ? 1 : 0;
+ break;
+ case ZYDIS_NODETYPE_FILTER_MODE_MPX:
+ index = state->decoder->decoder_mode[ZYDIS_DECODER_MODE_MPX] ? 1 : 0;
+ break;
+ case ZYDIS_NODETYPE_FILTER_MODE_CET:
+ index = state->decoder->decoder_mode[ZYDIS_DECODER_MODE_CET] ? 1 : 0;
+ break;
+ case ZYDIS_NODETYPE_FILTER_MODE_LZCNT:
+ index = state->decoder->decoder_mode[ZYDIS_DECODER_MODE_LZCNT] ? 1 : 0;
+ break;
+ case ZYDIS_NODETYPE_FILTER_MODE_TZCNT:
+ index = state->decoder->decoder_mode[ZYDIS_DECODER_MODE_TZCNT] ? 1 : 0;
+ break;
+ case ZYDIS_NODETYPE_FILTER_MODE_WBNOINVD:
+ index = state->decoder->decoder_mode[ZYDIS_DECODER_MODE_WBNOINVD] ? 1 : 0;
+ break;
+ case ZYDIS_NODETYPE_FILTER_MODE_CLDEMOTE:
+ index = state->decoder->decoder_mode[ZYDIS_DECODER_MODE_CLDEMOTE] ? 1 : 0;
+ break;
+ default:
+ if (node_type & ZYDIS_NODETYPE_DEFINITION_MASK)
+ {
+ const ZydisInstructionDefinition* definition;
+ ZydisGetInstructionDefinition(instruction->encoding, node->value, &definition);
+ ZydisSetEffectiveOperandWidth(state->context, instruction, definition);
+ ZydisSetEffectiveAddressWidth(state->context, instruction, definition);
+
+ const ZydisInstructionEncodingInfo* info;
+ ZydisGetInstructionEncodingInfo(node, &info);
+ ZYAN_CHECK(ZydisDecodeOptionalInstructionParts(state, instruction, info));
+ ZYAN_CHECK(ZydisCheckErrorConditions(state, instruction, definition));
+
+ if (instruction->encoding == ZYDIS_INSTRUCTION_ENCODING_3DNOW)
+ {
+ // Get actual 3DNOW opcode and definition
+ ZYAN_CHECK(ZydisInputNext(state, instruction, &instruction->opcode));
+ node = ZydisDecoderTreeGetRootNode();
+ node = ZydisDecoderTreeGetChildNode(node, 0x0F);
+ node = ZydisDecoderTreeGetChildNode(node, 0x0F);
+ node = ZydisDecoderTreeGetChildNode(node, instruction->opcode);
+ if (node->type == ZYDIS_NODETYPE_INVALID)
+ {
+ return ZYDIS_STATUS_DECODING_ERROR;
+ }
+ ZYAN_ASSERT(node->type == ZYDIS_NODETYPE_FILTER_MODRM_MOD_COMPACT);
+ node = ZydisDecoderTreeGetChildNode(
+ node, (instruction->raw.modrm.mod == 0x3) ? 0 : 1);
+ ZYAN_ASSERT(node->type & ZYDIS_NODETYPE_DEFINITION_MASK);
+ ZydisGetInstructionDefinition(instruction->encoding, node->value, &definition);
+ }
+
+ instruction->mnemonic = definition->mnemonic;
+
+#ifndef ZYDIS_MINIMAL_MODE
+
+ instruction->operand_count = definition->operand_count;
+ instruction->operand_count_visible = definition->operand_count_visible;
+ state->context->definition = definition;
+
+ instruction->meta.category = definition->category;
+ instruction->meta.isa_set = definition->isa_set;
+ instruction->meta.isa_ext = definition->isa_ext;
+ instruction->meta.branch_type = definition->branch_type;
+ ZYAN_ASSERT((instruction->meta.branch_type == ZYDIS_BRANCH_TYPE_NONE) ||
+ ((instruction->meta.category == ZYDIS_CATEGORY_CALL) ||
+ (instruction->meta.category == ZYDIS_CATEGORY_COND_BR) ||
+ (instruction->meta.category == ZYDIS_CATEGORY_UNCOND_BR) ||
+ (instruction->meta.category == ZYDIS_CATEGORY_RET)));
+ instruction->meta.exception_class = definition->exception_class;
+
+ if (!state->decoder->decoder_mode[ZYDIS_DECODER_MODE_MINIMAL])
+ {
+ ZydisSetAttributes(state, instruction, definition);
+ switch (instruction->encoding)
+ {
+ case ZYDIS_INSTRUCTION_ENCODING_XOP:
+ case ZYDIS_INSTRUCTION_ENCODING_VEX:
+ case ZYDIS_INSTRUCTION_ENCODING_EVEX:
+ case ZYDIS_INSTRUCTION_ENCODING_MVEX:
+ ZydisSetAVXInformation(state->context, instruction, definition);
+ break;
+ default:
+ break;
+ }
+
+ const ZydisDefinitionAccessedFlags* flags;
+ if (ZydisGetAccessedFlags(definition, &flags))
+ {
+ instruction->attributes |= ZYDIS_ATTRIB_CPUFLAG_ACCESS;
+ }
+ instruction->cpu_flags = &flags->cpu_flags;
+ instruction->fpu_flags = &flags->fpu_flags;
+ }
+
+#endif
+
+ return ZYAN_STATUS_SUCCESS;
+ }
+ ZYAN_UNREACHABLE;
+ }
+ ZYAN_CHECK(status);
+ node = ZydisDecoderTreeGetChildNode(node, index);
+ } while ((node_type != ZYDIS_NODETYPE_INVALID) && !(node_type & ZYDIS_NODETYPE_DEFINITION_MASK));
+ return ZYAN_STATUS_SUCCESS;
+}
+
+/* ---------------------------------------------------------------------------------------------- */
+
+/* ============================================================================================== */
+/* Exported functions */
+/* ============================================================================================== */
+
+ZyanStatus ZydisDecoderInit(ZydisDecoder* decoder, ZydisMachineMode machine_mode,
+ ZydisStackWidth stack_width)
+{
+ static const ZyanBool decoder_modes[ZYDIS_DECODER_MODE_MAX_VALUE + 1] =
+ {
+#ifdef ZYDIS_MINIMAL_MODE
+ ZYAN_TRUE , // ZYDIS_DECODER_MODE_MINIMAL
+#else
+ ZYAN_FALSE, // ZYDIS_DECODER_MODE_MINIMAL
+#endif
+ ZYAN_FALSE, // ZYDIS_DECODER_MODE_AMD_BRANCHES
+ ZYAN_FALSE, // ZYDIS_DECODER_MODE_KNC
+ ZYAN_TRUE , // ZYDIS_DECODER_MODE_MPX
+ ZYAN_TRUE , // ZYDIS_DECODER_MODE_CET
+ ZYAN_TRUE , // ZYDIS_DECODER_MODE_LZCNT
+ ZYAN_TRUE , // ZYDIS_DECODER_MODE_TZCNT
+ ZYAN_FALSE, // ZYDIS_DECODER_MODE_WBNOINVD
+ ZYAN_TRUE // ZYDIS_DECODER_MODE_CLDEMOTE
+ };
+
+ if (!decoder)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ switch (machine_mode)
+ {
+ case ZYDIS_MACHINE_MODE_LONG_64:
+ if (stack_width != ZYDIS_STACK_WIDTH_64)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ break;
+ case ZYDIS_MACHINE_MODE_LONG_COMPAT_32:
+ case ZYDIS_MACHINE_MODE_LONG_COMPAT_16:
+ case ZYDIS_MACHINE_MODE_LEGACY_32:
+ case ZYDIS_MACHINE_MODE_LEGACY_16:
+ case ZYDIS_MACHINE_MODE_REAL_16:
+ if ((stack_width != ZYDIS_STACK_WIDTH_16) && (stack_width != ZYDIS_STACK_WIDTH_32))
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ break;
+ default:
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ decoder->machine_mode = machine_mode;
+ decoder->stack_width = stack_width;
+ ZYAN_MEMCPY(&decoder->decoder_mode, &decoder_modes, sizeof(decoder_modes));
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZydisDecoderEnableMode(ZydisDecoder* decoder, ZydisDecoderMode mode, ZyanBool enabled)
+{
+ if (!decoder || ((ZyanUSize)mode > ZYDIS_DECODER_MODE_MAX_VALUE))
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+#ifdef ZYDIS_MINIMAL_MODE
+ if ((mode == ZYDIS_DECODER_MODE_MINIMAL) && !enabled)
+ {
+ return ZYAN_STATUS_INVALID_OPERATION;
+ }
+#endif
+
+ decoder->decoder_mode[mode] = enabled;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZydisDecoderDecodeFull(const ZydisDecoder* decoder,
+ const void* buffer, ZyanUSize length, ZydisDecodedInstruction* instruction,
+ ZydisDecodedOperand operands[ZYDIS_MAX_OPERAND_COUNT])
+{
+ if (!decoder || !instruction || !buffer || !operands)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+ if (!length)
+ {
+ return ZYDIS_STATUS_NO_MORE_DATA;
+ }
+ if (decoder->decoder_mode[ZYDIS_DECODER_MODE_MINIMAL])
+ {
+ return ZYAN_STATUS_MISSING_DEPENDENCY; // TODO: Introduce better status code
+ }
+
+ ZydisDecoderContext context;
+ ZYAN_CHECK(ZydisDecoderDecodeInstruction(decoder, &context, buffer, length, instruction));
+ ZYAN_CHECK(ZydisDecoderDecodeOperands(decoder, &context, instruction, operands,
+ instruction->operand_count));
+ ZYAN_MEMSET(&operands[instruction->operand_count], 0,
+ (ZYDIS_MAX_OPERAND_COUNT - instruction->operand_count) * sizeof(operands[0]));
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZydisDecoderDecodeInstruction(const ZydisDecoder* decoder, ZydisDecoderContext* context,
+ const void* buffer, ZyanUSize length, ZydisDecodedInstruction* instruction)
+{
+ if (!decoder || !instruction || !buffer)
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ if (!length)
+ {
+ return ZYDIS_STATUS_NO_MORE_DATA;
+ }
+
+ ZydisDecoderState state;
+ ZYAN_MEMSET(&state, 0, sizeof(state));
+ state.decoder = decoder;
+ state.buffer = (const ZyanU8*)buffer;
+ state.buffer_len = length;
+ state.prefixes.offset_notrack = -1;
+
+ ZydisDecoderContext default_context;
+ if (!context)
+ {
+ // Use a fallback context if no custom one has been provided
+ context = &default_context;
+ }
+ ZYAN_MEMSET(context, 0, sizeof(*context));
+ state.context = context;
+
+ ZYAN_MEMSET(instruction, 0, sizeof(*instruction));
+ instruction->machine_mode = decoder->machine_mode;
+ instruction->stack_width = 16 << decoder->stack_width;
+
+ ZYAN_CHECK(ZydisCollectOptionalPrefixes(&state, instruction));
+ ZYAN_CHECK(ZydisDecodeInstruction(&state, instruction));
+
+ instruction->raw.encoding2 = instruction->encoding;
+
+ return ZYAN_STATUS_SUCCESS;
+}
+
+ZyanStatus ZydisDecoderDecodeOperands(const ZydisDecoder* decoder,
+ const ZydisDecoderContext* context, const ZydisDecodedInstruction* instruction,
+ ZydisDecodedOperand* operands, ZyanU8 operand_count)
+{
+#ifdef ZYDIS_MINIMAL_MODE
+
+ ZYAN_UNUSED(decoder);
+ ZYAN_UNUSED(context);
+ ZYAN_UNUSED(instruction);
+ ZYAN_UNUSED(operands);
+ ZYAN_UNUSED(operand_count);
+
+ return ZYAN_STATUS_MISSING_DEPENDENCY; // TODO: Introduce better status code
+
+#else
+
+ if (!decoder || !context || !context->definition || !instruction ||
+ (operand_count && !operands) || (operand_count > ZYDIS_MAX_OPERAND_COUNT))
+ {
+ return ZYAN_STATUS_INVALID_ARGUMENT;
+ }
+
+ if (decoder->decoder_mode[ZYDIS_DECODER_MODE_MINIMAL])
+ {
+ return ZYAN_STATUS_MISSING_DEPENDENCY; // TODO: Introduce better status code
+ }
+
+ operand_count = ZYAN_MIN(operand_count, instruction->operand_count);
+ if (!operand_count)
+ {
+ return ZYAN_STATUS_SUCCESS;
+ }
+
+ return ZydisDecodeOperands(decoder, context, instruction, operands, operand_count);
+
+#endif
+}
+
+/* ============================================================================================== */