zydis/src/Encoder.c

1133 lines
36 KiB
C
Raw Normal View History

/***************************************************************************************************
Zyan Disassembler Library (Zydis)
Original Author : Joel H<EFBFBD>ner
* 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.
***************************************************************************************************/
#include <Zydis/Encoder.h>
2017-07-10 20:34:25 +08:00
#include <SharedData.h>
#include <EncoderData.h>
2017-01-18 03:53:34 +08:00
#include <string.h>
#include <stdint.h>
/* ============================================================================================== */
2017-01-23 00:38:14 +08:00
/* Internal context and table types */
2017-01-13 01:53:28 +08:00
/* ============================================================================================== */
struct ZydisPrefixAcceptMapping
{
uint64_t has;
uint64_t accepts;
} static const prefixAcceptMap[] =
{
{ ZYDIS_ATTRIB_HAS_LOCK, ZYDIS_ATTRIB_ACCEPTS_LOCK },
{ ZYDIS_ATTRIB_HAS_REP, ZYDIS_ATTRIB_ACCEPTS_REP },
{ ZYDIS_ATTRIB_HAS_REPE, ZYDIS_ATTRIB_ACCEPTS_REPE },
{ ZYDIS_ATTRIB_HAS_REPZ, ZYDIS_ATTRIB_ACCEPTS_REPZ },
{ ZYDIS_ATTRIB_HAS_REPNE, ZYDIS_ATTRIB_ACCEPTS_REPNE },
{ ZYDIS_ATTRIB_HAS_REPNZ, ZYDIS_ATTRIB_ACCEPTS_REPNZ },
{ ZYDIS_ATTRIB_HAS_BOUND, ZYDIS_ATTRIB_ACCEPTS_BOUND },
{ ZYDIS_ATTRIB_HAS_XACQUIRE, ZYDIS_ATTRIB_ACCEPTS_XACQUIRE },
{ ZYDIS_ATTRIB_HAS_XRELEASE, ZYDIS_ATTRIB_ACCEPTS_XRELEASE },
{ ZYDIS_ATTRIB_HAS_BRANCH_NOT_TAKEN, ZYDIS_ATTRIB_ACCEPTS_BRANCH_HINTS },
{ ZYDIS_ATTRIB_HAS_BRANCH_TAKEN, ZYDIS_ATTRIB_ACCEPTS_BRANCH_HINTS },
{ ZYDIS_ATTRIB_HAS_SEGMENT, ZYDIS_ATTRIB_ACCEPTS_SEGMENT },
{ ZYDIS_ATTRIB_HAS_SEGMENT_CS, ZYDIS_ATTRIB_ACCEPTS_SEGMENT },
{ ZYDIS_ATTRIB_HAS_SEGMENT_SS, ZYDIS_ATTRIB_ACCEPTS_SEGMENT },
{ ZYDIS_ATTRIB_HAS_SEGMENT_DS, ZYDIS_ATTRIB_ACCEPTS_SEGMENT },
{ ZYDIS_ATTRIB_HAS_SEGMENT_ES, ZYDIS_ATTRIB_ACCEPTS_SEGMENT },
{ ZYDIS_ATTRIB_HAS_SEGMENT_FS, ZYDIS_ATTRIB_ACCEPTS_SEGMENT },
{ ZYDIS_ATTRIB_HAS_SEGMENT_GS, ZYDIS_ATTRIB_ACCEPTS_SEGMENT },
//{ ZYDIS_ATTRIB_HAS_OPERANDSIZE, ZYDIS_ATTRIB_ACCEPTS_OPERANDSIZE },
//{ ZYDIS_ATTRIB_HAS_ADDRESSSIZE, ZYDIS_ATTRIB_ACCEPTS_ADDRESSSIZE },
};
2017-01-13 01:53:28 +08:00
/**
* @brief The encoder context struct.
*/
typedef struct ZydisEncoderContext_
2017-01-13 01:53:28 +08:00
{
uint8_t* buffer;
size_t bufferLen;
size_t writeOffs;
ZydisEncoderRequest* req;
2017-07-10 20:34:25 +08:00
const ZydisEncodableInstruction* matchingInsn;
const ZydisInstructionDefinition* matchingDef;
uint8_t matchingOperandCount;
const ZydisOperandDefinition* matchingOperands;
ZydisBool emitMandatoryPrefix;
uint8_t mandatoryPrefix;
uint8_t dispBitSize;
uint64_t disp;
uint8_t immBitSizes[2];
uint64_t imms[2];
uint8_t opcodeMapPrefixLen;
uint8_t opcodeMapPrefix[3];
struct
{
uint8_t opcode;
struct
{
uint8_t W;
uint8_t R;
uint8_t X;
uint8_t B;
} rex;
struct
{
uint8_t R;
uint8_t X;
uint8_t B;
uint8_t m_mmmm;
uint8_t W;
uint8_t vvvv;
uint8_t L;
uint8_t pp;
} xop;
struct
{
uint8_t R;
uint8_t X;
uint8_t B;
uint8_t m_mmmm;
uint8_t W;
uint8_t vvvv;
uint8_t L;
uint8_t pp;
} vex;
struct
{
uint8_t R;
uint8_t X;
uint8_t B;
uint8_t R2;
uint8_t mm;
uint8_t W;
uint8_t vvvv;
uint8_t pp;
uint8_t z;
uint8_t L2;
uint8_t L;
uint8_t b;
uint8_t V2;
uint8_t aaa;
} evex;
struct
{
uint8_t R;
uint8_t X;
uint8_t B;
uint8_t R2;
uint8_t mmmm;
uint8_t W;
uint8_t vvvv;
uint8_t pp;
uint8_t E;
uint8_t SSS;
uint8_t V2;
uint8_t kkk;
} mvex;
struct
{
uint8_t mod;
uint8_t reg;
uint8_t rm;
} modrm;
struct
{
uint8_t scale;
uint8_t index;
uint8_t base;
} sib;
struct
{
int64_t value;
uint8_t size;
uint8_t offset;
} disp;
struct
{
ZydisBool isSigned;
ZydisBool isRelative;
union
{
uint64_t u;
int64_t s;
} value;
uint8_t size;
uint8_t offset;
} imm[2];
} raw;
} ZydisEncoderContext;
2017-01-13 01:53:28 +08:00
/* ============================================================================================== */
/* Internal helpers */
/* ============================================================================================== */
2017-01-23 00:38:14 +08:00
/* ---------------------------------------------------------------------------------------------- */
/* Byte stream output functions. Those are the only funcs that access the output stream directly. */
/* ---------------------------------------------------------------------------------------------- */
static ZydisStatus ZydisEmitImm(ZydisEncoderContext* ctx, uint64_t imm, int bits)
2017-01-18 03:53:34 +08:00
{
ZYDIS_ASSERT(bits == 8 || bits == 16 || bits == 32 || bits == 64);
size_t newWriteOffs = ctx->writeOffs + bits / 8;
2017-01-23 00:38:14 +08:00
if (newWriteOffs >= ctx->bufferLen)
2017-01-18 03:53:34 +08:00
{
return ZYDIS_STATUS_INSUFFICIENT_BUFFER_SIZE;
}
2017-01-23 00:38:14 +08:00
if (newWriteOffs > ZYDIS_MAX_INSTRUCTION_LENGTH)
{
return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO
}
2017-01-18 03:53:34 +08:00
// TODO: bswap on big-endian
switch (bits)
{
case 8: *(uint8_t* )&ctx->buffer[ctx->writeOffs] = (uint8_t )imm; break;
case 16: *(uint16_t*)&ctx->buffer[ctx->writeOffs] = (uint16_t)imm; break;
case 32: *(uint32_t*)&ctx->buffer[ctx->writeOffs] = (uint32_t)imm; break;
case 64: *(uint64_t*)&ctx->buffer[ctx->writeOffs] = (uint64_t)imm; break;
2017-01-18 03:53:34 +08:00
default: ZYDIS_UNREACHABLE;
}
ctx->writeOffs = newWriteOffs;
2017-01-18 03:53:34 +08:00
return ZYDIS_STATUS_SUCCESS;
}
2017-01-23 00:38:14 +08:00
static ZydisStatus ZydisEmitByte(ZydisEncoderContext* ctx, uint8_t byte)
{
return ZydisEmitImm(ctx, byte, 8);
}
/* ---------------------------------------------------------------------------------------------- */
/* Byte code encoding functions. Translate prepared data to final format. */
/* ---------------------------------------------------------------------------------------------- */
static ZydisStatus ZydisEmitLegacyPrefixes(ZydisEncoderContext* ctx)
{
ZYDIS_ASSERT(ctx);
ZydisInstructionAttributes attribs = ctx->req->attributes;
2017-01-13 01:53:28 +08:00
if (attribs & ZYDIS_ATTRIB_HAS_LOCK)
{
ZYDIS_CHECK(ZydisEmitByte(ctx, 0xF0));
2017-01-13 01:53:28 +08:00
}
if (attribs & (ZYDIS_ATTRIB_HAS_REP | ZYDIS_ATTRIB_HAS_REPE | ZYDIS_ATTRIB_HAS_XRELEASE))
{
ZYDIS_CHECK(ZydisEmitByte(ctx, 0xF3));
2017-01-13 01:53:28 +08:00
}
if (attribs & (ZYDIS_ATTRIB_HAS_REPNE | ZYDIS_ATTRIB_HAS_BOUND | ZYDIS_ATTRIB_HAS_XACQUIRE))
{
ZYDIS_CHECK(ZydisEmitByte(ctx, 0xF2));
2017-01-13 01:53:28 +08:00
}
if (attribs & (ZYDIS_ATTRIB_HAS_BRANCH_NOT_TAKEN | ZYDIS_ATTRIB_HAS_SEGMENT_CS))
{
ZYDIS_CHECK(ZydisEmitByte(ctx, 0x2E));
2017-01-13 01:53:28 +08:00
}
if (attribs & (ZYDIS_ATTRIB_HAS_BRANCH_TAKEN | ZYDIS_ATTRIB_HAS_SEGMENT_DS))
{
ZYDIS_CHECK(ZydisEmitByte(ctx, 0x3E));
2017-01-13 01:53:28 +08:00
}
if (attribs & ZYDIS_ATTRIB_HAS_SEGMENT_SS)
{
ZYDIS_CHECK(ZydisEmitByte(ctx, 0x36));
2017-01-13 01:53:28 +08:00
}
if (attribs & ZYDIS_ATTRIB_HAS_SEGMENT_ES)
{
ZYDIS_CHECK(ZydisEmitByte(ctx, 0x26));
2017-01-13 01:53:28 +08:00
}
if (attribs & ZYDIS_ATTRIB_HAS_SEGMENT_FS)
{
ZYDIS_CHECK(ZydisEmitByte(ctx, 0x64));
2017-01-13 01:53:28 +08:00
}
if (attribs & ZYDIS_ATTRIB_HAS_SEGMENT_GS)
{
ZYDIS_CHECK(ZydisEmitByte(ctx, 0x65));
2017-01-13 01:53:28 +08:00
}
if (attribs & ZYDIS_ATTRIB_HAS_OPERANDSIZE)
{
ZYDIS_CHECK(ZydisEmitByte(ctx, 0x66));
2017-01-13 01:53:28 +08:00
}
if (attribs & ZYDIS_ATTRIB_HAS_ADDRESSSIZE)
{
ZYDIS_CHECK(ZydisEmitByte(ctx, 0x67));
2017-01-13 01:53:28 +08:00
}
return ZYDIS_STATUS_SUCCESS;
}
static ZydisStatus ZydisEmitREX(ZydisEncoderContext* ctx)
2017-01-13 01:53:28 +08:00
{
ZYDIS_ASSERT(ctx);
2017-01-13 01:53:28 +08:00
ZYDIS_CHECK(ZydisEmitByte(
ctx,
2017-01-13 01:53:28 +08:00
0x40 |
(ctx->raw.rex.W & 0x01) << 3 |
(ctx->raw.rex.R & 0x01) << 2 |
(ctx->raw.rex.X & 0x01) << 1 |
(ctx->raw.rex.B & 0x01) << 0
2017-01-13 01:53:28 +08:00
));
return ZYDIS_STATUS_SUCCESS;
}
static ZydisStatus ZydisEmitVEX(ZydisEncoderContext* ctx)
2017-01-13 01:53:28 +08:00
{
ZYDIS_ASSERT(ctx);
2017-01-13 01:53:28 +08:00
// Can we use short 2-byte VEX encoding?
if (ctx->raw.vex.X == 0 &&
ctx->raw.vex.B == 0 &&
ctx->raw.vex.W == 0 &&
ctx->raw.vex.m_mmmm == 1)
2017-01-13 01:53:28 +08:00
{
ZYDIS_CHECK(ZydisEmitByte(ctx, 0xC5));
2017-01-13 01:53:28 +08:00
ZYDIS_CHECK(ZydisEmitByte(
ctx,
(~ctx->raw.vex.R & 0x01) << 7 |
(~ctx->raw.vex.vvvv & 0x0F) << 3 |
( ctx->raw.vex.L & 0x01) << 2 |
( ctx->raw.vex.pp & 0x03) << 0
2017-01-13 01:53:28 +08:00
));
}
// Nope, use 3-byte VEX.
else
{
ZYDIS_CHECK(ZydisEmitByte(ctx, 0xC4));
2017-01-13 01:53:28 +08:00
ZYDIS_CHECK(ZydisEmitByte(
ctx,
(~ctx->raw.vex.R & 0x01) << 7 |
(~ctx->raw.vex.X & 0x01) << 6 |
(~ctx->raw.vex.B & 0x01) << 5 |
( ctx->raw.vex.m_mmmm & 0x1F) << 0
2017-01-13 01:53:28 +08:00
));
ZYDIS_CHECK(ZydisEmitByte(
ctx,
( ctx->raw.vex.W & 0x01) << 7 |
(~ctx->raw.vex.vvvv & 0x0F) << 3 |
( ctx->raw.vex.L & 0x01) << 2 |
( ctx->raw.vex.pp & 0x03) << 0
2017-01-13 01:53:28 +08:00
));
}
return ZYDIS_STATUS_SUCCESS;
}
static ZydisStatus ZydisEmitEVEX(ZydisEncoderContext* ctx)
2017-01-13 01:53:28 +08:00
{
ZYDIS_ASSERT(ctx);
ZYDIS_CHECK(ZydisEmitByte(ctx, 0x62));
2017-01-13 01:53:28 +08:00
ZYDIS_CHECK(ZydisEmitByte(
ctx,
(ctx->raw.evex.R & 0x01) << 7 |
(ctx->raw.evex.X & 0x01) << 6 |
(ctx->raw.evex.B & 0x01) << 5 |
(ctx->raw.evex.R2 & 0x01) << 4 |
(ctx->raw.evex.mm & 0x03) << 0
2017-01-13 01:53:28 +08:00
));
ZYDIS_CHECK(ZydisEmitByte(
ctx,
(ctx->raw.evex.W & 0x01) << 7 |
(ctx->raw.evex.vvvv & 0x0F) << 3 |
(ctx->raw.evex.pp & 0x03) << 0
2017-01-13 01:53:28 +08:00
));
ZYDIS_CHECK(ZydisEmitByte(
ctx,
(ctx->raw.evex.z & 0x01) << 7 |
(ctx->raw.evex.L2 & 0x01) << 6 |
(ctx->raw.evex.L & 0x01) << 5 |
(ctx->raw.evex.b & 0x01) << 4 |
(ctx->raw.evex.V2 & 0x01) << 3 |
(ctx->raw.evex.aaa & 0x07) << 0
2017-01-13 01:53:28 +08:00
));
return ZYDIS_STATUS_SUCCESS;
}
static ZydisStatus ZydisEmitXOP(ZydisEncoderContext* ctx)
2017-01-13 01:53:28 +08:00
{
ZYDIS_ASSERT(ctx);
ZYDIS_CHECK(ZydisEmitByte(ctx, 0x8F));
2017-01-13 01:53:28 +08:00
ZYDIS_CHECK(ZydisEmitByte(
ctx,
(ctx->raw.xop.R & 0x01) << 7 |
(ctx->raw.xop.X & 0x01) << 6 |
(ctx->raw.xop.B & 0x01) << 5 |
(ctx->raw.xop.m_mmmm & 0x1F) << 0
2017-01-13 01:53:28 +08:00
));
ZYDIS_CHECK(ZydisEmitByte(
ctx,
(ctx->raw.xop.W & 0x01) << 7 |
(ctx->raw.xop.vvvv & 0x0F) << 3 |
(ctx->raw.xop.L & 0x01) << 2 |
(ctx->raw.xop.pp & 0x03) << 0
2017-01-13 01:53:28 +08:00
));
return ZYDIS_STATUS_SUCCESS;
}
static ZydisStatus ZydisEmitModRM(ZydisEncoderContext* ctx)
2017-01-18 03:53:34 +08:00
{
ZYDIS_ASSERT(ctx);
2017-01-18 03:53:34 +08:00
ZYDIS_CHECK(ZydisEmitByte(
ctx,
(ctx->raw.modrm.mod & 0x03) << 6 |
(ctx->raw.modrm.reg & 0x07) << 3 |
(ctx->raw.modrm.rm & 0x07) << 0
2017-01-18 03:53:34 +08:00
));
return ZYDIS_STATUS_SUCCESS;
}
static ZydisStatus ZydisEmitSIB(ZydisEncoderContext* ctx)
2017-01-18 03:53:34 +08:00
{
ZYDIS_ASSERT(ctx);
2017-01-18 03:53:34 +08:00
ZYDIS_CHECK(ZydisEmitByte(
ctx,
(ctx->raw.sib.scale & 0x03) << 6 |
(ctx->raw.sib.index & 0x07) << 3 |
(ctx->raw.sib.base & 0x07) << 0
2017-01-18 03:53:34 +08:00
));
return ZYDIS_STATUS_SUCCESS;
}
2017-01-23 00:38:14 +08:00
/* ---------------------------------------------------------------------------------------------- */
/* Preparation functions. Parse encoder request, determine required bytes and prefixes. */
/* ---------------------------------------------------------------------------------------------- */
static ZydisStatus ZydisPrepareOpcode(ZydisEncoderContext* ctx)
2017-01-18 03:53:34 +08:00
{
ZYDIS_ASSERT(ctx);
2017-07-10 20:34:25 +08:00
ZYDIS_ASSERT(ctx->matchingDef);
ZYDIS_ASSERT(ctx->req);
2017-01-18 03:53:34 +08:00
// Put opcode map prefix(es), if required.
switch (ctx->req->encoding)
2017-01-18 03:53:34 +08:00
{
case ZYDIS_INSTRUCTION_ENCODING_DEFAULT:
case ZYDIS_INSTRUCTION_ENCODING_3DNOW:
2017-07-10 20:34:25 +08:00
switch (ctx->matchingInsn->opcodeMap)
{
case ZYDIS_OPCODE_MAP_0F:
ctx->opcodeMapPrefix[ctx->opcodeMapPrefixLen++] = 0x0F;
break;
case ZYDIS_OPCODE_MAP_0F38:
ctx->opcodeMapPrefix[ctx->opcodeMapPrefixLen++] = 0x0F;
ctx->opcodeMapPrefix[ctx->opcodeMapPrefixLen++] = 0x38;
break;
case ZYDIS_OPCODE_MAP_0F3A:
ctx->opcodeMapPrefix[ctx->opcodeMapPrefixLen++] = 0x0F;
ctx->opcodeMapPrefix[ctx->opcodeMapPrefixLen++] = 0x3A;
break;
case ZYDIS_OPCODE_MAP_DEFAULT:
break; // Nothing to do.
default:
ZYDIS_UNREACHABLE;
}
2017-01-18 03:53:34 +08:00
break;
case ZYDIS_INSTRUCTION_ENCODING_VEX:
2017-07-10 20:34:25 +08:00
ctx->raw.vex.m_mmmm = ctx->matchingInsn->opcodeMap;
ZYDIS_ASSERT(ctx->raw.vex.m_mmmm <= 0x03);
2017-01-18 03:53:34 +08:00
break;
case ZYDIS_INSTRUCTION_ENCODING_EVEX:
2017-07-10 20:34:25 +08:00
ctx->raw.evex.mm = ctx->matchingInsn->opcodeMap;
ZYDIS_ASSERT(ctx->raw.evex.mm <= 0x03);
2017-01-18 03:53:34 +08:00
break;
case ZYDIS_INSTRUCTION_ENCODING_XOP:
ctx->raw.xop.m_mmmm =
2017-07-10 20:34:25 +08:00
ctx->matchingInsn->opcodeMap - ZYDIS_OPCODE_MAP_XOP8 + 0x08;
ZYDIS_ASSERT(ctx->raw.xop.m_mmmm >= 0x08);
ZYDIS_ASSERT(ctx->raw.xop.m_mmmm <= 0x0B);
2017-01-18 03:53:34 +08:00
break;
default:
ZYDIS_UNREACHABLE;
}
return ZYDIS_STATUS_SUCCESS;
}
static ZydisStatus ZydisPrepareRegOperand(ZydisEncoderContext* ctx,
2017-01-20 07:54:48 +08:00
ZydisRegister reg, char topBitLoc)
{
ZYDIS_ASSERT(ctx);
int16_t regID = ZydisRegisterGetId(reg);
if (regID == -1) return ZYDIS_STATUS_INVALID_PARAMETER;
2017-01-20 07:54:48 +08:00
uint8_t lowerBits = (regID & 0x07) >> 0;
uint8_t topBit = (regID & 0x08) >> 3;
switch (topBitLoc)
{
case 'B': ctx->raw.modrm.rm = lowerBits; break;
case 'R': ctx->raw.modrm.reg = lowerBits; break;
case 'X': ctx->raw.sib.index = lowerBits; break;
2017-01-20 07:54:48 +08:00
default: ZYDIS_UNREACHABLE;
}
2017-01-24 01:31:50 +08:00
// No top bit? Quick exit.
if (!topBit) return ZYDIS_STATUS_SUCCESS;
2017-01-20 07:54:48 +08:00
switch (ctx->req->encoding)
2017-01-20 07:54:48 +08:00
{
case ZYDIS_INSTRUCTION_ENCODING_DEFAULT:
case ZYDIS_INSTRUCTION_ENCODING_3DNOW:
switch (topBitLoc)
{
2017-07-10 20:34:25 +08:00
case 'B': ctx->raw.rex.B = topBit; break;
case 'R': ctx->raw.rex.R = topBit; break;
case 'X': ctx->raw.rex.X = topBit; break;
2017-01-20 07:54:48 +08:00
default: ZYDIS_UNREACHABLE;
}
if (topBit) ctx->req->attributes |= ZYDIS_ATTRIB_HAS_REX;
2017-01-20 07:54:48 +08:00
break;
case ZYDIS_INSTRUCTION_ENCODING_VEX:
switch (topBitLoc)
{
2017-07-10 20:34:25 +08:00
case 'B': ctx->raw.vex.B = topBit; break;
case 'R': ctx->raw.vex.R = topBit; break;
case 'X': ctx->raw.vex.X = topBit; break;
2017-01-20 07:54:48 +08:00
default: ZYDIS_UNREACHABLE;
}
break;
case ZYDIS_INSTRUCTION_ENCODING_XOP:
switch (topBitLoc)
{
2017-07-10 20:34:25 +08:00
case 'B': ctx->raw.xop.B = topBit; break;
case 'R': ctx->raw.xop.R = topBit; break;
case 'X': ctx->raw.xop.X = topBit; break;
2017-01-20 07:54:48 +08:00
default: ZYDIS_UNREACHABLE;
}
break;
case ZYDIS_INSTRUCTION_ENCODING_EVEX:
switch (topBitLoc)
{
2017-07-10 20:34:25 +08:00
case 'B': ctx->raw.evex.B = topBit; break;
case 'R': ctx->raw.evex.R = topBit; break;
case 'X': ctx->raw.evex.X = topBit; break;
2017-01-20 07:54:48 +08:00
default: ZYDIS_UNREACHABLE;
}
break;
default:
return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO
}
2017-01-18 03:53:34 +08:00
return ZYDIS_STATUS_SUCCESS;
}
2017-01-23 00:38:14 +08:00
static ZydisBool ZydisIsBPReg(ZydisRegister reg)
{
2017-01-23 00:38:14 +08:00
return reg == ZYDIS_REGISTER_BPL ||
reg == ZYDIS_REGISTER_BP ||
reg == ZYDIS_REGISTER_EBP ||
reg == ZYDIS_REGISTER_RBP;
}
2017-01-24 01:31:50 +08:00
static ZydisBool ZydisIsSPReg(ZydisRegister reg)
2017-01-23 00:38:14 +08:00
{
return reg == ZYDIS_REGISTER_SPL ||
reg == ZYDIS_REGISTER_SP ||
reg == ZYDIS_REGISTER_ESP ||
2017-01-24 01:31:50 +08:00
reg == ZYDIS_REGISTER_RSP;
}
static ZydisBool ZydisIsIPReg(ZydisRegister reg)
{
return reg == ZYDIS_REGISTER_IP ||
reg == ZYDIS_REGISTER_EIP ||
reg == ZYDIS_REGISTER_RIP;
}
2017-01-24 01:31:50 +08:00
static ZydisBool ZydisIsStackReg(ZydisRegister reg)
{
return ZydisIsSPReg(reg) || ZydisIsBPReg(reg);
}
2017-01-24 04:52:26 +08:00
static ZydisStatus ZydisPrepareSegmentPrefix(ZydisEncoderContext* ctx,
ZydisRegister segment, ZydisRegister base)
2017-01-24 01:31:50 +08:00
{
2017-01-24 04:52:26 +08:00
// Segment prefix required?
switch (segment)
2017-01-24 01:31:50 +08:00
{
2017-01-24 04:52:26 +08:00
case ZYDIS_REGISTER_ES:
ctx->req->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_ES;
2017-01-24 04:52:26 +08:00
break;
case ZYDIS_REGISTER_SS:
if (!ZydisIsStackReg(base))
2017-01-24 01:31:50 +08:00
{
ctx->req->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_SS;
2017-01-24 01:31:50 +08:00
}
2017-01-24 04:52:26 +08:00
break;
case ZYDIS_REGISTER_CS:
ctx->req->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_CS;
2017-01-24 04:52:26 +08:00
break;
case ZYDIS_REGISTER_DS:
if (ZydisIsStackReg(base))
2017-01-24 01:31:50 +08:00
{
ctx->req->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_DS;
2017-01-24 01:31:50 +08:00
}
2017-01-24 04:52:26 +08:00
break;
case ZYDIS_REGISTER_FS:
ctx->req->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_FS;
2017-01-24 04:52:26 +08:00
break;
case ZYDIS_REGISTER_GS:
ctx->req->attributes |= ZYDIS_ATTRIB_HAS_SEGMENT_GS;
2017-01-24 04:52:26 +08:00
break;
default:
return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO: Better status.
2017-01-24 01:31:50 +08:00
}
return ZYDIS_STATUS_SUCCESS;
2017-01-23 00:38:14 +08:00
}
static ZydisStatus ZydisPrepareMemoryOperand(ZydisEncoderContext* ctx,
2017-07-10 20:34:25 +08:00
ZydisEncoderOperand* operand, const ZydisOperandDefinition* operandDef)
{
ZYDIS_ASSERT(ctx);
2017-07-10 20:34:25 +08:00
ZYDIS_ASSERT(ctx->req);
ZYDIS_ASSERT(operand);
2017-07-10 20:34:25 +08:00
ZYDIS_ASSERT(operandDef);
2017-01-24 04:52:26 +08:00
ZYDIS_CHECK(ZydisPrepareSegmentPrefix(ctx, operand->mem.segment, operand->mem.base));
// Absolute memory access? Special case.
if (operand->mem.base == ZYDIS_REGISTER_NONE)
{
ctx->disp = operand->mem.disp;
ctx->dispBitSize = 32;
// In 32 bit mode, ModRM allows for a shortcut here.
if (ctx->req->machineMode == 32)
{
ctx->raw.modrm.mod = 0x00;
ctx->raw.modrm.rm = 0x05 /* memory */;
}
// In AMD64 mode, we have to build a special SIB.
else
{
ctx->raw.modrm.mod = 0x00;
ctx->raw.modrm.rm = 0x04 /* SIB */;
ctx->raw.sib.index = 0x04 /* none */;
ctx->raw.sib.scale = 0x00 /* * 1 */;
ctx->raw.sib.base = 0x05;
ctx->req->attributes |= ZYDIS_ATTRIB_HAS_SIB;
}
return ZYDIS_STATUS_SUCCESS;
}
// rIP relative addressing? Special case.
if (ZydisIsIPReg(operand->mem.base))
{
// rIP addressing is only available since AMD64.
if (ctx->req->machineMode != 64)
{
return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO
}
// Only available with either EIP or RIP, not with IP.
if (operand->mem.base == ZYDIS_REGISTER_IP)
{
return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO
}
ctx->disp = operand->mem.disp;
ctx->dispBitSize = 32;
ctx->raw.modrm.mod = 0x00;
ctx->raw.modrm.rm = 0x05 /* RIP relative mem */;
if (operand->mem.base == ZYDIS_REGISTER_EIP)
{
ctx->req->attributes |= ZYDIS_ATTRIB_HAS_ADDRESSSIZE;
}
return ZYDIS_STATUS_SUCCESS;
}
// Process base register.
ZYDIS_CHECK(ZydisPrepareRegOperand(ctx, operand->mem.base, 'B'));
// Address size prefix required?
ZydisRegisterClass baseRegClass = ZydisRegisterGetClass(operand->mem.base);
switch (baseRegClass)
{
case ZYDIS_REGCLASS_GPR16:
switch (ctx->req->machineMode)
{
case 16:
break; // Nothing to do.
case 32:
ctx->req->attributes |= ZYDIS_ATTRIB_HAS_ADDRESSSIZE;
break;
case 64:
// AMD64 doesn't allow for 16 bit addressing.
return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO
default:
return ZYDIS_STATUS_INVALID_PARAMETER; // TODO
}
break;
case ZYDIS_REGCLASS_GPR32:
switch (ctx->req->machineMode)
{
case 16:
return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO
case 32:
break; // Nothing to do.
case 64:
ctx->req->attributes |= ZYDIS_ATTRIB_HAS_ADDRESSSIZE;
default:
return ZYDIS_STATUS_INVALID_PARAMETER; // TODO
}
break;
case ZYDIS_REGCLASS_GPR64:
if (ctx->req->machineMode != 64)
{
return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO
}
break;
default:
return ZYDIS_STATUS_INVALID_PARAMETER; // TODO
}
2017-01-24 01:31:50 +08:00
// SIB byte required? rSP can only be encoded with SIB.
if (operand->mem.index || operand->mem.scale || ZydisIsSPReg(operand->mem.base))
{
// Translate scale to SIB format.
switch (operand->mem.scale)
{
2017-01-24 01:31:50 +08:00
case 0: // We take 0 (uninitialized, 0 from memset) as * 1.
case 1: ctx->raw.sib.scale = 0x00; break;
case 2: ctx->raw.sib.scale = 0x01; break;
case 4: ctx->raw.sib.scale = 0x02; break;
case 8: ctx->raw.sib.scale = 0x03; break;
default: return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO
}
2017-01-24 01:31:50 +08:00
// Move base register info to SIB.
ctx->raw.sib.base = ctx->raw.modrm.rm;
ctx->raw.modrm.rm = 0x04 /* SIB */;
2017-01-24 01:31:50 +08:00
// Process index register.
if (operand->mem.index != ZYDIS_REGISTER_NONE)
{
// Base and index register must be of same register class, verify.
if (ZydisRegisterGetClass(operand->mem.index) != baseRegClass)
{
return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO
}
ZYDIS_CHECK(ZydisPrepareRegOperand(ctx, operand->mem.index, 'X'));
}
else
{
ctx->raw.sib.index = 0x04 /* no index */;
2017-01-24 01:31:50 +08:00
}
ctx->req->attributes |= ZYDIS_ATTRIB_HAS_SIB;
}
2017-01-23 00:38:14 +08:00
// Has displacement or is rBP and we have no SIB?
// rBP can't be ModRM-encoded without a disp.
if (operand->mem.disp || (!(ctx->req->attributes & ZYDIS_ATTRIB_HAS_SIB)
&& ZydisIsBPReg(operand->mem.base)))
{
ctx->dispBitSize = 32;
ctx->raw.modrm.mod = 0x02 /* 32 bit disp */;
ctx->disp = operand->mem.disp;
}
// No displacement.
else
{
ctx->raw.modrm.mod = 0x00 /* no disp */;
}
return ZYDIS_STATUS_SUCCESS;
}
static ZydisStatus ZydisPrepareOperand(ZydisEncoderContext* ctx,
2017-07-10 20:34:25 +08:00
ZydisEncoderOperand* operand, const ZydisOperandDefinition* operandDef)
2017-01-18 03:53:34 +08:00
{
ZYDIS_ASSERT(ctx);
ZYDIS_ASSERT(operand);
2017-07-10 20:34:25 +08:00
ZYDIS_ASSERT(operandDef);
ZYDIS_ASSERT(operandDef->type != ZYDIS_SEMANTIC_OPTYPE_IMPLICIT_REG);
ZYDIS_ASSERT(operandDef->type != ZYDIS_SEMANTIC_OPTYPE_IMPLICIT_MEM);
ZYDIS_ASSERT(operandDef->type != ZYDIS_SEMANTIC_OPTYPE_IMPLICIT_IMM1);
2017-07-10 20:34:25 +08:00
switch (operandDef->op.encoding)
2017-01-18 03:53:34 +08:00
{
case ZYDIS_OPERAND_ENCODING_NONE:
2017-07-10 20:34:25 +08:00
{
2017-01-24 04:52:26 +08:00
// For some encodings, we have to switch on the sem op type.
2017-07-10 20:34:25 +08:00
if (operandDef->type == ZYDIS_SEMANTIC_OPTYPE_MOFFS)
{
ZYDIS_CHECK(ZydisPrepareSegmentPrefix(
ctx, operand->mem.segment, ZYDIS_REGISTER_NONE
));
ctx->imms[0] = operand->mem.disp;
ctx->immBitSizes[0] = operand->mem.dispSize;
}
} break;
case ZYDIS_OPERAND_ENCODING_MODRM_REG:
2017-01-18 03:53:34 +08:00
{
ZYDIS_ASSERT(!ctx->raw.modrm.reg);
2017-01-20 07:54:48 +08:00
ZYDIS_CHECK(ZydisPrepareRegOperand(ctx, operand->reg, 'R'));
2017-01-18 03:53:34 +08:00
} break;
case ZYDIS_OPERAND_ENCODING_MODRM_RM:
2017-01-18 03:53:34 +08:00
{
ZYDIS_ASSERT(!ctx->raw.modrm.mod);
ZYDIS_ASSERT(!ctx->raw.modrm.rm);
ZYDIS_ASSERT(!ctx->raw.sib.base);
ZYDIS_ASSERT(!ctx->raw.sib.index);
ZYDIS_ASSERT(!ctx->raw.sib.scale);
2017-01-18 03:53:34 +08:00
// Memory operand?
if (operand->type == ZYDIS_OPERAND_TYPE_MEMORY)
2017-01-18 03:53:34 +08:00
{
2017-07-10 20:34:25 +08:00
ZYDIS_CHECK(ZydisPrepareMemoryOperand(ctx, operand, operandDef));
2017-01-18 03:53:34 +08:00
}
// Nope, register.
else if (operand->type == ZYDIS_OPERAND_TYPE_REGISTER)
2017-01-18 03:53:34 +08:00
{
2017-01-20 07:54:48 +08:00
ZYDIS_CHECK(ZydisPrepareRegOperand(ctx, operand->reg, 'B'));
ctx->raw.modrm.mod = 0x03 /* reg */;
2017-01-18 03:53:34 +08:00
}
ctx->req->attributes |= ZYDIS_ATTRIB_HAS_MODRM;
break;
2017-01-18 03:53:34 +08:00
}
case ZYDIS_OPERAND_ENCODING_OPCODE:
{
int16_t reg = ZydisRegisterGetId(operand->reg);
if (reg == -1) return ZYDIS_STATUS_INVALID_PARAMETER;
ctx->raw.opcode += reg & 0x07;
ctx->raw.rex.B = (reg & 0x08) >> 3;
if (ctx->raw.rex.B) ctx->req->attributes |= ZYDIS_ATTRIB_HAS_REX;
break;
}
case ZYDIS_OPERAND_ENCODING_NDSNDD:
{
int16_t reg = ZydisRegisterGetId(operand->reg);
if (reg == -1) return ZYDIS_STATUS_INVALID_PARAMETER;
// TODO: Conditional assignment instead?
ctx->raw.vex.vvvv = (reg & 0x0F);
ctx->raw.xop.vvvv = (reg & 0x0F);
ctx->raw.evex.vvvv = (reg & 0x0F);
break;
}
case ZYDIS_OPERAND_ENCODING_MASK:
return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION;
2017-01-18 03:53:34 +08:00
break; // TODO
case ZYDIS_OPERAND_ENCODING_IS4:
{
ctx->immBitSizes[0] = 8;
ctx->imms[0] |= operand->imm.u & 0x0F;
break;
}
// TODO
//case ZYDIS_OPERAND_ENCODING_IS4:
//{
// ctx->immBitSizes[0] = 8;
// ctx->imms[0] |= (operand->imm.u & 0x0F) << 4;
// break;
//}
case ZYDIS_OPERAND_ENCODING_SIMM8:
case ZYDIS_OPERAND_ENCODING_UIMM8:
case ZYDIS_OPERAND_ENCODING_SIMM16:
case ZYDIS_OPERAND_ENCODING_UIMM16:
case ZYDIS_OPERAND_ENCODING_SIMM32:
case ZYDIS_OPERAND_ENCODING_UIMM32:
case ZYDIS_OPERAND_ENCODING_UIMM64:
case ZYDIS_OPERAND_ENCODING_SIMM64:
{
uint8_t immIdx = ctx->immBitSizes[0] ? 1 : 0;
ctx->immBitSizes[immIdx] = operand->immSize;
ctx->imms[immIdx] = operand->imm.u;
break;
}
2017-01-18 03:53:34 +08:00
default:
ZYDIS_UNREACHABLE;
}
return ZYDIS_STATUS_SUCCESS;
}
static ZydisStatus ZydisPrepareMandatoryPrefixes(ZydisEncoderContext* ctx)
{
ZYDIS_ASSERT(ctx);
// Is a prefix mandatory? 0x00 is a sentinel value for `None` in the table.
2017-07-10 20:34:25 +08:00
uint8_t prefix = ctx->matchingInsn->mandatoryPrefix;
if (prefix != 0x00)
{
switch (ctx->req->encoding)
{
case ZYDIS_INSTRUCTION_ENCODING_DEFAULT:
case ZYDIS_INSTRUCTION_ENCODING_3DNOW:
2017-07-10 20:34:25 +08:00
ctx->emitMandatoryPrefix = ZYDIS_TRUE;
ctx->mandatoryPrefix = prefix;
break;
case ZYDIS_INSTRUCTION_ENCODING_VEX:
2017-07-10 20:34:25 +08:00
ctx->raw.vex.pp = prefix;
break;
case ZYDIS_INSTRUCTION_ENCODING_EVEX:
2017-07-10 20:34:25 +08:00
ctx->raw.evex.pp = prefix;
break;
case ZYDIS_INSTRUCTION_ENCODING_XOP:
2017-07-10 20:34:25 +08:00
ctx->raw.xop.pp = prefix;
break;
2017-07-10 20:34:25 +08:00
default:
ZYDIS_UNREACHABLE;
}
}
return ZYDIS_STATUS_SUCCESS;
}
2017-07-10 20:34:25 +08:00
static ZydisStatus ZydisDetermineInstructionEncoding(ZydisEncoderRequest* ctx)
{
2017-07-10 20:34:25 +08:00
ZYDIS_ASSERT(ctx);
2017-07-10 20:34:25 +08:00
for (const ZydisEncoderOperand *op = ctx->operands,
*end = ctx->operands + ctx->operandCount
; op < end
; ++op)
{
2017-07-10 20:34:25 +08:00
}
return ZYDIS_STATUS_SUCCESS;
}
2017-07-10 20:34:25 +08:00
static ZydisStatus ZydisFindMatchingDef(
ZydisEncoderContext* ctx, const ZydisEncoderRequest* req)
2017-01-18 03:53:34 +08:00
{
2017-07-10 20:34:25 +08:00
ZYDIS_ASSERT(ctx);
ZYDIS_ASSERT(req);
2017-01-18 03:53:34 +08:00
2017-07-10 20:34:25 +08:00
// Translate requested mode to flags.
uint8_t modeFlag;
switch (req->encoding)
{
case 16: modeFlag = 0x01; break;
case 32: modeFlag = 0x02; break;
case 64: modeFlag = 0x04; break;
default: return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO
}
// Walk list of candidates.
const ZydisEncodableInstruction* insns = NULL;
uint8_t insnCount = ZydisGetEncodableInstructions(req->mnemonic, &insns);
if (!insnCount) return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO
ZYDIS_ASSERT(insns);
for (uint8_t i = 0; i < insnCount; ++i)
2017-01-18 03:53:34 +08:00
{
2017-07-10 20:34:25 +08:00
const ZydisEncodableInstruction* candidateInsn = insns + i;
if (!(candidateInsn->mode & modeFlag)) goto _nextInsn;
const ZydisInstructionDefinition* candidateDef = NULL;
ZydisGetInstructionDefinition(
candidateInsn->encoding, insns->definitionReference, &candidateDef);
ZydisOperandDefinition* candidateOperands = NULL;
uint8_t defOperandCount = ZydisGetOperandDefinitions(candidateDef, &candidateOperands);
if (req->operandCount > defOperandCount) goto _nextInsn;
const ZydisOperandDefinition* curDefOperand = candidateOperands;
for (uint8_t k = 0; k < req->operandCount; ++k)
2017-01-18 03:53:34 +08:00
{
2017-07-10 20:34:25 +08:00
curDefOperand = candidateOperands + k;
const ZydisEncoderOperand* curReqOperand = req->operands + k;
// Visible operands are always in the front. When we encounter the first hidden
// operand in the definition and haven't exhausted the request operands yet,
// it's safe to assume this isn't the instruction we're looking for.
if (curDefOperand->visibility == ZYDIS_OPERAND_VISIBILITY_HIDDEN) goto _nextInsn;
if (curDefOperand->type != curReqOperand->type) continue;
// blah blah more checks
2017-01-18 03:53:34 +08:00
}
2017-07-10 20:34:25 +08:00
// Make sure we compared either all operands or the remaining operands are hidden.
if (req->operandCount != defOperandCount &&
(curDefOperand + 1)->visibility != ZYDIS_OPERAND_VISIBILITY_HIDDEN)
2017-01-18 03:53:34 +08:00
{
2017-07-10 20:34:25 +08:00
goto _nextInsn;
2017-01-18 03:53:34 +08:00
}
2017-07-10 20:34:25 +08:00
// Still here? Looks like we found our instruction, then!
ctx->matchingInsn = candidateInsn;
ctx->matchingDef = candidateDef;
ctx->matchingOperandCount = req->operandCount;
2017-01-18 03:53:34 +08:00
return ZYDIS_STATUS_SUCCESS;
2017-07-10 20:34:25 +08:00
_nextInsn:
2017-01-18 03:53:34 +08:00
;
}
return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION;
}
2017-01-23 00:38:14 +08:00
/* ---------------------------------------------------------------------------------------------- */
2017-01-13 01:53:28 +08:00
/* ============================================================================================== */
/* Implementation of public functions */
/* ============================================================================================== */
2017-07-10 20:34:25 +08:00
#ifdef ZYDIS_ENABLE_FEATURE_DECODER
ZydisStatus ZydisEncoderDecodedInstructionToRequest(
const ZydisDecodedInstruction* in, ZydisEncoderRequest* out)
{
ZYDIS_ASSERT(in);
ZYDIS_ASSERT(out);
(void) in; (void) out;
// TODO
return ZYDIS_STATUS_SUCCESS;
}
#endif
ZydisStatus ZydisEncoderEncodeInstruction(void* buffer, size_t* bufferLen,
ZydisEncoderRequest* request)
2017-01-13 01:53:28 +08:00
{
2017-07-10 20:34:25 +08:00
if (!request || !bufferLen || request->operandCount > ZYDIS_ARRAY_SIZE(request->operands))
{
return ZYDIS_STATUS_INVALID_PARAMETER;
}
if (!buffer || !*bufferLen) return ZYDIS_STATUS_INSUFFICIENT_BUFFER_SIZE;
ZydisEncoderContext ctx;
memset(&ctx, 0, sizeof(ctx));
ctx.buffer = (uint8_t*)buffer;
ctx.bufferLen = *bufferLen;
ctx.writeOffs = 0;
ctx.req = request;
*bufferLen = 0;
2017-01-13 01:53:28 +08:00
2017-01-18 03:53:34 +08:00
// Mask out attributes that can't be set explicitly by user.
request->attributes &= ZYDIS_USER_ENCODABLE_ATTRIB_MASK;
2017-01-18 03:53:34 +08:00
// Search matching instruction, collect information about what needs to be
// encoded, what prefixes are required, etc.
2017-07-10 20:34:25 +08:00
ZYDIS_CHECK(ZydisFindMatchingDef(&ctx, request));
ctx.raw.opcode = ctx.matchingInsn->opcode;
2017-01-18 03:53:34 +08:00
// Check compatibility of requested prefixes to found instruction.
2017-07-10 20:34:25 +08:00
// TODO
//if (ctx.req->attributes)
//{
// for (size_t i = 0; i < ZYDIS_ARRAY_SIZE(prefixAcceptMap); ++i)
// {
// if (ctx.req->attributes & prefixAcceptMap[i].has &&
// !(ctx.matchingDef->attribs & prefixAcceptMap[i].accepts))
// {
// // TODO: Better status.
// return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION;
// }
// }
//}
// Determine required prefixes.
2017-07-10 20:34:25 +08:00
switch (ctx.matchingInsn->encoding)
{
case ZYDIS_INSTRUCTION_ENCODING_EVEX:
request->attributes |= ZYDIS_ATTRIB_HAS_EVEX;
break;
case ZYDIS_INSTRUCTION_ENCODING_VEX:
request->attributes |= ZYDIS_ATTRIB_HAS_VEX;
break;
case ZYDIS_INSTRUCTION_ENCODING_XOP:
request->attributes |= ZYDIS_ATTRIB_HAS_XOP;
break;
default:
ZYDIS_UNREACHABLE;
}
// Prepare prefix bits.
2017-07-10 20:34:25 +08:00
ctx.raw.evex.B = ctx.matchingInsn->evexB;
ctx.raw.evex.L = ctx.matchingInsn->vectorLength & 0x01;
ctx.raw.evex.L2 = ctx.matchingInsn->vectorLength & 0x02;
ctx.raw.vex.L = ctx.matchingInsn->vectorLength & 0x01;
if (ctx.matchingInsn->rexW)
2017-01-24 01:31:50 +08:00
{
ctx.raw.rex.W = 1;
request->attributes |= ZYDIS_ATTRIB_HAS_REX;
2017-01-24 01:31:50 +08:00
}
ZYDIS_CHECK(ZydisPrepareMandatoryPrefixes(&ctx));
// Prepare opcode.
ZYDIS_CHECK(ZydisPrepareOpcode(&ctx));
// Some instructions have additional opcode bits encoded in ModRM.reg.
2017-07-10 20:34:25 +08:00
if (ctx.matchingInsn->modrmReg != 0xFF)
{
2017-07-10 20:34:25 +08:00
ctx.raw.modrm.reg = ctx.matchingInsn->modrmReg;
}
2017-01-18 03:53:34 +08:00
// Analyze and prepare operands.
if (request->operandCount > ZYDIS_ARRAY_SIZE(request->operands))
{
// TODO: Better status?
return ZYDIS_STATUS_INVALID_PARAMETER;
}
2017-07-10 20:34:25 +08:00
//for (size_t i = 0; i < ctx.matchingDef->operandCount; ++i)
//{
// ZYDIS_CHECK(ZydisPrepareOperand(
// &ctx, &request->operands[i],
// &ctx.matchingDef->operands[i]
// ));
//}
//
2017-01-18 03:53:34 +08:00
// Do actual encoding work.
ZYDIS_CHECK(ZydisEmitLegacyPrefixes(&ctx));
if (request->attributes & ZYDIS_ATTRIB_HAS_REX ) ZYDIS_CHECK(ZydisEmitREX (&ctx));
if (request->attributes & ZYDIS_ATTRIB_HAS_VEX ) ZYDIS_CHECK(ZydisEmitVEX (&ctx));
if (request->attributes & ZYDIS_ATTRIB_HAS_EVEX ) ZYDIS_CHECK(ZydisEmitEVEX (&ctx));
if (request->attributes & ZYDIS_ATTRIB_HAS_XOP ) ZYDIS_CHECK(ZydisEmitXOP (&ctx));
2017-07-10 20:34:25 +08:00
if (ctx.emitMandatoryPrefix) ZYDIS_CHECK(ZydisEmitByte(&ctx, ctx.mandatoryPrefix));
for (uint8_t i = 0; i < ctx.opcodeMapPrefixLen; ++i)
{
ZYDIS_CHECK(ZydisEmitByte(&ctx, ctx.opcodeMapPrefix[i]));
}
ZYDIS_CHECK(ZydisEmitByte(&ctx, ctx.raw.opcode));
if (request->attributes & ZYDIS_ATTRIB_HAS_MODRM) ZYDIS_CHECK(ZydisEmitModRM(&ctx));
if (request->attributes & ZYDIS_ATTRIB_HAS_SIB ) ZYDIS_CHECK(ZydisEmitSIB (&ctx));
2017-01-20 07:54:48 +08:00
if (ctx.dispBitSize ) ZYDIS_CHECK(ZydisEmitImm(&ctx, ctx.disp, ctx.dispBitSize ));
if (ctx.immBitSizes[0]) ZYDIS_CHECK(ZydisEmitImm(&ctx, ctx.imms[0], ctx.immBitSizes[0]));
if (ctx.immBitSizes[1]) ZYDIS_CHECK(ZydisEmitImm(&ctx, ctx.imms[1], ctx.immBitSizes[1]));
*bufferLen = ctx.writeOffs;
return ZYDIS_STATUS_SUCCESS;
}
/* ============================================================================================== */