Major rework of encoder context design

- Split into various smaller structs
- Only hand functions parts they actually need
This commit is contained in:
Joel Höner 2017-07-28 03:13:30 +02:00
parent 9152714865
commit 5ac595eb72
1 changed files with 224 additions and 208 deletions

View File

@ -37,9 +37,118 @@
typedef uint32_t ZydisSemanticOperandTypeMask; typedef uint32_t ZydisSemanticOperandTypeMask;
/** typedef struct ZydisInstructionQuery_
* @brief The encoder context struct. {
*/ ZydisSemanticOperandTypeMask semOperandTypeMasks[ZYDIS_ENCODER_MAX_OPERANDS];
ZydisBool require66;
ZydisBool require67;
ZydisBool requireREXW;
uint8_t eosz;
uint8_t easz;
} ZydisInstructionQuery;
typedef struct ZydisInstructionMatch_
{
const ZydisEncodableInstruction* insn;
const ZydisInstructionDefinition* def;
uint8_t operandCount;
const ZydisOperandDefinition* operands;
uint8_t derivedImmSizes[ZYDIS_ENCODER_MAX_OPERANDS];
} ZydisInstructionMatch;
typedef struct ZydisRawInstruction_
{
ZydisInstructionAttributes derivedAttrs;
uint8_t mandatoryPrefix; // 0 = not present
uint8_t opcodeMapPrefixLen;
uint8_t opcodeMapPrefix[3];
uint8_t opcode;
struct
{
int64_t val;
uint8_t size;
} disp;
struct
{
uint64_t val;
uint8_t size;
} imms[2];
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;
} ZydisRawInstruction;
typedef struct ZydisEncoderContext_ typedef struct ZydisEncoderContext_
{ {
// Input parameters. // Input parameters.
@ -48,109 +157,7 @@ typedef struct ZydisEncoderContext_
size_t writeOffs; size_t writeOffs;
const ZydisEncoderRequest* req; const ZydisEncoderRequest* req;
// Definition requirements (filled by `ZydisAnalyzeRequirements`) ZydisRawInstruction raw;
ZydisSemanticOperandTypeMask semOperandTypeMasks[ZYDIS_ENCODER_MAX_OPERANDS];
uint8_t derivedImmSize[ZYDIS_ENCODER_MAX_OPERANDS];
ZydisBool require66;
ZydisBool require67;
ZydisBool requireREXW;
uint8_t eosz;
uint8_t easz;
// Found matching definition info (filled by `ZydisFindMatchingDef`)
const ZydisEncodableInstruction* matchingInsn;
const ZydisInstructionDefinition* matchingDef;
uint8_t matchingOperandCount;
const ZydisOperandDefinition* matchingOperands;
ZydisInstructionAttributes derivedAttrs;
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];
// Prepared, raw instruction data (filled by `ZydisPrepare*` funcs)
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;
} raw;
} ZydisEncoderContext; } ZydisEncoderContext;
/* ============================================================================================== */ /* ============================================================================================== */
@ -646,29 +653,28 @@ static ZydisBool ZydisSemanticTypeIsImplicit(ZydisSemanticOperandType type)
/* Preparation functions. Parse encoder request, determine required bytes and prefixes. */ /* Preparation functions. Parse encoder request, determine required bytes and prefixes. */
/* ---------------------------------------------------------------------------------------------- */ /* ---------------------------------------------------------------------------------------------- */
static ZydisStatus ZydisPrepareOpcode(ZydisEncoderContext* ctx) static ZydisStatus ZydisPrepareOpcode(ZydisEncoderContext* ctx, const ZydisInstructionMatch* match)
{ {
ZYDIS_ASSERT(ctx); ZYDIS_ASSERT(ctx);
ZYDIS_ASSERT(ctx->matchingDef); ZYDIS_ASSERT(match);
ZYDIS_ASSERT(ctx->req);
// Put opcode map prefix(es), if required. // Put opcode map prefix(es), if required.
switch (ctx->req->encoding) switch (ctx->req->encoding)
{ {
case ZYDIS_INSTRUCTION_ENCODING_DEFAULT: case ZYDIS_INSTRUCTION_ENCODING_DEFAULT:
case ZYDIS_INSTRUCTION_ENCODING_3DNOW: case ZYDIS_INSTRUCTION_ENCODING_3DNOW:
switch (ctx->matchingInsn->opcodeMap) switch (match->insn->opcodeMap)
{ {
case ZYDIS_OPCODE_MAP_0F: case ZYDIS_OPCODE_MAP_0F:
ctx->opcodeMapPrefix[ctx->opcodeMapPrefixLen++] = 0x0F; ctx->raw.opcodeMapPrefix[ctx->raw.opcodeMapPrefixLen++] = 0x0F;
break; break;
case ZYDIS_OPCODE_MAP_0F38: case ZYDIS_OPCODE_MAP_0F38:
ctx->opcodeMapPrefix[ctx->opcodeMapPrefixLen++] = 0x0F; ctx->raw.opcodeMapPrefix[ctx->raw.opcodeMapPrefixLen++] = 0x0F;
ctx->opcodeMapPrefix[ctx->opcodeMapPrefixLen++] = 0x38; ctx->raw.opcodeMapPrefix[ctx->raw.opcodeMapPrefixLen++] = 0x38;
break; break;
case ZYDIS_OPCODE_MAP_0F3A: case ZYDIS_OPCODE_MAP_0F3A:
ctx->opcodeMapPrefix[ctx->opcodeMapPrefixLen++] = 0x0F; ctx->raw.opcodeMapPrefix[ctx->raw.opcodeMapPrefixLen++] = 0x0F;
ctx->opcodeMapPrefix[ctx->opcodeMapPrefixLen++] = 0x3A; ctx->raw.opcodeMapPrefix[ctx->raw.opcodeMapPrefixLen++] = 0x3A;
break; break;
case ZYDIS_OPCODE_MAP_DEFAULT: case ZYDIS_OPCODE_MAP_DEFAULT:
break; // Nothing to do. break; // Nothing to do.
@ -677,16 +683,16 @@ static ZydisStatus ZydisPrepareOpcode(ZydisEncoderContext* ctx)
} }
break; break;
case ZYDIS_INSTRUCTION_ENCODING_VEX: case ZYDIS_INSTRUCTION_ENCODING_VEX:
ctx->raw.vex.m_mmmm = ctx->matchingInsn->opcodeMap; ctx->raw.vex.m_mmmm = match->insn->opcodeMap;
ZYDIS_ASSERT(ctx->raw.vex.m_mmmm <= 0x03); ZYDIS_ASSERT(ctx->raw.vex.m_mmmm <= 0x03);
break; break;
case ZYDIS_INSTRUCTION_ENCODING_EVEX: case ZYDIS_INSTRUCTION_ENCODING_EVEX:
ctx->raw.evex.mm = ctx->matchingInsn->opcodeMap; ctx->raw.evex.mm = match->insn->opcodeMap;
ZYDIS_ASSERT(ctx->raw.evex.mm <= 0x03); ZYDIS_ASSERT(ctx->raw.evex.mm <= 0x03);
break; break;
case ZYDIS_INSTRUCTION_ENCODING_XOP: case ZYDIS_INSTRUCTION_ENCODING_XOP:
ctx->raw.xop.m_mmmm = ctx->raw.xop.m_mmmm =
ctx->matchingInsn->opcodeMap - ZYDIS_OPCODE_MAP_XOP8 + 0x08; match->insn->opcodeMap - ZYDIS_OPCODE_MAP_XOP8 + 0x08;
ZYDIS_ASSERT(ctx->raw.xop.m_mmmm >= 0x08); ZYDIS_ASSERT(ctx->raw.xop.m_mmmm >= 0x08);
ZYDIS_ASSERT(ctx->raw.xop.m_mmmm <= 0x0B); ZYDIS_ASSERT(ctx->raw.xop.m_mmmm <= 0x0B);
break; break;
@ -730,7 +736,7 @@ static ZydisStatus ZydisPrepareRegOperand(ZydisEncoderContext* ctx,
case 'X': ctx->raw.rex.X = topBit; break; case 'X': ctx->raw.rex.X = topBit; break;
default: ZYDIS_UNREACHABLE; default: ZYDIS_UNREACHABLE;
} }
if (topBit) ctx->derivedAttrs |= ZYDIS_ATTRIB_HAS_REX; if (topBit) ctx->raw.derivedAttrs |= ZYDIS_ATTRIB_HAS_REX;
break; break;
case ZYDIS_INSTRUCTION_ENCODING_VEX: case ZYDIS_INSTRUCTION_ENCODING_VEX:
switch (topBitLoc) switch (topBitLoc)
@ -773,28 +779,28 @@ static ZydisStatus ZydisPrepareSegmentPrefix(ZydisEncoderContext* ctx,
switch (segment) switch (segment)
{ {
case ZYDIS_REGISTER_ES: case ZYDIS_REGISTER_ES:
ctx->derivedAttrs |= ZYDIS_ATTRIB_HAS_SEGMENT_ES; ctx->raw.derivedAttrs |= ZYDIS_ATTRIB_HAS_SEGMENT_ES;
break; break;
case ZYDIS_REGISTER_SS: case ZYDIS_REGISTER_SS:
if (!ZydisRegIsStack(base)) if (!ZydisRegIsStack(base))
{ {
ctx->derivedAttrs |= ZYDIS_ATTRIB_HAS_SEGMENT_SS; ctx->raw.derivedAttrs |= ZYDIS_ATTRIB_HAS_SEGMENT_SS;
} }
break; break;
case ZYDIS_REGISTER_CS: case ZYDIS_REGISTER_CS:
ctx->derivedAttrs |= ZYDIS_ATTRIB_HAS_SEGMENT_CS; ctx->raw.derivedAttrs |= ZYDIS_ATTRIB_HAS_SEGMENT_CS;
break; break;
case ZYDIS_REGISTER_DS: case ZYDIS_REGISTER_DS:
if (ZydisRegIsStack(base)) if (ZydisRegIsStack(base))
{ {
ctx->derivedAttrs |= ZYDIS_ATTRIB_HAS_SEGMENT_DS; ctx->raw.derivedAttrs |= ZYDIS_ATTRIB_HAS_SEGMENT_DS;
} }
break; break;
case ZYDIS_REGISTER_FS: case ZYDIS_REGISTER_FS:
ctx->derivedAttrs |= ZYDIS_ATTRIB_HAS_SEGMENT_FS; ctx->raw.derivedAttrs |= ZYDIS_ATTRIB_HAS_SEGMENT_FS;
break; break;
case ZYDIS_REGISTER_GS: case ZYDIS_REGISTER_GS:
ctx->derivedAttrs |= ZYDIS_ATTRIB_HAS_SEGMENT_GS; ctx->raw.derivedAttrs |= ZYDIS_ATTRIB_HAS_SEGMENT_GS;
break; break;
default: default:
return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO: Better status. return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO: Better status.
@ -815,8 +821,8 @@ static ZydisStatus ZydisPrepareMemoryOperand(ZydisEncoderContext* ctx,
// Absolute memory access? Special case. // Absolute memory access? Special case.
if (operand->mem.base == ZYDIS_REGISTER_NONE) if (operand->mem.base == ZYDIS_REGISTER_NONE)
{ {
ctx->disp = operand->mem.disp; ctx->raw.disp.val = operand->mem.disp;
ctx->dispBitSize = 32; ctx->raw.disp.size = 32;
// In 32 bit mode, ModRM allows for a shortcut here. // In 32 bit mode, ModRM allows for a shortcut here.
if (ctx->req->machineMode == 32) if (ctx->req->machineMode == 32)
@ -832,7 +838,7 @@ static ZydisStatus ZydisPrepareMemoryOperand(ZydisEncoderContext* ctx,
ctx->raw.sib.index = 0x04 /* none */; ctx->raw.sib.index = 0x04 /* none */;
ctx->raw.sib.scale = 0x00 /* * 1 */; ctx->raw.sib.scale = 0x00 /* * 1 */;
ctx->raw.sib.base = 0x05; ctx->raw.sib.base = 0x05;
ctx->derivedAttrs |= ZYDIS_ATTRIB_HAS_SIB; ctx->raw.derivedAttrs |= ZYDIS_ATTRIB_HAS_SIB;
} }
return ZYDIS_STATUS_SUCCESS; return ZYDIS_STATUS_SUCCESS;
@ -853,14 +859,14 @@ static ZydisStatus ZydisPrepareMemoryOperand(ZydisEncoderContext* ctx,
return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO
} }
ctx->disp = operand->mem.disp; ctx->raw.disp.val = operand->mem.disp;
ctx->dispBitSize = 32; ctx->raw.disp.size = 32;
ctx->raw.modrm.mod = 0x00; ctx->raw.modrm.mod = 0x00;
ctx->raw.modrm.rm = 0x05 /* RIP relative mem */; ctx->raw.modrm.rm = 0x05 /* RIP relative mem */;
if (operand->mem.base == ZYDIS_REGISTER_EIP) if (operand->mem.base == ZYDIS_REGISTER_EIP)
{ {
ctx->derivedAttrs |= ZYDIS_ATTRIB_HAS_ADDRESSSIZE; ctx->raw.derivedAttrs |= ZYDIS_ATTRIB_HAS_ADDRESSSIZE;
} }
return ZYDIS_STATUS_SUCCESS; return ZYDIS_STATUS_SUCCESS;
@ -879,7 +885,7 @@ static ZydisStatus ZydisPrepareMemoryOperand(ZydisEncoderContext* ctx,
case 16: case 16:
break; // Nothing to do. break; // Nothing to do.
case 32: case 32:
ctx->derivedAttrs |= ZYDIS_ATTRIB_HAS_ADDRESSSIZE; ctx->raw.derivedAttrs |= ZYDIS_ATTRIB_HAS_ADDRESSSIZE;
break; break;
case 64: case 64:
// AMD64 doesn't allow for 16 bit addressing. // AMD64 doesn't allow for 16 bit addressing.
@ -896,7 +902,7 @@ static ZydisStatus ZydisPrepareMemoryOperand(ZydisEncoderContext* ctx,
case 32: case 32:
break; // Nothing to do. break; // Nothing to do.
case 64: case 64:
ctx->derivedAttrs |= ZYDIS_ATTRIB_HAS_ADDRESSSIZE; ctx->raw.derivedAttrs |= ZYDIS_ATTRIB_HAS_ADDRESSSIZE;
default: default:
return ZYDIS_STATUS_INVALID_PARAMETER; // TODO return ZYDIS_STATUS_INVALID_PARAMETER; // TODO
} }
@ -944,7 +950,7 @@ static ZydisStatus ZydisPrepareMemoryOperand(ZydisEncoderContext* ctx,
ctx->raw.sib.index = 0x04 /* no index */; ctx->raw.sib.index = 0x04 /* no index */;
} }
ctx->derivedAttrs |= ZYDIS_ATTRIB_HAS_SIB; ctx->raw.derivedAttrs |= ZYDIS_ATTRIB_HAS_SIB;
} }
// Has displacement or is rBP and we have no SIB? // Has displacement or is rBP and we have no SIB?
@ -952,9 +958,9 @@ static ZydisStatus ZydisPrepareMemoryOperand(ZydisEncoderContext* ctx,
if (operand->mem.disp || (!(ctx->req->attributes & ZYDIS_ATTRIB_HAS_SIB) if (operand->mem.disp || (!(ctx->req->attributes & ZYDIS_ATTRIB_HAS_SIB)
&& ZydisRegIsBP(operand->mem.base))) && ZydisRegIsBP(operand->mem.base)))
{ {
ctx->dispBitSize = 32; ctx->raw.disp.size = 32;
ctx->raw.modrm.mod = 0x02 /* 32 bit disp */; ctx->raw.modrm.mod = 0x02 /* 32 bit disp */;
ctx->disp = operand->mem.disp; ctx->raw.disp.val = operand->mem.disp;
} }
// No displacement. // No displacement.
else else
@ -965,11 +971,12 @@ static ZydisStatus ZydisPrepareMemoryOperand(ZydisEncoderContext* ctx,
return ZYDIS_STATUS_SUCCESS; return ZYDIS_STATUS_SUCCESS;
} }
static ZydisStatus ZydisPrepareOperand(ZydisEncoderContext* ctx, uint8_t n) static ZydisStatus ZydisPrepareOperand(ZydisEncoderContext* ctx,
ZydisInstructionMatch* match, uint8_t n)
{ {
ZYDIS_ASSERT(ctx); ZYDIS_ASSERT(ctx);
const ZydisEncoderOperand* reqOperand = ctx->req->operands + n; const ZydisEncoderOperand* reqOperand = ctx->req->operands + n;
const ZydisOperandDefinition* operandDef = ctx->matchingOperands + n; const ZydisOperandDefinition* operandDef = match->operands + n;
//ZYDIS_ASSERT(!ZydisSemanticTypeIsImplicit(operandDef->type)); //ZYDIS_ASSERT(!ZydisSemanticTypeIsImplicit(operandDef->type));
switch (operandDef->op.encoding) switch (operandDef->op.encoding)
@ -982,8 +989,8 @@ static ZydisStatus ZydisPrepareOperand(ZydisEncoderContext* ctx, uint8_t n)
ZYDIS_CHECK(ZydisPrepareSegmentPrefix( ZYDIS_CHECK(ZydisPrepareSegmentPrefix(
ctx, reqOperand->mem.segment, ZYDIS_REGISTER_NONE ctx, reqOperand->mem.segment, ZYDIS_REGISTER_NONE
)); ));
ctx->imms[0] = reqOperand->mem.disp; ctx->raw.imms[0].val = reqOperand->mem.disp;
ctx->immBitSizes[0] = reqOperand->mem.dispSize; ctx->raw.imms[0].size = reqOperand->mem.dispSize;
} }
} break; } break;
case ZYDIS_OPERAND_ENCODING_MODRM_REG: case ZYDIS_OPERAND_ENCODING_MODRM_REG:
@ -1011,7 +1018,7 @@ static ZydisStatus ZydisPrepareOperand(ZydisEncoderContext* ctx, uint8_t n)
ctx->raw.modrm.mod = 0x03 /* reg */; ctx->raw.modrm.mod = 0x03 /* reg */;
} }
ctx->derivedAttrs |= ZYDIS_ATTRIB_HAS_MODRM; ctx->raw.derivedAttrs |= ZYDIS_ATTRIB_HAS_MODRM;
break; break;
} }
case ZYDIS_OPERAND_ENCODING_OPCODE: case ZYDIS_OPERAND_ENCODING_OPCODE:
@ -1020,7 +1027,7 @@ static ZydisStatus ZydisPrepareOperand(ZydisEncoderContext* ctx, uint8_t n)
if (reg == -1) return ZYDIS_STATUS_INVALID_PARAMETER; if (reg == -1) return ZYDIS_STATUS_INVALID_PARAMETER;
ctx->raw.opcode += reg & 0x07; ctx->raw.opcode += reg & 0x07;
ctx->raw.rex.B = (reg & 0x08) >> 3; ctx->raw.rex.B = (reg & 0x08) >> 3;
if (ctx->raw.rex.B) ctx->derivedAttrs |= ZYDIS_ATTRIB_HAS_REX; if (ctx->raw.rex.B) ctx->raw.derivedAttrs |= ZYDIS_ATTRIB_HAS_REX;
break; break;
} }
case ZYDIS_OPERAND_ENCODING_NDSNDD: case ZYDIS_OPERAND_ENCODING_NDSNDD:
@ -1035,8 +1042,8 @@ static ZydisStatus ZydisPrepareOperand(ZydisEncoderContext* ctx, uint8_t n)
break; // TODO break; // TODO
case ZYDIS_OPERAND_ENCODING_IS4: case ZYDIS_OPERAND_ENCODING_IS4:
{ {
ctx->immBitSizes[0] = 8; ctx->raw.imms[0].size = 8;
ctx->imms[0] |= reqOperand->imm.u & 0x0F; ctx->raw.imms[0].val |= reqOperand->imm.u & 0x0F;
break; break;
} }
// TODO // TODO
@ -1059,9 +1066,9 @@ static ZydisStatus ZydisPrepareOperand(ZydisEncoderContext* ctx, uint8_t n)
case ZYDIS_OPERAND_ENCODING_SIMM64: case ZYDIS_OPERAND_ENCODING_SIMM64:
case ZYDIS_OPERAND_ENCODING_JIMM64: case ZYDIS_OPERAND_ENCODING_JIMM64:
{ {
uint8_t immIdx = ctx->immBitSizes[0] ? 1 : 0; uint8_t immIdx = ctx->raw.imms[0].size ? 1 : 0;
ctx->immBitSizes[immIdx] = ctx->derivedImmSize[n]; ctx->raw.imms[immIdx].val = reqOperand->imm.u;
ctx->imms[immIdx] = reqOperand->imm.u; ctx->raw.imms[immIdx].size = match->derivedImmSizes[n];
break; break;
} }
default: default:
@ -1071,20 +1078,21 @@ static ZydisStatus ZydisPrepareOperand(ZydisEncoderContext* ctx, uint8_t n)
return ZYDIS_STATUS_SUCCESS; return ZYDIS_STATUS_SUCCESS;
} }
static ZydisStatus ZydisPrepareMandatoryPrefixes(ZydisEncoderContext* ctx) static ZydisStatus ZydisPrepareMandatoryPrefixes(ZydisEncoderContext* ctx,
ZydisInstructionMatch* match)
{ {
ZYDIS_ASSERT(ctx); ZYDIS_ASSERT(ctx);
ZYDIS_ASSERT(match);
// Is a prefix mandatory? 0x00 is a sentinel value for `None` in the table. // Is a prefix mandatory? 0x00 is a sentinel value for `None` in the table.
uint8_t prefix = ctx->matchingInsn->mandatoryPrefix; uint8_t prefix = match->insn->mandatoryPrefix;
if (prefix != 0x00) if (prefix != 0x00)
{ {
switch (ctx->req->encoding) switch (ctx->req->encoding)
{ {
case ZYDIS_INSTRUCTION_ENCODING_DEFAULT: case ZYDIS_INSTRUCTION_ENCODING_DEFAULT:
case ZYDIS_INSTRUCTION_ENCODING_3DNOW: case ZYDIS_INSTRUCTION_ENCODING_3DNOW:
ctx->emitMandatoryPrefix = ZYDIS_TRUE; ctx->raw.mandatoryPrefix = prefix;
ctx->mandatoryPrefix = prefix;
break; break;
case ZYDIS_INSTRUCTION_ENCODING_VEX: case ZYDIS_INSTRUCTION_ENCODING_VEX:
ctx->raw.vex.pp = prefix; ctx->raw.vex.pp = prefix;
@ -1104,18 +1112,19 @@ static ZydisStatus ZydisPrepareMandatoryPrefixes(ZydisEncoderContext* ctx)
} }
static ZydisStatus ZydisAnalyzeRequirements( static ZydisStatus ZydisAnalyzeRequirements(
ZydisEncoderContext* ctx, const ZydisEncoderRequest* req) ZydisEncoderContext* ctx, const ZydisEncoderRequest* req, ZydisInstructionQuery* q)
{ {
ZYDIS_ASSERT(ctx); ZYDIS_ASSERT(ctx);
ZYDIS_ASSERT(req); ZYDIS_ASSERT(req);
ZYDIS_ASSERT(q);
// Walk list of requested operands, derive possible encodings // Walk list of requested operands, derive possible encodings
// and perform additional sanity checks. // and perform additional sanity checks.
ctx->require66 = ZYDIS_FALSE; q->require66 = ZYDIS_FALSE;
ctx->require67 = ZYDIS_FALSE; q->require67 = ZYDIS_FALSE;
ctx->requireREXW = ZYDIS_FALSE; q->requireREXW = ZYDIS_FALSE;
ctx->eosz = req->machineMode; q->eosz = req->machineMode;
ctx->easz = req->machineMode; q->easz = req->machineMode;
for (uint8_t i = 0; i < req->operandCount; ++i) for (uint8_t i = 0; i < req->operandCount; ++i)
{ {
const ZydisEncoderOperand* curReqOperand = req->operands + i; const ZydisEncoderOperand* curReqOperand = req->operands + i;
@ -1126,32 +1135,32 @@ static ZydisStatus ZydisAnalyzeRequirements(
switch (ZydisRegisterGetClass(curReqOperand->reg)) switch (ZydisRegisterGetClass(curReqOperand->reg))
{ {
case ZYDIS_REGCLASS_GPR16: case ZYDIS_REGCLASS_GPR16:
ctx->eosz = 16; q->eosz = 16;
switch (req->machineMode) switch (req->machineMode)
{ {
case 16: break; // Default mode. case 16: break; // Default mode.
case 32: case 32:
case 64: ctx->require66 = ZYDIS_TRUE; break; case 64: q->require66 = ZYDIS_TRUE; break;
default: return ZYDIS_STATUS_INVALID_PARAMETER; default: return ZYDIS_STATUS_INVALID_PARAMETER;
} }
break; break;
case ZYDIS_REGCLASS_GPR32: case ZYDIS_REGCLASS_GPR32:
ctx->eosz = 32; q->eosz = 32;
switch (req->machineMode) switch (req->machineMode)
{ {
case 16: ctx->require66 = ZYDIS_TRUE; break; case 16: q->require66 = ZYDIS_TRUE; break;
case 32: case 32:
case 64: break; // Default mode. case 64: break; // Default mode.
default: return ZYDIS_STATUS_INVALID_PARAMETER; default: return ZYDIS_STATUS_INVALID_PARAMETER;
} }
break; break;
case ZYDIS_REGCLASS_GPR64: case ZYDIS_REGCLASS_GPR64:
ctx->eosz = 64; q->eosz = 64;
switch (req->machineMode) switch (req->machineMode)
{ {
case 16: case 16:
case 32: return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; case 32: return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION;
case 64: ctx->requireREXW = ZYDIS_TRUE; break; case 64: q->requireREXW = ZYDIS_TRUE; break;
default: return ZYDIS_STATUS_INVALID_PARAMETER; default: return ZYDIS_STATUS_INVALID_PARAMETER;
} }
break; break;
@ -1175,27 +1184,27 @@ static ZydisStatus ZydisAnalyzeRequirements(
// Address size prefix required? // Address size prefix required?
switch (baseRegClass) switch (baseRegClass)
{ {
case ZYDIS_REGCLASS_GPR16: ctx->easz = 16; break; case ZYDIS_REGCLASS_GPR16: q->easz = 16; break;
case ZYDIS_REGCLASS_GPR32: ctx->easz = 32; break; case ZYDIS_REGCLASS_GPR32: q->easz = 32; break;
case ZYDIS_REGCLASS_GPR64: ctx->easz = 64; break; case ZYDIS_REGCLASS_GPR64: q->easz = 64; break;
default: default:
switch (baseRegClass) switch (baseRegClass)
{ {
case ZYDIS_REGISTER_IP: ctx->easz = 16; break; case ZYDIS_REGISTER_IP: q->easz = 16; break;
case ZYDIS_REGISTER_EIP: ctx->easz = 32; break; case ZYDIS_REGISTER_EIP: q->easz = 32; break;
case ZYDIS_REGISTER_RIP: ctx->easz = 64; break; case ZYDIS_REGISTER_RIP: q->easz = 64; break;
default: default:
; // Other registers can't be address-scaled. ; // Other registers can't be address-scaled.
} }
} }
switch (ctx->easz) switch (q->easz)
{ {
case 16: case 16:
switch (ctx->req->machineMode) switch (ctx->req->machineMode)
{ {
case 16: break; // Default mode. case 16: break; // Default mode.
case 32: ctx->require67 = ZYDIS_TRUE; break; case 32: q->require67 = ZYDIS_TRUE; break;
case 64: return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO case 64: return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO
default: return ZYDIS_STATUS_INVALID_PARAMETER; default: return ZYDIS_STATUS_INVALID_PARAMETER;
} }
@ -1203,9 +1212,9 @@ static ZydisStatus ZydisAnalyzeRequirements(
case 32: case 32:
switch (ctx->req->machineMode) switch (ctx->req->machineMode)
{ {
case 16: ctx->require67 = ZYDIS_TRUE; break; case 16: q->require67 = ZYDIS_TRUE; break;
case 32: break; // Default mode. case 32: break; // Default mode.
case 64: ctx->require67 = ZYDIS_TRUE; break; case 64: q->require67 = ZYDIS_TRUE; break;
default: return ZYDIS_STATUS_INVALID_PARAMETER; default: return ZYDIS_STATUS_INVALID_PARAMETER;
} }
break; break;
@ -1221,7 +1230,7 @@ static ZydisStatus ZydisAnalyzeRequirements(
} }
ZYDIS_CHECK(ZydisSemanticOperandTypeDeriveMask( ZYDIS_CHECK(ZydisSemanticOperandTypeDeriveMask(
req->operands + i, ctx->semOperandTypeMasks + i req->operands + i, q->semOperandTypeMasks + i
)); ));
} }
@ -1229,13 +1238,15 @@ static ZydisStatus ZydisAnalyzeRequirements(
} }
static ZydisStatus ZydisFindMatchingDef( static ZydisStatus ZydisFindMatchingDef(
ZydisEncoderContext* ctx, const ZydisEncoderRequest* req) ZydisEncoderContext* ctx, const ZydisEncoderRequest* req, ZydisInstructionMatch* match)
{ {
ZYDIS_ASSERT(ctx); ZYDIS_ASSERT(ctx);
ZYDIS_ASSERT(req); ZYDIS_ASSERT(req);
ZYDIS_ASSERT(match);
// Evaluate request. // Evaluate request.
ZYDIS_CHECK(ZydisAnalyzeRequirements(ctx, req)); ZydisInstructionQuery q;
ZYDIS_CHECK(ZydisAnalyzeRequirements(ctx, req, &q));
// Translate requested mode to flags. // Translate requested mode to flags.
uint8_t modeFlag; uint8_t modeFlag;
@ -1280,7 +1291,7 @@ static ZydisStatus ZydisFindMatchingDef(
if (curDefOperand->visibility == ZYDIS_OPERAND_VISIBILITY_HIDDEN) goto _nextInsn; if (curDefOperand->visibility == ZYDIS_OPERAND_VISIBILITY_HIDDEN) goto _nextInsn;
// Is the type one of those we permit for the given operand? // Is the type one of those we permit for the given operand?
if (!(1 << curDefOperand->type & ctx->semOperandTypeMasks[k])) goto _nextInsn; if (!(1 << curDefOperand->type & q.semOperandTypeMasks[k])) goto _nextInsn;
// For some operand types, additional checks are required. // For some operand types, additional checks are required.
switch (curDefOperand->type) switch (curDefOperand->type)
@ -1352,7 +1363,7 @@ static ZydisStatus ZydisFindMatchingDef(
curDefOperand->op.encoding, req->machineMode, &eisz curDefOperand->op.encoding, req->machineMode, &eisz
)); ));
if (eisz < minSize) goto _nextInsn; if (eisz < minSize) goto _nextInsn;
ctx->derivedImmSize[k] = eisz; match->derivedImmSizes[k] = eisz;
} break; } break;
default: default:
; // No further checks required. ; // No further checks required.
@ -1367,10 +1378,10 @@ static ZydisStatus ZydisFindMatchingDef(
} }
// Still here? Looks like we found our instruction, then! // Still here? Looks like we found our instruction, then!
ctx->matchingInsn = candidateInsn; match->insn = candidateInsn;
ctx->matchingDef = candidateDef; match->def = candidateDef;
ctx->matchingOperands = candidateOperands; match->operands = candidateOperands;
ctx->matchingOperandCount = req->operandCount; match->operandCount = req->operandCount;
return ZYDIS_STATUS_SUCCESS; return ZYDIS_STATUS_SUCCESS;
_nextInsn: _nextInsn:
@ -1462,34 +1473,35 @@ ZydisStatus ZydisEncoderEncodeInstruction(void* buffer, size_t* bufferLen,
// Mask out attributes that can't be set explicitly by user. // Mask out attributes that can't be set explicitly by user.
// TODO: We should probably rather error on unsupported attrs. // TODO: We should probably rather error on unsupported attrs.
ctx.derivedAttrs = request->attributes & ZYDIS_USER_ENCODABLE_ATTRIB_MASK; ctx.raw.derivedAttrs = request->attributes & ZYDIS_USER_ENCODABLE_ATTRIB_MASK;
// Search matching instruction, collect information about what needs to be // Search matching instruction, collect information about what needs to be
// encoded, what prefixes are required, etc. // encoded, what prefixes are required, etc.
ZYDIS_CHECK(ZydisFindMatchingDef(&ctx, request)); ZydisInstructionMatch match;
ctx.raw.opcode = ctx.matchingInsn->opcode; ZYDIS_CHECK(ZydisFindMatchingDef(&ctx, request, &match));
ctx.raw.opcode = match.insn->opcode;
// TODO: Check compatibility of requested prefixes to found instruction. // TODO: Check compatibility of requested prefixes to found instruction.
// Prepare prefix bits. // Prepare prefix bits.
ctx.raw.evex.B = ctx.matchingInsn->evexB; ctx.raw.evex.B = match.insn->evexB;
ctx.raw.evex.L = ctx.matchingInsn->vectorLength & 0x01; ctx.raw.evex.L = match.insn->vectorLength & 0x01;
ctx.raw.evex.L2 = ctx.matchingInsn->vectorLength & 0x02; ctx.raw.evex.L2 = match.insn->vectorLength & 0x02;
ctx.raw.vex.L = ctx.matchingInsn->vectorLength & 0x01; ctx.raw.vex.L = match.insn->vectorLength & 0x01;
if (ctx.matchingInsn->rexW) if (match.insn->rexW)
{ {
ctx.raw.rex.W = 1; ctx.raw.rex.W = 1;
ctx.derivedAttrs |= ZYDIS_ATTRIB_HAS_REX; ctx.raw.derivedAttrs |= ZYDIS_ATTRIB_HAS_REX;
} }
ZYDIS_CHECK(ZydisPrepareMandatoryPrefixes(&ctx)); ZYDIS_CHECK(ZydisPrepareMandatoryPrefixes(&ctx, &match));
// Prepare opcode. // Prepare opcode.
ZYDIS_CHECK(ZydisPrepareOpcode(&ctx)); ZYDIS_CHECK(ZydisPrepareOpcode(&ctx, &match));
// Some instructions have additional opcode bits encoded in ModRM.reg. // Some instructions have additional opcode bits encoded in ModRM.reg.
if (ctx.matchingInsn->modrmReg != 0xFF) if (match.insn->modrmReg != 0xFF)
{ {
ctx.raw.modrm.reg = ctx.matchingInsn->modrmReg; ctx.raw.modrm.reg = match.insn->modrmReg;
} }
// Analyze and prepare operands. // Analyze and prepare operands.
@ -1499,16 +1511,16 @@ ZydisStatus ZydisEncoderEncodeInstruction(void* buffer, size_t* bufferLen,
return ZYDIS_STATUS_INVALID_PARAMETER; return ZYDIS_STATUS_INVALID_PARAMETER;
} }
for (uint8_t i = 0; i < ctx.matchingDef->operandCount; ++i) for (uint8_t i = 0; i < match.def->operandCount; ++i)
{ {
ZYDIS_CHECK(ZydisPrepareOperand(&ctx, i)); ZYDIS_CHECK(ZydisPrepareOperand(&ctx, &match, i));
} }
// Do actual encoding work. // Emit prepared raw instruction to bytestream.
ZYDIS_CHECK(ZydisEmitLegacyPrefixes(&ctx)); ZYDIS_CHECK(ZydisEmitLegacyPrefixes(&ctx));
if (ctx.derivedAttrs & ZYDIS_ATTRIB_HAS_REX) ZYDIS_CHECK(ZydisEmitREX(&ctx)); if (ctx.raw.derivedAttrs & ZYDIS_ATTRIB_HAS_REX) ZYDIS_CHECK(ZydisEmitREX(&ctx));
switch (ctx.matchingInsn->encoding) switch (match.insn->encoding)
{ {
case ZYDIS_INSTRUCTION_ENCODING_EVEX: ZYDIS_CHECK(ZydisEmitEVEX(&ctx)); break; case ZYDIS_INSTRUCTION_ENCODING_EVEX: ZYDIS_CHECK(ZydisEmitEVEX(&ctx)); break;
case ZYDIS_INSTRUCTION_ENCODING_VEX: ZYDIS_CHECK(ZydisEmitVEX (&ctx)); break; case ZYDIS_INSTRUCTION_ENCODING_VEX: ZYDIS_CHECK(ZydisEmitVEX (&ctx)); break;
@ -1516,21 +1528,25 @@ ZydisStatus ZydisEncoderEncodeInstruction(void* buffer, size_t* bufferLen,
default:; // Shut up linter. default:; // Shut up linter.
} }
if (ctx.emitMandatoryPrefix) ZYDIS_CHECK(ZydisEmitByte(&ctx, ctx.mandatoryPrefix)); if (ctx.raw.mandatoryPrefix) ZYDIS_CHECK(ZydisEmitByte(&ctx, ctx.raw.mandatoryPrefix));
for (uint8_t i = 0; i < ctx.opcodeMapPrefixLen; ++i) for (uint8_t i = 0; i < ctx.raw.opcodeMapPrefixLen; ++i)
{ {
ZYDIS_CHECK(ZydisEmitByte(&ctx, ctx.opcodeMapPrefix[i])); ZYDIS_CHECK(ZydisEmitByte(&ctx, ctx.raw.opcodeMapPrefix[i]));
} }
ZYDIS_CHECK(ZydisEmitByte(&ctx, ctx.raw.opcode)); ZYDIS_CHECK(ZydisEmitByte(&ctx, ctx.raw.opcode));
if (ctx.derivedAttrs & ZYDIS_ATTRIB_HAS_MODRM) ZYDIS_CHECK(ZydisEmitModRM(&ctx)); if (ctx.raw.derivedAttrs & ZYDIS_ATTRIB_HAS_MODRM) ZYDIS_CHECK(ZydisEmitModRM(&ctx));
if (ctx.derivedAttrs & ZYDIS_ATTRIB_HAS_SIB ) ZYDIS_CHECK(ZydisEmitSIB (&ctx)); if (ctx.raw.derivedAttrs & ZYDIS_ATTRIB_HAS_SIB) ZYDIS_CHECK(ZydisEmitSIB(&ctx));
if (ctx.raw.disp.size) ZYDIS_CHECK(ZydisEmitImm(&ctx, ctx.raw.disp.val, ctx.raw.disp.size));
if (ctx.dispBitSize ) ZYDIS_CHECK(ZydisEmitImm(&ctx, ctx.disp, ctx.dispBitSize )); for (uint8_t i = 0
if (ctx.immBitSizes[0]) ZYDIS_CHECK(ZydisEmitImm(&ctx, ctx.imms[0], ctx.immBitSizes[0])); ; i < ZYDIS_ARRAY_SIZE(ctx.raw.imms) && ctx.raw.imms[i].size
if (ctx.immBitSizes[1]) ZYDIS_CHECK(ZydisEmitImm(&ctx, ctx.imms[1], ctx.immBitSizes[1])); ; ++i)
{
ZYDIS_CHECK(ZydisEmitImm(&ctx, ctx.raw.imms[i].val, ctx.raw.imms[i].size));
}
*bufferLen = ctx.writeOffs; *bufferLen = ctx.writeOffs;
return ZYDIS_STATUS_SUCCESS; return ZYDIS_STATUS_SUCCESS;