More encoder progress

This commit is contained in:
Joel Höner 2017-08-02 23:05:30 +02:00
parent 87394ef4da
commit 9437e89006
2 changed files with 125 additions and 127 deletions

View File

@ -104,8 +104,16 @@ typedef struct ZydisEncoderRequest_
uint8_t operandCount;
ZydisEncoderOperand operands[ZYDIS_ENCODER_MAX_OPERANDS];
// TODO: AVX stuff
// TODO: MVEX stuff
struct
{
ZydisVectorLength vectorLength;
struct
{
ZydisMaskMode mode;
ZydisRegister reg;
} mask;
} avx;
// TODO: mvex stuff
} ZydisEncoderRequest;
/* ============================================================================================== */

View File

@ -48,6 +48,7 @@ typedef struct ZydisInstructionQuery_
typedef struct ZydisInstructionMatch_
{
const ZydisInstructionQuery* q;
const ZydisEncodableInstruction* insn;
const ZydisInstructionDefinition* def;
uint8_t operandCount;
@ -62,6 +63,7 @@ typedef struct ZydisRawInstruction_
uint8_t opcodeMapPrefixLen;
uint8_t opcodeMapPrefix[3];
uint8_t opcode;
ZydisBool didWriteFirstHalfIS4;
struct
{
@ -203,10 +205,11 @@ static ZydisStatus ZydisEmitByte(ZydisEncoderContext* ctx, uint8_t byte)
/* Byte code encoding functions. Translate prepared data to final format. */
/* ---------------------------------------------------------------------------------------------- */
static ZydisStatus ZydisEmitLegacyPrefixes(ZydisEncoderContext* ctx)
static ZydisStatus ZydisEmitLegacyPrefixes(ZydisEncoderContext* ctx,
const ZydisInstructionQuery* q)
{
ZYDIS_ASSERT(ctx);
ZydisInstructionAttributes attribs = ctx->req->attributes;
ZydisInstructionAttributes attribs = ctx->raw.derivedAttrs;
if (attribs & ZYDIS_ATTRIB_HAS_LOCK)
{
@ -244,11 +247,11 @@ static ZydisStatus ZydisEmitLegacyPrefixes(ZydisEncoderContext* ctx)
{
ZYDIS_CHECK(ZydisEmitByte(ctx, 0x65));
}
if (attribs & ZYDIS_ATTRIB_HAS_OPERANDSIZE)
if (q->require66)
{
ZYDIS_CHECK(ZydisEmitByte(ctx, 0x66));
}
if (attribs & ZYDIS_ATTRIB_HAS_ADDRESSSIZE)
if (q->require67)
{
ZYDIS_CHECK(ZydisEmitByte(ctx, 0x67));
}
@ -613,6 +616,17 @@ static ZydisStatus ZydisOperandEncodingGetEffectiveImmSize(
}
}
static uint8_t ZydisSizeToFlag(uint8_t size)
{
switch (size)
{
case 16: return 1 << 0;
case 32: return 1 << 1;
case 64: return 1 << 2;
default: return 0;
}
}
static ZydisBool ZydisRegIsBP(ZydisRegister reg)
{
return reg == ZYDIS_REGISTER_BPL ||
@ -641,13 +655,6 @@ static ZydisBool ZydisRegIsStack(ZydisRegister reg)
return ZydisRegIsSP(reg) || ZydisRegIsBP(reg);
}
static ZydisBool ZydisSemanticTypeIsImplicit(ZydisSemanticOperandType type)
{
return type == ZYDIS_SEMANTIC_OPTYPE_IMPLICIT_REG ||
type == ZYDIS_SEMANTIC_OPTYPE_IMPLICIT_MEM ||
type == ZYDIS_SEMANTIC_OPTYPE_IMPLICIT_IMM1;
}
/* ---------------------------------------------------------------------------------------------- */
/* Preparation functions. Parse encoder request, determine required bytes and prefixes. */
/* ---------------------------------------------------------------------------------------------- */
@ -703,7 +710,7 @@ static ZydisStatus ZydisPrepareOpcode(ZydisEncoderContext* ctx, const ZydisInstr
}
static ZydisStatus ZydisPrepareRegOperand(ZydisEncoderContext* ctx,
ZydisRegister reg, char topBitLoc)
ZydisRegister reg, char topBitDst)
{
ZYDIS_ASSERT(ctx);
@ -713,7 +720,7 @@ static ZydisStatus ZydisPrepareRegOperand(ZydisEncoderContext* ctx,
uint8_t lowerBits = (regID & 0x07) >> 0;
uint8_t topBit = (regID & 0x08) >> 3;
switch (topBitLoc)
switch (topBitDst)
{
case 'B': ctx->raw.modrm.rm = lowerBits; break;
case 'R': ctx->raw.modrm.reg = lowerBits; break;
@ -728,7 +735,7 @@ static ZydisStatus ZydisPrepareRegOperand(ZydisEncoderContext* ctx,
{
case ZYDIS_INSTRUCTION_ENCODING_DEFAULT:
case ZYDIS_INSTRUCTION_ENCODING_3DNOW:
switch (topBitLoc)
switch (topBitDst)
{
case 'B': ctx->raw.rex.B = topBit; break;
case 'R': ctx->raw.rex.R = topBit; break;
@ -738,7 +745,7 @@ static ZydisStatus ZydisPrepareRegOperand(ZydisEncoderContext* ctx,
if (topBit) ctx->raw.derivedAttrs |= ZYDIS_ATTRIB_HAS_REX;
break;
case ZYDIS_INSTRUCTION_ENCODING_VEX:
switch (topBitLoc)
switch (topBitDst)
{
case 'B': ctx->raw.vex.B = topBit; break;
case 'R': ctx->raw.vex.R = topBit; break;
@ -747,7 +754,7 @@ static ZydisStatus ZydisPrepareRegOperand(ZydisEncoderContext* ctx,
}
break;
case ZYDIS_INSTRUCTION_ENCODING_XOP:
switch (topBitLoc)
switch (topBitDst)
{
case 'B': ctx->raw.xop.B = topBit; break;
case 'R': ctx->raw.xop.R = topBit; break;
@ -756,7 +763,7 @@ static ZydisStatus ZydisPrepareRegOperand(ZydisEncoderContext* ctx,
}
break;
case ZYDIS_INSTRUCTION_ENCODING_EVEX:
switch (topBitLoc)
switch (topBitDst)
{
case 'B': ctx->raw.evex.B = topBit; break;
case 'R': ctx->raw.evex.R = topBit; break;
@ -764,6 +771,15 @@ static ZydisStatus ZydisPrepareRegOperand(ZydisEncoderContext* ctx,
default: ZYDIS_UNREACHABLE;
}
break;
case ZYDIS_INSTRUCTION_ENCODING_MVEX:
switch (topBitDst)
{
case 'B': ctx->raw.mvex.B = topBit; break;
case 'R': ctx->raw.mvex.R = topBit; break;
case 'X': ctx->raw.mvex.X = topBit; break;
default: ZYDIS_UNREACHABLE;
}
break;
default:
return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO
}
@ -809,7 +825,7 @@ static ZydisStatus ZydisPrepareSegmentPrefix(ZydisEncoderContext* ctx,
}
static ZydisStatus ZydisPrepareMemoryOperand(ZydisEncoderContext* ctx,
const ZydisEncoderOperand* operand)
const ZydisEncoderOperand* operand, const ZydisInstructionMatch* match)
{
ZYDIS_ASSERT(ctx);
ZYDIS_ASSERT(ctx->req);
@ -858,64 +874,17 @@ static ZydisStatus ZydisPrepareMemoryOperand(ZydisEncoderContext* ctx,
return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO
}
ctx->raw.disp.val = operand->mem.disp;
ctx->raw.disp.val = operand->mem.disp;
ctx->raw.disp.size = 32;
ctx->raw.modrm.mod = 0x00;
ctx->raw.modrm.rm = 0x05 /* RIP relative mem */;
if (operand->mem.base == ZYDIS_REGISTER_EIP)
{
ctx->raw.derivedAttrs |= 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->raw.derivedAttrs |= 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->raw.derivedAttrs |= 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
}
// SIB byte required? rSP can only be encoded with SIB.
if (operand->mem.index || operand->mem.scale || ZydisRegIsSP(operand->mem.base))
{
@ -937,11 +906,6 @@ static ZydisStatus ZydisPrepareMemoryOperand(ZydisEncoderContext* ctx,
// 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
@ -957,8 +921,16 @@ static ZydisStatus ZydisPrepareMemoryOperand(ZydisEncoderContext* ctx,
if (operand->mem.disp || (!(ctx->req->attributes & ZYDIS_ATTRIB_HAS_SIB)
&& ZydisRegIsBP(operand->mem.base)))
{
ctx->raw.disp.size = 32;
ctx->raw.modrm.mod = 0x02 /* 32 bit disp */;
if (ZydisSImmGetMinSize(operand->mem.disp) == 8)
{
ctx->raw.disp.size = 8;
ctx->raw.modrm.mod = 0x01 /* 8 bit disp */;
}
else
{
ctx->raw.disp.size = 32;
ctx->raw.modrm.mod = 0x02 /* 32 bit disp */;
}
ctx->raw.disp.val = operand->mem.disp;
}
// No displacement.
@ -967,6 +939,16 @@ static ZydisStatus ZydisPrepareMemoryOperand(ZydisEncoderContext* ctx,
ctx->raw.modrm.mod = 0x00 /* no disp */;
}
// Verify if the `.reg` and `.rm` values we calculated are permitted for this
// instruction. We don't backtrace for a different definition here in that case
// since the instructions with such restrictions don't have alternate encodings
// that would allow the instruction to be encoded anyway.
if ((!match->insn->forceModrmRm && !(1 << ctx->raw.modrm.rm & match->insn->modrmRm )) ||
(!match->insn->forceModrmReg && !(1 << ctx->raw.modrm.reg & match->insn->modrmReg)))
{
return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO
}
return ZYDIS_STATUS_SUCCESS;
}
@ -974,9 +956,10 @@ static ZydisStatus ZydisPrepareOperand(ZydisEncoderContext* ctx,
ZydisInstructionMatch* match, uint8_t n)
{
ZYDIS_ASSERT(ctx);
ZYDIS_ASSERT(n < ZYDIS_ARRAY_SIZE(ctx->req->operands));
ZYDIS_ASSERT(n < match->operandCount);
const ZydisEncoderOperand* reqOperand = ctx->req->operands + n;
const ZydisOperandDefinition* operandDef = match->operands + n;
//ZYDIS_ASSERT(!ZydisSemanticTypeIsImplicit(operandDef->type));
switch (operandDef->op.encoding)
{
@ -1008,7 +991,7 @@ static ZydisStatus ZydisPrepareOperand(ZydisEncoderContext* ctx,
// Memory operand?
if (reqOperand->type == ZYDIS_OPERAND_TYPE_MEMORY)
{
ZYDIS_CHECK(ZydisPrepareMemoryOperand(ctx, reqOperand));
ZYDIS_CHECK(ZydisPrepareMemoryOperand(ctx, reqOperand, match));
}
// Nope, register.
else if (reqOperand->type == ZYDIS_OPERAND_TYPE_REGISTER)
@ -1037,21 +1020,30 @@ static ZydisStatus ZydisPrepareOperand(ZydisEncoderContext* ctx,
break;
}
case ZYDIS_OPERAND_ENCODING_MASK:
return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION;
break; // TODO
{
uint8_t regId = reqOperand->reg - ZYDIS_REGISTER_K0;
switch (match->insn->encoding)
{
case ZYDIS_INSTRUCTION_ENCODING_EVEX: ctx->raw.evex.aaa = regId; break;
case ZYDIS_INSTRUCTION_ENCODING_MVEX: ctx->raw.mvex.kkk = regId; break;
default: ZYDIS_UNREACHABLE;
}
} break;
case ZYDIS_OPERAND_ENCODING_IS4:
{
ctx->raw.imms[0].size = 8;
ctx->raw.imms[0].val |= reqOperand->imm.u & 0x0F;
if (!ctx->raw.didWriteFirstHalfIS4)
{
ctx->raw.imms[0].size = 8;
ctx->raw.imms[0].val |= reqOperand->imm.u & 0x0F;
ctx->raw.didWriteFirstHalfIS4 = ZYDIS_TRUE;
}
else
{
ZYDIS_ASSERT(ctx->raw.imms[0].size == 8);
ctx->raw.imms[0].val |= (reqOperand->imm.u & 0x0F) << 4;
}
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_JIMM8:
@ -1077,7 +1069,7 @@ static ZydisStatus ZydisPrepareOperand(ZydisEncoderContext* ctx,
return ZYDIS_STATUS_SUCCESS;
}
static ZydisStatus ZydisPrepareMandatoryPrefixes(ZydisEncoderContext* ctx,
static void ZydisPrepareMandatoryPrefixes(ZydisEncoderContext* ctx,
ZydisInstructionMatch* match)
{
ZYDIS_ASSERT(ctx);
@ -1106,11 +1098,9 @@ static ZydisStatus ZydisPrepareMandatoryPrefixes(ZydisEncoderContext* ctx,
ZYDIS_UNREACHABLE;
}
}
return ZYDIS_STATUS_SUCCESS;
}
static ZydisStatus ZydisAnalyzeRequirements(
static ZydisStatus ZydisRequestToInstructionQuery(
ZydisEncoderContext* ctx, const ZydisEncoderRequest* req, ZydisInstructionQuery* q)
{
ZYDIS_ASSERT(ctx);
@ -1174,7 +1164,7 @@ static ZydisStatus ZydisAnalyzeRequirements(
// Verify base and index have the same register class, if present.
ZydisRegisterClass baseRegClass = ZydisRegisterGetClass(curReqOperand->mem.base);
if (curReqOperand->mem.base != ZYDIS_REGISTER_NONE &&
curReqOperand->mem.base != ZYDIS_REGISTER_NONE &&
curReqOperand->mem.index != ZYDIS_REGISTER_NONE &&
baseRegClass != ZydisRegisterGetClass(curReqOperand->mem.index))
{
return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO
@ -1237,25 +1227,19 @@ static ZydisStatus ZydisAnalyzeRequirements(
}
static ZydisStatus ZydisFindMatchingDef(
ZydisEncoderContext* ctx, const ZydisEncoderRequest* req, ZydisInstructionMatch* match)
ZydisEncoderContext* ctx, const ZydisEncoderRequest* req, const ZydisInstructionQuery* q,
ZydisInstructionMatch* match)
{
ZYDIS_ASSERT(ctx);
ZYDIS_ASSERT(req);
ZYDIS_ASSERT(match);
// Evaluate request.
ZydisInstructionQuery q;
ZYDIS_CHECK(ZydisAnalyzeRequirements(ctx, req, &q));
// Translate requested mode to flags.
uint8_t modeFlag;
switch (req->machineMode)
{
case 16: modeFlag = 0x01; break;
case 32: modeFlag = 0x02; break;
case 64: modeFlag = 0x04; break;
default: return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO
}
// Translate sizes to flags.
uint8_t modeFlag = ZydisSizeToFlag(req->machineMode);
if (!modeFlag) return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION; // TODO
uint8_t easzFlag = ZydisSizeToFlag(q->easz);
uint8_t eoszFlag = ZydisSizeToFlag(q->eosz);
ZYDIS_ASSERT(easzFlag && eoszFlag);
// Walk list of candidates.
const ZydisEncodableInstruction* insns = NULL;
@ -1269,6 +1253,9 @@ static ZydisStatus ZydisFindMatchingDef(
; ++candidateInsn)
{
if (!(candidateInsn->mode & modeFlag)) goto _nextInsn;
//if (!candidateInsn->rexW && q->requireREXW) goto _nextInsn;
if (!(candidateInsn->addressSize & easzFlag)) goto _nextInsn;
if (!(candidateInsn->operandSize & eoszFlag)) goto _nextInsn;
const ZydisInstructionDefinition* candidateDef = NULL;
ZydisGetInstructionDefinition(
@ -1290,7 +1277,7 @@ static ZydisStatus ZydisFindMatchingDef(
if (curDefOperand->visibility == ZYDIS_OPERAND_VISIBILITY_HIDDEN) goto _nextInsn;
// Is the type one of those we permit for the given operand?
if (!(1 << curDefOperand->type & q.semOperandTypeMasks[k])) goto _nextInsn;
if (!(1 << curDefOperand->type & q->semOperandTypeMasks[k])) goto _nextInsn;
// For some operand types, additional checks are required.
switch (curDefOperand->type)
@ -1377,6 +1364,7 @@ static ZydisStatus ZydisFindMatchingDef(
}
// Still here? Looks like we found our instruction, then!
match->q = q;
match->insn = candidateInsn;
match->def = candidateDef;
match->operands = candidateOperands;
@ -1408,9 +1396,12 @@ ZydisStatus ZydisEncoderDecodedInstructionToRequest(
out->machineMode = in->machineMode;
out->mnemonic = in->mnemonic;
out->attributes = in->attributes;
out->attributes = in->attributes & ZYDIS_USER_ENCODABLE_ATTRIB_MASK;
out->encoding = in->encoding;
out->operandCount = 0;
out->avx.mask.reg = in->avx.mask.reg;
out->avx.mask.mode = in->avx.mask.mode;
out->avx.vectorLength = in->avx.vectorLength;
for (uint8_t i = 0
; i < in->operandCount && out->operandCount < ZYDIS_ARRAY_SIZE(out->operands)
@ -1456,11 +1447,12 @@ ZydisStatus ZydisEncoderDecodedInstructionToRequest(
ZydisStatus ZydisEncoderEncodeInstruction(void* buffer, size_t* bufferLen,
const ZydisEncoderRequest* request)
{
if (!request || !bufferLen || request->operandCount > ZYDIS_ARRAY_SIZE(request->operands))
if (!request || !bufferLen ) return ZYDIS_STATUS_INVALID_PARAMETER;
if (!buffer || !*bufferLen) return ZYDIS_STATUS_INSUFFICIENT_BUFFER_SIZE;
if (request->operandCount > ZYDIS_ARRAY_SIZE(request->operands))
{
return ZYDIS_STATUS_INVALID_PARAMETER;
return ZYDIS_STATUS_IMPOSSIBLE_INSTRUCTION;
}
if (!buffer || !*bufferLen) return ZYDIS_STATUS_INSUFFICIENT_BUFFER_SIZE;
ZydisEncoderContext ctx;
memset(&ctx, 0, sizeof(ctx));
@ -1474,10 +1466,16 @@ ZydisStatus ZydisEncoderEncodeInstruction(void* buffer, size_t* bufferLen,
// TODO: We should probably rather error on unsupported attrs.
ctx.raw.derivedAttrs = request->attributes & ZYDIS_USER_ENCODABLE_ATTRIB_MASK;
// Evaluate request.
ZydisInstructionQuery q;
memset(&q, 0, sizeof(q));
ZYDIS_CHECK(ZydisRequestToInstructionQuery(&ctx, request, &q));
// Search matching instruction, collect information about what needs to be
// encoded, what prefixes are required, etc.
ZydisInstructionMatch match;
ZYDIS_CHECK(ZydisFindMatchingDef(&ctx, request, &match));
memset(&match, 0, sizeof(match));
ZYDIS_CHECK(ZydisFindMatchingDef(&ctx, request, &q, &match));
ctx.raw.opcode = match.insn->opcode;
// TODO: Check compatibility of requested prefixes to found instruction.
@ -1492,31 +1490,23 @@ ZydisStatus ZydisEncoderEncodeInstruction(void* buffer, size_t* bufferLen,
ctx.raw.rex.W = 1;
ctx.raw.derivedAttrs |= ZYDIS_ATTRIB_HAS_REX;
}
ZYDIS_CHECK(ZydisPrepareMandatoryPrefixes(&ctx, &match));
ZydisPrepareMandatoryPrefixes(&ctx, &match);
// Prepare opcode.
ZYDIS_CHECK(ZydisPrepareOpcode(&ctx, &match));
// Some instructions have additional opcode bits encoded in ModRM.reg.
if (match.insn->modrmReg != 0xFF)
{
ctx.raw.modrm.reg = match.insn->modrmReg;
}
// Some instructions have additional opcode bits encoded in ModRM.[rm/reg].
if (match.insn->forceModrmReg) ctx.raw.modrm.reg = match.insn->modrmReg;
if (match.insn->forceModrmRm ) ctx.raw.modrm.rm = match.insn->modrmRm;
// Analyze and prepare operands.
if (request->operandCount > ZYDIS_ARRAY_SIZE(request->operands))
{
// TODO: Better status?
return ZYDIS_STATUS_INVALID_PARAMETER;
}
for (uint8_t i = 0; i < match.def->operandCount; ++i)
{
ZYDIS_CHECK(ZydisPrepareOperand(&ctx, &match, i));
}
}
// Emit prepared raw instruction to bytestream.
ZYDIS_CHECK(ZydisEmitLegacyPrefixes(&ctx));
ZYDIS_CHECK(ZydisEmitLegacyPrefixes(&ctx, &q));
if (ctx.raw.derivedAttrs & ZYDIS_ATTRIB_HAS_REX) ZYDIS_CHECK(ZydisEmitREX(&ctx));
switch (match.insn->encoding)