Source code
Revision control
Copy as Markdown
Other Tools
/***************************************************************************************************
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);