diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2c7da76..f6bb614 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -96,6 +96,7 @@ add_library(
bddisasm STATIC
bddisasm/crt.c
bddisasm/bddisasm.c
+ bddisasm/bdformat.c
# Add the headers so they will show up in IDEs.
bddisasm/include/instructions.h
bddisasm/include/mnemonics.h
diff --git a/bddisasm/bddisasm.c b/bddisasm/bddisasm.c
index d550dd3..22bc236 100644
--- a/bddisasm/bddisasm.c
+++ b/bddisasm/bddisasm.c
@@ -8,133 +8,10 @@
// The table definitions.
#include "include/tabledefs.h"
-// Handy macros.
-#define RET_EQ(x, y, z) if ((x) == (y)) { return (z); }
-#define RET_GE(x, y, z) if ((x) >= (y)) { return (z); }
-#define RET_GT(x, y, z) if ((x) > (y)) { return (z); }
-
#ifndef UNREFERENCED_PARAMETER
#define UNREFERENCED_PARAMETER(P) ((void)(P))
#endif
-#ifndef BDDISASM_NO_FORMAT
-
-static const char *gReg8Bit[] =
-{
- "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh",
-};
-
-static const char *gReg8Bit64[] =
-{
- "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil",
- "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b",
-};
-
-static const char *gReg16Bit[] =
-{
- "ax", "cx", "dx", "bx", "sp", "bp", "si", "di",
- "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w",
-};
-
-static const char *gReg32Bit[] =
-{
- "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi",
- "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d",
-};
-
-static const char *gReg64Bit[] =
-{
- "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
- "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
-};
-
-static const char *gRegFpu[] =
-{
- "st0", "st1", "st2", "st3", "st4", "st5", "st6", "st7",
-};
-
-static const char *gRegMmx[] =
-{
- "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
-};
-
-static const char *gRegControl[] =
-{
- "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7",
- "cr8", "cr9", "cr10", "cr11", "cr12", "cr13", "cr14", "cr15",
-};
-
-static const char *gRegDebug[] =
-{
- "dr0", "dr1", "dr2", "dr3", "dr4", "dr5", "dr6", "dr7",
- "dr8", "dr9", "dr10", "dr11", "dr12", "dr13", "dr14", "dr15",
-};
-
-static const char *gRegTest[] =
-{
- "tr0", "tr1", "tr2", "tr3", "tr4", "tr5", "tr6", "tr7",
- "tr8", "tr9", "tr10", "tr11", "tr12", "tr13", "tr14", "tr15",
-};
-
-static const char *gRegXmm[] =
-{
- "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
- "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15",
- "xmm16", "xmm17", "xmm18", "xmm19", "xmm20", "xmm21", "xmm22", "xmm23",
- "xmm24", "xmm25", "xmm26", "xmm27", "xmm28", "xmm29", "xmm30", "xmm31",
-};
-
-static const char *gRegYmm[] =
-{
- "ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", "ymm6", "ymm7",
- "ymm8", "ymm9", "ymm10", "ymm11", "ymm12", "ymm13", "ymm14", "ymm15",
- "ymm16", "ymm17", "ymm18", "ymm19", "ymm20", "ymm21", "ymm22", "ymm23",
- "ymm24", "ymm25", "ymm26", "ymm27", "ymm28", "ymm29", "ymm30", "ymm31"
-};
-
-static const char *gRegZmm[] =
-{
- "zmm0", "zmm1", "zmm2", "zmm3", "zmm4", "zmm5", "zmm6", "zmm7",
- "zmm8", "zmm9", "zmm10", "zmm11", "zmm12", "zmm13", "zmm14", "zmm15",
- "zmm16", "zmm17", "zmm18", "zmm19", "zmm20", "zmm21", "zmm22", "zmm23",
- "zmm24", "zmm25", "zmm26", "zmm27", "zmm28", "zmm29", "zmm30", "zmm31",
-};
-
-static const char *gRegSeg[] =
-{
- "es", "cs", "ss", "ds", "fs", "gs", "segr6", "segr7",
-};
-
-static const char *gRegBound[] =
-{
- "bnd0", "bnd1", "bnd2", "bnd3",
-};
-
-static const char *gRegMask[] =
-{
- "k0", "k1", "k2", "k3", "k4", "k5", "k6", "k7",
-};
-
-static const char *gRegTile[] =
-{
- "tmm0", "tmm1", "tmm2", "tmm3", "tmm4", "tmm5", "tmm6", "tmm7",
-};
-
-static const char *gConditionCodes[] =
-{
- "EQ", "LT", "LE", "UNORD", "NEQ", "NLT", "NLE", "ORD",
- "EQ_UQ", "NGE", "NGT", "false", "NEQ_OQ", "GE", "GT", "TRUE",
- "EQ_OS", "LT_OQ", "LE_OQ", "UNORD_S", "NEQ_US", "NLT_UQ", "NLE_UQ", "ORD_S",
- "EQ_US", "NGE_UQ", "NGT_UQ", "FALSE_OS", "NEQ_OS", "GE_OQ", "GT_OQ", "TRUE_US",
-};
-
-static const char *gEmbeddedRounding[] =
-{
- "rn", "rd", "ru", "rz",
-};
-
-#endif // !BDDISASM_NO_FORMAT
-
static const uint16_t gOperandMap[] =
{
@@ -329,52 +206,6 @@ NdGetVersion(
}
-//
-// NdSprintf
-//
-#ifndef BDDISASM_NO_FORMAT
-static NDSTATUS
-NdSprintf(
- char *Destination,
- size_t DestinationSize,
- const char *Formatstring,
- ...
- )
-//
-// Wrapper on vsnprintf.
-//
-{
- int res;
- va_list args;
-
- if (NULL == Destination)
- {
- return ND_STATUS_INVALID_PARAMETER;
- }
-
- if (NULL == Formatstring)
- {
- return ND_STATUS_INVALID_PARAMETER;
- }
-
- nd_memzero(Destination, DestinationSize);
-
- va_start(args, Formatstring);
-
- // _vsnprintf is used instead of the more secure _vsnprintf_s because the mini-Petru supports just
- // the unsecured version, and we depend on it.
- res = nd_vsnprintf_s(Destination, DestinationSize, DestinationSize - 1, Formatstring, args);
-
- va_end(args);
-
- if ((res < 0) || ((size_t)res >= DestinationSize - 1))
- {
- return ND_STATUS_BUFFER_OVERFLOW;
- }
-
- return ND_STATUS_SUCCESS;
-}
-#endif // !BDDISASM_NO_FORMAT
//
@@ -4314,884 +4145,6 @@ NdDecode(
}
-//
-// NdToText
-//
-#ifndef BDDISASM_NO_FORMAT
-NDSTATUS
-NdToText(
- const INSTRUX *Instrux,
- uint64_t Rip,
- uint32_t BufferSize,
- char *Buffer
- )
-{
- NDSTATUS status;
- char *res, temp[64];
- uint32_t opIndex, opsStored;
- const ND_OPERAND *pOp;
- bool alignmentStored;
-
- // pre-init
- status = ND_STATUS_SUCCESS;
- res = NULL;
- opIndex = 0;
- opsStored = 0;
- pOp = NULL;
- alignmentStored = false;
-
- // Validate args.
- if (NULL == Instrux)
- {
- return ND_STATUS_INVALID_PARAMETER;
- }
-
- if (NULL == Buffer)
- {
- return ND_STATUS_INVALID_PARAMETER;
- }
-
- if (BufferSize < ND_MIN_BUF_SIZE)
- {
- return ND_STATUS_INVALID_PARAMETER;
- }
-
- // init the text.
- nd_memzero(Buffer, BufferSize);
- nd_memzero(temp, sizeof(temp));
-
- // First, store the prefixes.
- if (Instrux->Rep != 0)
- {
- // Check for REPZ/REPNZ support, and store prefixes.
- if (ND_REPC_SUPPORT(Instrux))
- {
- if (Instrux->Rep == ND_PREFIX_G1_REPE_REPZ)
- {
- res = nd_strcat_s(Buffer, BufferSize, "REPZ ");
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
- else if (Instrux->Rep == ND_PREFIX_G1_REPNE_REPNZ)
- {
- res = nd_strcat_s(Buffer, BufferSize, "REPNZ ");
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
- }
-
- // Check for REP support and store prefixes.
- if (ND_REP_SUPPORT(Instrux))
- {
- if (Instrux->Rep == ND_PREFIX_G1_REPE_REPZ)
- {
- res = nd_strcat_s(Buffer, BufferSize, "REP ");
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
- else if (Instrux->Rep == ND_PREFIX_G1_REPNE_REPNZ)
- {
- res = nd_strcat_s(Buffer, BufferSize, "REPNZ ");
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
- }
-
- if (Instrux->IsXreleaseEnabled)
- {
- res = nd_strcat_s(Buffer, BufferSize, "XRELEASE ");
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
- else if (Instrux->IsXacquireEnabled)
- {
- res = nd_strcat_s(Buffer, BufferSize, "XACQUIRE ");
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
- }
-
- if (Instrux->HasLock)
- {
- if (ND_LOCK_SUPPORT(Instrux))
- {
- res = nd_strcat_s(Buffer, BufferSize, "LOCK ");
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
- }
-
- if (Instrux->Rep == ND_PREFIX_G1_BND)
- {
- if (ND_BND_SUPPORT(Instrux))
- {
- res = nd_strcat_s(Buffer, BufferSize, "BND ");
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
- }
-
- if (Instrux->HasSeg && ND_BHINT_SUPPORT(Instrux))
- {
- switch (Instrux->Seg)
- {
- case ND_PREFIX_G2_BR_TAKEN:
- res = nd_strcat_s(Buffer, BufferSize, "BHT ");
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- break;
-
- case ND_PREFIX_G2_BR_NOT_TAKEN:
- res = nd_strcat_s(Buffer, BufferSize, "BHNT ");
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- break;
-
- case ND_PREFIX_G2_BR_ALT:
- res = nd_strcat_s(Buffer, BufferSize, "BHALT ");
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- break;
-
- default:
- break;
- }
- }
-
- if (Instrux->HasSeg && ND_DNT_SUPPORT(Instrux))
- {
- if (!Instrux->IsCetTracked)
- {
- res = nd_strcat_s(Buffer, BufferSize, "DNT ");
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
- }
-
- // Store the mnemonic.
- res = nd_strcat_s(Buffer, BufferSize, Instrux->Mnemonic);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
-
- // Store condition code, if any.
- if (ND_HAS_SSE_CONDITION(Instrux))
- {
- res = nd_strcat_s(Buffer, BufferSize, gConditionCodes[Instrux->SseCondition]);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
-
- // If there are no explicit operands, we can leave.
- if (0 == Instrux->ExpOperandsCount)
- {
- return ND_STATUS_SUCCESS;
- }
-
- // Now the operands.
- for (opIndex = 0; opIndex < Instrux->OperandsCount; opIndex++)
- {
- status = ND_STATUS_SUCCESS;
-
- pOp = &Instrux->Operands[opIndex];
-
- if (pOp->Type == ND_OP_NOT_PRESENT)
- {
- break;
- }
-
- if (pOp->Flags.IsDefault)
- {
- continue;
- }
-
- // If this is a mask operand that has been used as masking for a previous operand, than we
- // can safely skip it. We check this by seeing where is the operand encoded. If it's encoded
- // in the evex.aaa field, than it is a conventional mask.
- if ((pOp->Encoding == ND_OPE_A) && (pOp->Type == ND_OP_REG) &&
- (pOp->Info.Register.Type == ND_REG_MSK) && (opIndex > 0))
- {
- continue;
- }
-
- // Store alignment.
- if (!alignmentStored)
- {
- size_t idx = 0;
-
- while ((idx < BufferSize) && (Buffer[idx]))
- {
- idx++;
- }
-
- while ((idx < 9) && (idx + 1 < BufferSize))
- {
- Buffer[idx++] = 0x20;
- }
-
- if (idx + 1 < BufferSize)
- {
- Buffer[idx++] = 0x20;
- }
-
- Buffer[idx] = 0;
-
- alignmentStored = true;
- }
-
- // Store the comma, if this isn't the first operand.
- if (opsStored > 0)
- {
- res = nd_strcat_s(Buffer, BufferSize, ", ");
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
-
- opsStored++;
-
- switch (pOp->Type)
- {
- case ND_OP_REG:
- switch (pOp->Info.Register.Type)
- {
- case ND_REG_GPR:
- {
- if (pOp->Info.Register.Reg >= ND_MAX_GPR_REGS)
- {
- return ND_STATUS_INVALID_INSTRUX;
- }
-
- // General purpose register.
- switch (pOp->Info.Register.Size)
- {
- case ND_SIZE_8BIT:
- // 8 bit register.
- if ((Instrux->EncMode != ND_ENCM_LEGACY) || Instrux->HasRex)
- {
- res = nd_strcat_s(Buffer, BufferSize, gReg8Bit64[pOp->Info.Register.Reg]);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
- else
- {
- res = nd_strcat_s(Buffer, BufferSize, gReg8Bit[pOp->Info.Register.Reg]);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
- break;
-
- case ND_SIZE_16BIT:
- // 16 bit register.
- res = nd_strcat_s(Buffer, BufferSize, gReg16Bit[pOp->Info.Register.Reg]);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- break;
-
- case ND_SIZE_32BIT:
- // 32 bit register.
- res = nd_strcat_s(Buffer, BufferSize, gReg32Bit[pOp->Info.Register.Reg]);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- break;
-
- case ND_SIZE_64BIT:
- // 64 bit register.
- res = nd_strcat_s(Buffer, BufferSize, gReg64Bit[pOp->Info.Register.Reg]);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- break;
-
- default:
- return ND_STATUS_INVALID_INSTRUX;
- }
- }
- break;
-
- case ND_REG_SEG:
- {
- if (pOp->Info.Register.Reg >= ND_MAX_SEG_REGS)
- {
- return ND_STATUS_INVALID_INSTRUX;
- }
-
- res = nd_strcat_s(Buffer, BufferSize, gRegSeg[pOp->Info.Register.Reg]);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
- break;
-
- case ND_REG_FPU:
- {
- if (pOp->Info.Register.Reg >= ND_MAX_FPU_REGS)
- {
- return ND_STATUS_INVALID_INSTRUX;
- }
-
- res = nd_strcat_s(Buffer, BufferSize, gRegFpu[pOp->Info.Register.Reg]);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
- break;
-
- case ND_REG_MMX:
- {
- if (pOp->Info.Register.Reg >= ND_MAX_MMX_REGS)
- {
- return ND_STATUS_INVALID_INSTRUX;
- }
-
- res = nd_strcat_s(Buffer, BufferSize, gRegMmx[pOp->Info.Register.Reg]);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
- break;
-
- case ND_REG_SSE:
- {
- if (pOp->Info.Register.Reg >= ND_MAX_SSE_REGS)
- {
- return ND_STATUS_INVALID_INSTRUX;
- }
-
- switch (pOp->Info.Register.Size)
- {
- case ND_SIZE_128BIT:
- res = nd_strcat_s(Buffer, BufferSize, gRegXmm[pOp->Info.Register.Reg]);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- break;
- case ND_SIZE_256BIT:
- res = nd_strcat_s(Buffer, BufferSize, gRegYmm[pOp->Info.Register.Reg]);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- break;
- case ND_SIZE_512BIT:
- res = nd_strcat_s(Buffer, BufferSize, gRegZmm[pOp->Info.Register.Reg]);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- break;
- default:
- return ND_STATUS_INVALID_INSTRUX;
- }
- }
- break;
-
- case ND_REG_CR:
- {
- if (pOp->Info.Register.Reg >= ND_MAX_CR_REGS)
- {
- return ND_STATUS_INVALID_INSTRUX;
- }
-
- res = nd_strcat_s(Buffer, BufferSize, gRegControl[pOp->Info.Register.Reg]);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
- break;
-
- case ND_REG_DR:
- {
- if (pOp->Info.Register.Reg >= ND_MAX_DR_REGS)
- {
- return ND_STATUS_INVALID_INSTRUX;
- }
-
- res = nd_strcat_s(Buffer, BufferSize, gRegDebug[pOp->Info.Register.Reg]);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
- break;
-
- case ND_REG_TR:
- {
- if (pOp->Info.Register.Reg >= ND_MAX_TR_REGS)
- {
- return ND_STATUS_INVALID_INSTRUX;
- }
-
- res = nd_strcat_s(Buffer, BufferSize, gRegTest[pOp->Info.Register.Reg]);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
- break;
-
- case ND_REG_BND:
- {
- // Sanity check.
- if (pOp->Info.Register.Reg >= ND_MAX_BND_REGS)
- {
- return ND_STATUS_INVALID_INSTRUX;
- }
-
- res = nd_strcat_s(Buffer, BufferSize, gRegBound[pOp->Info.Register.Reg]);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
- break;
-
- case ND_REG_MSK:
- {
- // Sanity check.
- if (pOp->Info.Register.Reg >= ND_MAX_MSK_REGS)
- {
- return ND_STATUS_INVALID_INSTRUX;
- }
-
- res = nd_strcat_s(Buffer, BufferSize, gRegMask[pOp->Info.Register.Reg]);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
- break;
-
- case ND_REG_TILE:
- {
- // Sanity check.
- if (pOp->Info.Register.Reg >= ND_MAX_TILE_REGS)
- {
- return ND_STATUS_INVALID_INSTRUX;
- }
-
- res = nd_strcat_s(Buffer, BufferSize, gRegTile[pOp->Info.Register.Reg]);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
- break;
-
- default:
- break;
- }
-
- if (pOp->Info.Register.Count > 1)
- {
- status = NdSprintf(temp, sizeof(temp), "+%d", pOp->Info.Register.Count - 1);
- if (!ND_SUCCESS(status))
- {
- return status;
- }
-
- res = nd_strcat_s(Buffer, BufferSize, temp);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
-
- break;
-
- case ND_OP_BANK:
- // Nothing to show.
- break;
-
- case ND_OP_CONST:
- {
- // Implicit constant
- status = NdSprintf(temp, sizeof(temp), "%d", pOp->Info.Constant.Const);
- if (!ND_SUCCESS(status))
- {
- return status;
- }
-
- res = nd_strcat_s(Buffer, BufferSize, temp);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
- break;
-
- case ND_OP_IMM:
- {
- switch (pOp->Size)
- {
- case 1:
- status = NdSprintf(temp, sizeof(temp), "0x%02x", (uint8_t)pOp->Info.Immediate.Imm);
- break;
- case 2:
- status = NdSprintf(temp, sizeof(temp), "0x%04x", (uint16_t)pOp->Info.Immediate.Imm);
- break;
- case 4:
- status = NdSprintf(temp, sizeof(temp), "0x%08x", (uint32_t)pOp->Info.Immediate.Imm);
- break;
- case 8:
- status = NdSprintf(temp, sizeof(temp), "0x%016llx", (uint64_t)pOp->Info.Immediate.Imm);
- break;
- }
- if (!ND_SUCCESS(status))
- {
- return status;
- }
-
- res = nd_strcat_s(Buffer, BufferSize, temp);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
- break;
-
- case ND_OP_OFFS:
- {
- uint64_t dest = Rip + Instrux->Length + pOp->Info.RelativeOffset.Rel;
-
- // Truncate to the actual word length.
- switch (Instrux->WordLength)
- {
- case 2:
- dest &= 0xFFFF;
- break;
- case 4:
- dest &= 0xFFFFFFFF;
- break;
- default:
- break;
- }
-
- status = NdSprintf(temp, sizeof(temp), "0x%llx", dest);
- if (!ND_SUCCESS(status))
- {
- return status;
- }
-
- res = nd_strcat_s(Buffer, BufferSize, temp);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
- break;
-
- case ND_OP_ADDR:
- {
- switch (Instrux->AddrLength)
- {
- case 4:
- status = NdSprintf(temp, sizeof(temp), "0x%04x:0x%04x",
- pOp->Info.Address.BaseSeg, (uint16_t)pOp->Info.Address.Offset);
- break;
- case 6:
- status = NdSprintf(temp, sizeof(temp), "0x%04x:0x%08x",
- pOp->Info.Address.BaseSeg, (uint32_t)pOp->Info.Address.Offset);
- break;
- case 10:
- status = NdSprintf(temp, sizeof(temp), "0x%04x:0x%016llx",
- pOp->Info.Address.BaseSeg, (uint64_t)pOp->Info.Address.Offset);
- break;
- default:
- return ND_STATUS_INVALID_INSTRUX;
- }
-
- if (!ND_SUCCESS(status))
- {
- return status;
- }
-
- res = nd_strcat_s(Buffer, BufferSize, temp);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
- break;
-
- case ND_OP_MEM:
- {
- // Prepend the size. For VSIB addressing, store the VSIB element size, not the total accessed size.
- ND_OPERAND_SIZE size = pOp->Info.Memory.IsVsib ? pOp->Info.Memory.Vsib.ElemSize : pOp->Size;
-
- switch (size)
- {
- case 1:
- res = nd_strcat_s(Buffer, BufferSize, "byte ptr ");
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- break;
- case 2:
- res = nd_strcat_s(Buffer, BufferSize, "word ptr ");
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- break;
- case 4:
- res = nd_strcat_s(Buffer, BufferSize, "dword ptr ");
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- break;
- case 6:
- res = nd_strcat_s(Buffer, BufferSize, "fword ptr ");
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- break;
- case 8:
- res = nd_strcat_s(Buffer, BufferSize, "qword ptr ");
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- break;
- case 10:
- res = nd_strcat_s(Buffer, BufferSize, "tbyte ptr ");
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- break;
- case 16:
- res = nd_strcat_s(Buffer, BufferSize, "xmmword ptr ");
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- break;
- case 32:
- res = nd_strcat_s(Buffer, BufferSize, "ymmword ptr ");
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- break;
- case 48:
- res = nd_strcat_s(Buffer, BufferSize, "m384 ptr ");
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- break;
- case 64:
- res = nd_strcat_s(Buffer, BufferSize, "zmmword ptr ");
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- break;
- default:
- break;
- }
-
- // Perpend the segment, only if it is overridden via a prefix.
- if (pOp->Info.Memory.HasSeg && Instrux->HasSeg)
- {
- if (pOp->Info.Memory.Seg >= ND_MAX_SEG_REGS)
- {
- return ND_STATUS_INVALID_INSTRUX;
- }
-
- if ((ND_CODE_64 != Instrux->DefCode) || (NDR_FS == pOp->Info.Memory.Seg) ||
- (NDR_GS == pOp->Info.Memory.Seg))
- {
- res = nd_strcat_s(Buffer, BufferSize, gRegSeg[pOp->Info.Memory.Seg]);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
-
- res = nd_strcat_s(Buffer, BufferSize, ":");
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
- }
-
- // Prepend the "["
- res = nd_strcat_s(Buffer, BufferSize, "[");
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
-
- // Base, if any.
- if (pOp->Info.Memory.HasBase)
- {
- if (pOp->Info.Memory.Base >= ND_MAX_GPR_REGS)
- {
- return ND_STATUS_INVALID_INSTRUX;
- }
-
- switch (pOp->Info.Memory.BaseSize)
- {
- case ND_SIZE_8BIT:
- res = nd_strcat_s(Buffer, BufferSize, gReg8Bit[pOp->Info.Memory.Base]);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- break;
- case ND_SIZE_16BIT:
- res = nd_strcat_s(Buffer, BufferSize, gReg16Bit[pOp->Info.Memory.Base]);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- break;
- case ND_SIZE_32BIT:
- res = nd_strcat_s(Buffer, BufferSize, gReg32Bit[pOp->Info.Memory.Base]);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- break;
- case ND_SIZE_64BIT:
- res = nd_strcat_s(Buffer, BufferSize, gReg64Bit[pOp->Info.Memory.Base]);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- break;
- default:
- return ND_STATUS_INVALID_INSTRUX;
- }
- }
-
- // Index, if any. Special treatment for VSIB addressing. Also, perpend a "+" if base is present.
- if (pOp->Info.Memory.HasIndex)
- {
- if (pOp->Info.Memory.Index >= (pOp->Info.Memory.IsVsib ? ND_MAX_SSE_REGS : ND_MAX_GPR_REGS))
- {
- return ND_STATUS_INVALID_INSTRUX;
- }
-
- if (pOp->Info.Memory.HasBase)
- {
- res = nd_strcat_s(Buffer, BufferSize, "+");
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
-
- switch (pOp->Info.Memory.IndexSize)
- {
- case ND_SIZE_8BIT:
- res = nd_strcat_s(Buffer, BufferSize, gReg8Bit[pOp->Info.Memory.Index]);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- break;
- case ND_SIZE_16BIT:
- res = nd_strcat_s(Buffer, BufferSize, gReg16Bit[pOp->Info.Memory.Index]);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- break;
- case ND_SIZE_32BIT:
- res = nd_strcat_s(Buffer, BufferSize, gReg32Bit[pOp->Info.Memory.Index]);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- break;
- case ND_SIZE_64BIT:
- res = nd_strcat_s(Buffer, BufferSize, gReg64Bit[pOp->Info.Memory.Index]);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- break;
- case ND_SIZE_128BIT:
- res = nd_strcat_s(Buffer, BufferSize, gRegXmm[pOp->Info.Memory.Index]);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- break;
- case ND_SIZE_256BIT:
- res = nd_strcat_s(Buffer, BufferSize, gRegYmm[pOp->Info.Memory.Index]);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- break;
- case ND_SIZE_512BIT:
- res = nd_strcat_s(Buffer, BufferSize, gRegZmm[pOp->Info.Memory.Index]);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- break;
- default:
- return ND_STATUS_INVALID_INSTRUX;
- }
-
- // If index is present, scale is also present.
- if (pOp->Info.Memory.Scale != 1 && !pOp->Info.Memory.IsMib)
- {
- status = NdSprintf(temp, sizeof(temp), "*%d", pOp->Info.Memory.Scale);
- if (!ND_SUCCESS(status))
- {
- return status;
- }
-
- res = nd_strcat_s(Buffer, BufferSize, temp);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
- }
-
- // Handle displacement.
- if (pOp->Info.Memory.HasDisp)
- {
- uint64_t normDisp, disp;
-
- disp = pOp->Info.Memory.Disp;
-
- // If this is direct addressing (O operand) or we don't have base or index, than we don't normalize
- // the displacement, since it is used as a direct offset. Note that the second condition also
- // includes the RIP-relative case.
- if (pOp->Info.Memory.IsDirect || !(pOp->Info.Memory.HasBase || pOp->Info.Memory.HasIndex))
- {
- normDisp = disp;
- }
- else
- {
- switch (pOp->Info.Memory.DispSize)
- {
- case 1:
- normDisp = ((disp & 0x80) ? ~disp + 1ULL : disp) & 0xFF;
- break;
- case 2:
- normDisp = ((disp & 0x8000) ? ~disp + 1ULL : disp) & 0xFFFF;
- break;
- case 4:
- normDisp = ((disp & 0x80000000) ? ~disp + 1ULL : disp) & 0xFFFFFFFF;
- break;
- default:
- normDisp = disp;
- break;
- }
-
- // Handle compressed displacement. It is fine to cast the normDisp to uint32_t, as the
- // compressed displacement only works with uint8_t displacements. Also, in this phase,
- // the normDisp is converted to a positive quantity, so no sign-extension is needed.
- if (pOp->Info.Memory.HasCompDisp)
- {
- normDisp = (uint64_t)(uint32_t)normDisp * pOp->Info.Memory.CompDispSize;
- }
- }
-
-
- // Now displacement.
- if (pOp->Info.Memory.HasBase || pOp->Info.Memory.HasIndex)
- {
- res = nd_strcat_s(Buffer, BufferSize, Instrux->SignDisp ? "-" : "+");
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
-
- if (pOp->Info.Memory.IsRipRel)
- {
- uint64_t target = disp + Rip + Instrux->Length;
-
- if (Instrux->AddrMode == ND_ADDR_32)
- {
- target &= 0xFFFFFFFF;
- }
-
- status = NdSprintf(temp, sizeof(temp), "rel 0x%llx", target);
- }
- else
- {
- uint8_t trimSize;
-
- trimSize = (Instrux->AddrMode == ND_ADDR_16) ? 2 : ((Instrux->AddrMode == ND_ADDR_32) ? 4 : 8);
-
- // Truncate the displacement size to the size of the address length.
- normDisp = ND_TRIM(trimSize, normDisp);
-
- status = NdSprintf(temp, sizeof(temp), "0x%llx", normDisp);
- }
- if (!ND_SUCCESS(status))
- {
- return status;
- }
-
- res = nd_strcat_s(Buffer, BufferSize, temp);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
-
- // And the ending "]"
- res = nd_strcat_s(Buffer, BufferSize, "]");
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
- break;
-
- default:
- return ND_STATUS_INVALID_INSTRUX;
- }
-
- // Handle memory broadcast.
- if (pOp->Decorator.HasBroadcast)
- {
- status = NdSprintf(temp, sizeof(temp), "{1to%d}", pOp->Decorator.Broadcast.Count);
- if (!ND_SUCCESS(status))
- {
- return status;
- }
-
- res = nd_strcat_s(Buffer, BufferSize, temp);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
-
- // Handle masking.
- if (pOp->Decorator.HasMask)
- {
- if (pOp->Decorator.Mask.Msk >= ND_MAX_MSK_REGS)
- {
- return ND_STATUS_INVALID_INSTRUX;
- }
-
- status = NdSprintf(temp, sizeof(temp), "{%s}", gRegMask[pOp->Decorator.Mask.Msk]);
- if (!ND_SUCCESS(status))
- {
- return status;
- }
-
- res = nd_strcat_s(Buffer, BufferSize, temp);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
-
- // Handle zeroing. Note that zeroing without masking is ignored.
- if (pOp->Decorator.HasZero && pOp->Decorator.HasMask)
- {
- res = nd_strcat_s(Buffer, BufferSize, "{z}");
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
-
- // Append Suppress All Exceptions decorator.
- if (pOp->Decorator.HasSae && !pOp->Decorator.HasEr)
- {
- // ER implies SAE, so if we have ER, we will list that.
- res = nd_strcat_s(Buffer, BufferSize, ", {sae}");
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
-
- // Append Embedded Rounding decorator.
- if (pOp->Decorator.HasEr)
- {
- if (Instrux->RoundingMode >= 4)
- {
- return ND_STATUS_INVALID_INSTRUX;
- }
-
- status = NdSprintf(temp, sizeof(temp), ", {%s-sae}", gEmbeddedRounding[Instrux->RoundingMode]);
- if (!ND_SUCCESS(status))
- {
- return status;
- }
-
- res = nd_strcat_s(Buffer, BufferSize, temp);
- RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
- }
- }
-
- return ND_STATUS_SUCCESS;
-}
-#else
-NDSTATUS
-NdToText(
- const INSTRUX *Instrux,
- uint64_t Rip,
- uint32_t BufferSize,
- char *Buffer
- )
-{
- UNREFERENCED_PARAMETER(Instrux);
- UNREFERENCED_PARAMETER(Rip);
-
- // At least make sure the buffer is NULL-terminated so integrators can use NdToText without checking if the
- // BDDISASM_NO_FORMAT macro is defined. This makes switching between versions with formatting and versions without
- // formatting easier.
- if (Buffer != NULL && BufferSize >= 1)
- {
- *Buffer = '\0';
- }
-
- return ND_STATUS_SUCCESS;
-}
-#endif // !BDDISASM_NO_FORMAT
//
diff --git a/bddisasm/bddisasm.vcxproj b/bddisasm/bddisasm.vcxproj
index e625ea6..ae1cbf3 100644
--- a/bddisasm/bddisasm.vcxproj
+++ b/bddisasm/bddisasm.vcxproj
@@ -420,6 +420,7 @@
+
NotUsing
diff --git a/bddisasm/bddisasm.vcxproj.filters b/bddisasm/bddisasm.vcxproj.filters
index 5ddd5ae..b4de484 100644
--- a/bddisasm/bddisasm.vcxproj.filters
+++ b/bddisasm/bddisasm.vcxproj.filters
@@ -27,6 +27,9 @@
Source Files
+
+ Source Files
+
diff --git a/bddisasm/bdformat.c b/bddisasm/bdformat.c
new file mode 100644
index 0000000..fc5842c
--- /dev/null
+++ b/bddisasm/bdformat.c
@@ -0,0 +1,1050 @@
+/*
+ * Copyright (c) 2020 Bitdefender
+ * SPDX-License-Identifier: Apache-2.0
+ */
+#include "include/nd_crt.h"
+#include "../inc/bddisasm.h"
+
+
+
+#ifndef BDDISASM_NO_FORMAT
+
+static const char *gReg8Bit[] =
+{
+ "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh",
+};
+
+static const char *gReg8Bit64[] =
+{
+ "al", "cl", "dl", "bl", "spl", "bpl", "sil", "dil",
+ "r8b", "r9b", "r10b", "r11b", "r12b", "r13b", "r14b", "r15b",
+};
+
+static const char *gReg16Bit[] =
+{
+ "ax", "cx", "dx", "bx", "sp", "bp", "si", "di",
+ "r8w", "r9w", "r10w", "r11w", "r12w", "r13w", "r14w", "r15w",
+};
+
+static const char *gReg32Bit[] =
+{
+ "eax", "ecx", "edx", "ebx", "esp", "ebp", "esi", "edi",
+ "r8d", "r9d", "r10d", "r11d", "r12d", "r13d", "r14d", "r15d",
+};
+
+static const char *gReg64Bit[] =
+{
+ "rax", "rcx", "rdx", "rbx", "rsp", "rbp", "rsi", "rdi",
+ "r8", "r9", "r10", "r11", "r12", "r13", "r14", "r15"
+};
+
+static const char *gRegFpu[] =
+{
+ "st0", "st1", "st2", "st3", "st4", "st5", "st6", "st7",
+};
+
+static const char *gRegMmx[] =
+{
+ "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
+};
+
+static const char *gRegControl[] =
+{
+ "cr0", "cr1", "cr2", "cr3", "cr4", "cr5", "cr6", "cr7",
+ "cr8", "cr9", "cr10", "cr11", "cr12", "cr13", "cr14", "cr15",
+};
+
+static const char *gRegDebug[] =
+{
+ "dr0", "dr1", "dr2", "dr3", "dr4", "dr5", "dr6", "dr7",
+ "dr8", "dr9", "dr10", "dr11", "dr12", "dr13", "dr14", "dr15",
+};
+
+static const char *gRegTest[] =
+{
+ "tr0", "tr1", "tr2", "tr3", "tr4", "tr5", "tr6", "tr7",
+ "tr8", "tr9", "tr10", "tr11", "tr12", "tr13", "tr14", "tr15",
+};
+
+static const char *gRegXmm[] =
+{
+ "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7",
+ "xmm8", "xmm9", "xmm10", "xmm11", "xmm12", "xmm13", "xmm14", "xmm15",
+ "xmm16", "xmm17", "xmm18", "xmm19", "xmm20", "xmm21", "xmm22", "xmm23",
+ "xmm24", "xmm25", "xmm26", "xmm27", "xmm28", "xmm29", "xmm30", "xmm31",
+};
+
+static const char *gRegYmm[] =
+{
+ "ymm0", "ymm1", "ymm2", "ymm3", "ymm4", "ymm5", "ymm6", "ymm7",
+ "ymm8", "ymm9", "ymm10", "ymm11", "ymm12", "ymm13", "ymm14", "ymm15",
+ "ymm16", "ymm17", "ymm18", "ymm19", "ymm20", "ymm21", "ymm22", "ymm23",
+ "ymm24", "ymm25", "ymm26", "ymm27", "ymm28", "ymm29", "ymm30", "ymm31"
+};
+
+static const char *gRegZmm[] =
+{
+ "zmm0", "zmm1", "zmm2", "zmm3", "zmm4", "zmm5", "zmm6", "zmm7",
+ "zmm8", "zmm9", "zmm10", "zmm11", "zmm12", "zmm13", "zmm14", "zmm15",
+ "zmm16", "zmm17", "zmm18", "zmm19", "zmm20", "zmm21", "zmm22", "zmm23",
+ "zmm24", "zmm25", "zmm26", "zmm27", "zmm28", "zmm29", "zmm30", "zmm31",
+};
+
+static const char *gRegSeg[] =
+{
+ "es", "cs", "ss", "ds", "fs", "gs", "segr6", "segr7",
+};
+
+static const char *gRegBound[] =
+{
+ "bnd0", "bnd1", "bnd2", "bnd3",
+};
+
+static const char *gRegMask[] =
+{
+ "k0", "k1", "k2", "k3", "k4", "k5", "k6", "k7",
+};
+
+static const char *gRegTile[] =
+{
+ "tmm0", "tmm1", "tmm2", "tmm3", "tmm4", "tmm5", "tmm6", "tmm7",
+};
+
+static const char *gConditionCodes[] =
+{
+ "EQ", "LT", "LE", "UNORD", "NEQ", "NLT", "NLE", "ORD",
+ "EQ_UQ", "NGE", "NGT", "false", "NEQ_OQ", "GE", "GT", "TRUE",
+ "EQ_OS", "LT_OQ", "LE_OQ", "UNORD_S", "NEQ_US", "NLT_UQ", "NLE_UQ", "ORD_S",
+ "EQ_US", "NGE_UQ", "NGT_UQ", "FALSE_OS", "NEQ_OS", "GE_OQ", "GT_OQ", "TRUE_US",
+};
+
+static const char *gEmbeddedRounding[] =
+{
+ "rn", "rd", "ru", "rz",
+};
+
+
+
+//
+// NdSprintf
+//
+static NDSTATUS
+NdSprintf(
+ char *Destination,
+ size_t DestinationSize,
+ const char *Formatstring,
+ ...
+ )
+//
+// Wrapper on vsnprintf.
+//
+{
+ int res;
+ va_list args;
+
+ if (NULL == Destination)
+ {
+ return ND_STATUS_INVALID_PARAMETER;
+ }
+
+ if (NULL == Formatstring)
+ {
+ return ND_STATUS_INVALID_PARAMETER;
+ }
+
+ nd_memzero(Destination, DestinationSize);
+
+ va_start(args, Formatstring);
+
+ // _vsnprintf is used instead of the more secure _vsnprintf_s because the mini-Petru supports just
+ // the unsecured version, and we depend on it.
+ res = nd_vsnprintf_s(Destination, DestinationSize, DestinationSize - 1, Formatstring, args);
+
+ va_end(args);
+
+ if ((res < 0) || ((size_t)res >= DestinationSize - 1))
+ {
+ return ND_STATUS_BUFFER_OVERFLOW;
+ }
+
+ return ND_STATUS_SUCCESS;
+}
+
+
+//
+// NdToText
+//
+NDSTATUS
+NdToText(
+ const INSTRUX *Instrux,
+ uint64_t Rip,
+ uint32_t BufferSize,
+ char *Buffer
+ )
+{
+ NDSTATUS status;
+ char *res, temp[64];
+ uint32_t opIndex, opsStored;
+ const ND_OPERAND *pOp;
+ bool alignmentStored;
+
+ // pre-init
+ status = ND_STATUS_SUCCESS;
+ res = NULL;
+ opIndex = 0;
+ opsStored = 0;
+ pOp = NULL;
+ alignmentStored = false;
+
+ // Validate args.
+ if (NULL == Instrux)
+ {
+ return ND_STATUS_INVALID_PARAMETER;
+ }
+
+ if (NULL == Buffer)
+ {
+ return ND_STATUS_INVALID_PARAMETER;
+ }
+
+ if (BufferSize < ND_MIN_BUF_SIZE)
+ {
+ return ND_STATUS_INVALID_PARAMETER;
+ }
+
+ // init the text.
+ nd_memzero(Buffer, BufferSize);
+ nd_memzero(temp, sizeof(temp));
+
+ // First, store the prefixes.
+ if (Instrux->Rep != 0)
+ {
+ // Check for REPZ/REPNZ support, and store prefixes.
+ if (ND_REPC_SUPPORT(Instrux))
+ {
+ if (Instrux->Rep == ND_PREFIX_G1_REPE_REPZ)
+ {
+ res = nd_strcat_s(Buffer, BufferSize, "REPZ ");
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+ else if (Instrux->Rep == ND_PREFIX_G1_REPNE_REPNZ)
+ {
+ res = nd_strcat_s(Buffer, BufferSize, "REPNZ ");
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+ }
+
+ // Check for REP support and store prefixes.
+ if (ND_REP_SUPPORT(Instrux))
+ {
+ if (Instrux->Rep == ND_PREFIX_G1_REPE_REPZ)
+ {
+ res = nd_strcat_s(Buffer, BufferSize, "REP ");
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+ else if (Instrux->Rep == ND_PREFIX_G1_REPNE_REPNZ)
+ {
+ res = nd_strcat_s(Buffer, BufferSize, "REPNZ ");
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+ }
+
+ if (Instrux->IsXreleaseEnabled)
+ {
+ res = nd_strcat_s(Buffer, BufferSize, "XRELEASE ");
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+ else if (Instrux->IsXacquireEnabled)
+ {
+ res = nd_strcat_s(Buffer, BufferSize, "XACQUIRE ");
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+ }
+
+ if (Instrux->HasLock)
+ {
+ if (ND_LOCK_SUPPORT(Instrux))
+ {
+ res = nd_strcat_s(Buffer, BufferSize, "LOCK ");
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+ }
+
+ if (Instrux->Rep == ND_PREFIX_G1_BND)
+ {
+ if (ND_BND_SUPPORT(Instrux))
+ {
+ res = nd_strcat_s(Buffer, BufferSize, "BND ");
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+ }
+
+ if (Instrux->HasSeg && ND_BHINT_SUPPORT(Instrux))
+ {
+ switch (Instrux->Seg)
+ {
+ case ND_PREFIX_G2_BR_TAKEN:
+ res = nd_strcat_s(Buffer, BufferSize, "BHT ");
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ break;
+
+ case ND_PREFIX_G2_BR_NOT_TAKEN:
+ res = nd_strcat_s(Buffer, BufferSize, "BHNT ");
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ break;
+
+ case ND_PREFIX_G2_BR_ALT:
+ res = nd_strcat_s(Buffer, BufferSize, "BHALT ");
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (Instrux->HasSeg && ND_DNT_SUPPORT(Instrux))
+ {
+ if (!Instrux->IsCetTracked)
+ {
+ res = nd_strcat_s(Buffer, BufferSize, "DNT ");
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+ }
+
+ // Store the mnemonic.
+ res = nd_strcat_s(Buffer, BufferSize, Instrux->Mnemonic);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+
+ // Store condition code, if any.
+ if (ND_HAS_SSE_CONDITION(Instrux))
+ {
+ res = nd_strcat_s(Buffer, BufferSize, gConditionCodes[Instrux->SseCondition]);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+
+ // If there are no explicit operands, we can leave.
+ if (0 == Instrux->ExpOperandsCount)
+ {
+ return ND_STATUS_SUCCESS;
+ }
+
+ // Now the operands.
+ for (opIndex = 0; opIndex < Instrux->OperandsCount; opIndex++)
+ {
+ status = ND_STATUS_SUCCESS;
+
+ pOp = &Instrux->Operands[opIndex];
+
+ if (pOp->Type == ND_OP_NOT_PRESENT)
+ {
+ break;
+ }
+
+ if (pOp->Flags.IsDefault)
+ {
+ continue;
+ }
+
+ // If this is a mask operand that has been used as masking for a previous operand, than we
+ // can safely skip it. We check this by seeing where is the operand encoded. If it's encoded
+ // in the evex.aaa field, than it is a conventional mask.
+ if ((pOp->Encoding == ND_OPE_A) && (pOp->Type == ND_OP_REG) &&
+ (pOp->Info.Register.Type == ND_REG_MSK) && (opIndex > 0))
+ {
+ continue;
+ }
+
+ // Store alignment.
+ if (!alignmentStored)
+ {
+ size_t idx = 0;
+
+ while ((idx < BufferSize) && (Buffer[idx]))
+ {
+ idx++;
+ }
+
+ while ((idx < 9) && (idx + 1 < BufferSize))
+ {
+ Buffer[idx++] = 0x20;
+ }
+
+ if (idx + 1 < BufferSize)
+ {
+ Buffer[idx++] = 0x20;
+ }
+
+ Buffer[idx] = 0;
+
+ alignmentStored = true;
+ }
+
+ // Store the comma, if this isn't the first operand.
+ if (opsStored > 0)
+ {
+ res = nd_strcat_s(Buffer, BufferSize, ", ");
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+
+ opsStored++;
+
+ switch (pOp->Type)
+ {
+ case ND_OP_REG:
+ switch (pOp->Info.Register.Type)
+ {
+ case ND_REG_GPR:
+ {
+ if (pOp->Info.Register.Reg >= ND_MAX_GPR_REGS)
+ {
+ return ND_STATUS_INVALID_INSTRUX;
+ }
+
+ // General purpose register.
+ switch (pOp->Info.Register.Size)
+ {
+ case ND_SIZE_8BIT:
+ // 8 bit register.
+ if ((Instrux->EncMode != ND_ENCM_LEGACY) || Instrux->HasRex)
+ {
+ res = nd_strcat_s(Buffer, BufferSize, gReg8Bit64[pOp->Info.Register.Reg]);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+ else
+ {
+ res = nd_strcat_s(Buffer, BufferSize, gReg8Bit[pOp->Info.Register.Reg]);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+ break;
+
+ case ND_SIZE_16BIT:
+ // 16 bit register.
+ res = nd_strcat_s(Buffer, BufferSize, gReg16Bit[pOp->Info.Register.Reg]);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ break;
+
+ case ND_SIZE_32BIT:
+ // 32 bit register.
+ res = nd_strcat_s(Buffer, BufferSize, gReg32Bit[pOp->Info.Register.Reg]);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ break;
+
+ case ND_SIZE_64BIT:
+ // 64 bit register.
+ res = nd_strcat_s(Buffer, BufferSize, gReg64Bit[pOp->Info.Register.Reg]);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ break;
+
+ default:
+ return ND_STATUS_INVALID_INSTRUX;
+ }
+ }
+ break;
+
+ case ND_REG_SEG:
+ {
+ if (pOp->Info.Register.Reg >= ND_MAX_SEG_REGS)
+ {
+ return ND_STATUS_INVALID_INSTRUX;
+ }
+
+ res = nd_strcat_s(Buffer, BufferSize, gRegSeg[pOp->Info.Register.Reg]);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+ break;
+
+ case ND_REG_FPU:
+ {
+ if (pOp->Info.Register.Reg >= ND_MAX_FPU_REGS)
+ {
+ return ND_STATUS_INVALID_INSTRUX;
+ }
+
+ res = nd_strcat_s(Buffer, BufferSize, gRegFpu[pOp->Info.Register.Reg]);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+ break;
+
+ case ND_REG_MMX:
+ {
+ if (pOp->Info.Register.Reg >= ND_MAX_MMX_REGS)
+ {
+ return ND_STATUS_INVALID_INSTRUX;
+ }
+
+ res = nd_strcat_s(Buffer, BufferSize, gRegMmx[pOp->Info.Register.Reg]);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+ break;
+
+ case ND_REG_SSE:
+ {
+ if (pOp->Info.Register.Reg >= ND_MAX_SSE_REGS)
+ {
+ return ND_STATUS_INVALID_INSTRUX;
+ }
+
+ switch (pOp->Info.Register.Size)
+ {
+ case ND_SIZE_128BIT:
+ res = nd_strcat_s(Buffer, BufferSize, gRegXmm[pOp->Info.Register.Reg]);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ break;
+ case ND_SIZE_256BIT:
+ res = nd_strcat_s(Buffer, BufferSize, gRegYmm[pOp->Info.Register.Reg]);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ break;
+ case ND_SIZE_512BIT:
+ res = nd_strcat_s(Buffer, BufferSize, gRegZmm[pOp->Info.Register.Reg]);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ break;
+ default:
+ return ND_STATUS_INVALID_INSTRUX;
+ }
+ }
+ break;
+
+ case ND_REG_CR:
+ {
+ if (pOp->Info.Register.Reg >= ND_MAX_CR_REGS)
+ {
+ return ND_STATUS_INVALID_INSTRUX;
+ }
+
+ res = nd_strcat_s(Buffer, BufferSize, gRegControl[pOp->Info.Register.Reg]);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+ break;
+
+ case ND_REG_DR:
+ {
+ if (pOp->Info.Register.Reg >= ND_MAX_DR_REGS)
+ {
+ return ND_STATUS_INVALID_INSTRUX;
+ }
+
+ res = nd_strcat_s(Buffer, BufferSize, gRegDebug[pOp->Info.Register.Reg]);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+ break;
+
+ case ND_REG_TR:
+ {
+ if (pOp->Info.Register.Reg >= ND_MAX_TR_REGS)
+ {
+ return ND_STATUS_INVALID_INSTRUX;
+ }
+
+ res = nd_strcat_s(Buffer, BufferSize, gRegTest[pOp->Info.Register.Reg]);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+ break;
+
+ case ND_REG_BND:
+ {
+ // Sanity check.
+ if (pOp->Info.Register.Reg >= ND_MAX_BND_REGS)
+ {
+ return ND_STATUS_INVALID_INSTRUX;
+ }
+
+ res = nd_strcat_s(Buffer, BufferSize, gRegBound[pOp->Info.Register.Reg]);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+ break;
+
+ case ND_REG_MSK:
+ {
+ // Sanity check.
+ if (pOp->Info.Register.Reg >= ND_MAX_MSK_REGS)
+ {
+ return ND_STATUS_INVALID_INSTRUX;
+ }
+
+ res = nd_strcat_s(Buffer, BufferSize, gRegMask[pOp->Info.Register.Reg]);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+ break;
+
+ case ND_REG_TILE:
+ {
+ // Sanity check.
+ if (pOp->Info.Register.Reg >= ND_MAX_TILE_REGS)
+ {
+ return ND_STATUS_INVALID_INSTRUX;
+ }
+
+ res = nd_strcat_s(Buffer, BufferSize, gRegTile[pOp->Info.Register.Reg]);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ if (pOp->Info.Register.Count > 1)
+ {
+ status = NdSprintf(temp, sizeof(temp), "+%d", pOp->Info.Register.Count - 1);
+ if (!ND_SUCCESS(status))
+ {
+ return status;
+ }
+
+ res = nd_strcat_s(Buffer, BufferSize, temp);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+
+ break;
+
+ case ND_OP_BANK:
+ // Nothing to show.
+ break;
+
+ case ND_OP_CONST:
+ {
+ // Implicit constant
+ status = NdSprintf(temp, sizeof(temp), "%d", pOp->Info.Constant.Const);
+ if (!ND_SUCCESS(status))
+ {
+ return status;
+ }
+
+ res = nd_strcat_s(Buffer, BufferSize, temp);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+ break;
+
+ case ND_OP_IMM:
+ {
+ switch (pOp->Size)
+ {
+ case 1:
+ status = NdSprintf(temp, sizeof(temp), "0x%02x", (uint8_t)pOp->Info.Immediate.Imm);
+ break;
+ case 2:
+ status = NdSprintf(temp, sizeof(temp), "0x%04x", (uint16_t)pOp->Info.Immediate.Imm);
+ break;
+ case 4:
+ status = NdSprintf(temp, sizeof(temp), "0x%08x", (uint32_t)pOp->Info.Immediate.Imm);
+ break;
+ case 8:
+ status = NdSprintf(temp, sizeof(temp), "0x%016llx", (uint64_t)pOp->Info.Immediate.Imm);
+ break;
+ }
+ if (!ND_SUCCESS(status))
+ {
+ return status;
+ }
+
+ res = nd_strcat_s(Buffer, BufferSize, temp);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+ break;
+
+ case ND_OP_OFFS:
+ {
+ uint64_t dest = Rip + Instrux->Length + pOp->Info.RelativeOffset.Rel;
+
+ // Truncate to the actual word length.
+ switch (Instrux->WordLength)
+ {
+ case 2:
+ dest &= 0xFFFF;
+ break;
+ case 4:
+ dest &= 0xFFFFFFFF;
+ break;
+ default:
+ break;
+ }
+
+ status = NdSprintf(temp, sizeof(temp), "0x%llx", dest);
+ if (!ND_SUCCESS(status))
+ {
+ return status;
+ }
+
+ res = nd_strcat_s(Buffer, BufferSize, temp);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+ break;
+
+ case ND_OP_ADDR:
+ {
+ switch (Instrux->AddrLength)
+ {
+ case 4:
+ status = NdSprintf(temp, sizeof(temp), "0x%04x:0x%04x",
+ pOp->Info.Address.BaseSeg, (uint16_t)pOp->Info.Address.Offset);
+ break;
+ case 6:
+ status = NdSprintf(temp, sizeof(temp), "0x%04x:0x%08x",
+ pOp->Info.Address.BaseSeg, (uint32_t)pOp->Info.Address.Offset);
+ break;
+ case 10:
+ status = NdSprintf(temp, sizeof(temp), "0x%04x:0x%016llx",
+ pOp->Info.Address.BaseSeg, (uint64_t)pOp->Info.Address.Offset);
+ break;
+ default:
+ return ND_STATUS_INVALID_INSTRUX;
+ }
+
+ if (!ND_SUCCESS(status))
+ {
+ return status;
+ }
+
+ res = nd_strcat_s(Buffer, BufferSize, temp);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+ break;
+
+ case ND_OP_MEM:
+ {
+ // Prepend the size. For VSIB addressing, store the VSIB element size, not the total accessed size.
+ ND_OPERAND_SIZE size = pOp->Info.Memory.IsVsib ? pOp->Info.Memory.Vsib.ElemSize : pOp->Size;
+
+ switch (size)
+ {
+ case 1:
+ res = nd_strcat_s(Buffer, BufferSize, "byte ptr ");
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ break;
+ case 2:
+ res = nd_strcat_s(Buffer, BufferSize, "word ptr ");
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ break;
+ case 4:
+ res = nd_strcat_s(Buffer, BufferSize, "dword ptr ");
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ break;
+ case 6:
+ res = nd_strcat_s(Buffer, BufferSize, "fword ptr ");
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ break;
+ case 8:
+ res = nd_strcat_s(Buffer, BufferSize, "qword ptr ");
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ break;
+ case 10:
+ res = nd_strcat_s(Buffer, BufferSize, "tbyte ptr ");
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ break;
+ case 16:
+ res = nd_strcat_s(Buffer, BufferSize, "xmmword ptr ");
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ break;
+ case 32:
+ res = nd_strcat_s(Buffer, BufferSize, "ymmword ptr ");
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ break;
+ case 48:
+ res = nd_strcat_s(Buffer, BufferSize, "m384 ptr ");
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ break;
+ case 64:
+ res = nd_strcat_s(Buffer, BufferSize, "zmmword ptr ");
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ break;
+ default:
+ break;
+ }
+
+ // Perpend the segment, only if it is overridden via a prefix.
+ if (pOp->Info.Memory.HasSeg && Instrux->HasSeg)
+ {
+ if (pOp->Info.Memory.Seg >= ND_MAX_SEG_REGS)
+ {
+ return ND_STATUS_INVALID_INSTRUX;
+ }
+
+ if ((ND_CODE_64 != Instrux->DefCode) || (NDR_FS == pOp->Info.Memory.Seg) ||
+ (NDR_GS == pOp->Info.Memory.Seg))
+ {
+ res = nd_strcat_s(Buffer, BufferSize, gRegSeg[pOp->Info.Memory.Seg]);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+
+ res = nd_strcat_s(Buffer, BufferSize, ":");
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+ }
+
+ // Prepend the "["
+ res = nd_strcat_s(Buffer, BufferSize, "[");
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+
+ // Base, if any.
+ if (pOp->Info.Memory.HasBase)
+ {
+ if (pOp->Info.Memory.Base >= ND_MAX_GPR_REGS)
+ {
+ return ND_STATUS_INVALID_INSTRUX;
+ }
+
+ switch (pOp->Info.Memory.BaseSize)
+ {
+ case ND_SIZE_8BIT:
+ res = nd_strcat_s(Buffer, BufferSize, gReg8Bit[pOp->Info.Memory.Base]);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ break;
+ case ND_SIZE_16BIT:
+ res = nd_strcat_s(Buffer, BufferSize, gReg16Bit[pOp->Info.Memory.Base]);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ break;
+ case ND_SIZE_32BIT:
+ res = nd_strcat_s(Buffer, BufferSize, gReg32Bit[pOp->Info.Memory.Base]);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ break;
+ case ND_SIZE_64BIT:
+ res = nd_strcat_s(Buffer, BufferSize, gReg64Bit[pOp->Info.Memory.Base]);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ break;
+ default:
+ return ND_STATUS_INVALID_INSTRUX;
+ }
+ }
+
+ // Index, if any. Special treatment for VSIB addressing. Also, perpend a "+" if base is present.
+ if (pOp->Info.Memory.HasIndex)
+ {
+ if (pOp->Info.Memory.Index >= (pOp->Info.Memory.IsVsib ? ND_MAX_SSE_REGS : ND_MAX_GPR_REGS))
+ {
+ return ND_STATUS_INVALID_INSTRUX;
+ }
+
+ if (pOp->Info.Memory.HasBase)
+ {
+ res = nd_strcat_s(Buffer, BufferSize, "+");
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+
+ switch (pOp->Info.Memory.IndexSize)
+ {
+ case ND_SIZE_8BIT:
+ res = nd_strcat_s(Buffer, BufferSize, gReg8Bit[pOp->Info.Memory.Index]);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ break;
+ case ND_SIZE_16BIT:
+ res = nd_strcat_s(Buffer, BufferSize, gReg16Bit[pOp->Info.Memory.Index]);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ break;
+ case ND_SIZE_32BIT:
+ res = nd_strcat_s(Buffer, BufferSize, gReg32Bit[pOp->Info.Memory.Index]);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ break;
+ case ND_SIZE_64BIT:
+ res = nd_strcat_s(Buffer, BufferSize, gReg64Bit[pOp->Info.Memory.Index]);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ break;
+ case ND_SIZE_128BIT:
+ res = nd_strcat_s(Buffer, BufferSize, gRegXmm[pOp->Info.Memory.Index]);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ break;
+ case ND_SIZE_256BIT:
+ res = nd_strcat_s(Buffer, BufferSize, gRegYmm[pOp->Info.Memory.Index]);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ break;
+ case ND_SIZE_512BIT:
+ res = nd_strcat_s(Buffer, BufferSize, gRegZmm[pOp->Info.Memory.Index]);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ break;
+ default:
+ return ND_STATUS_INVALID_INSTRUX;
+ }
+
+ // If index is present, scale is also present.
+ if (pOp->Info.Memory.Scale != 1 && !pOp->Info.Memory.IsMib)
+ {
+ status = NdSprintf(temp, sizeof(temp), "*%d", pOp->Info.Memory.Scale);
+ if (!ND_SUCCESS(status))
+ {
+ return status;
+ }
+
+ res = nd_strcat_s(Buffer, BufferSize, temp);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+ }
+
+ // Handle displacement.
+ if (pOp->Info.Memory.HasDisp)
+ {
+ uint64_t normDisp, disp;
+
+ disp = pOp->Info.Memory.Disp;
+
+ // If this is direct addressing (O operand) or we don't have base or index, than we don't normalize
+ // the displacement, since it is used as a direct offset. Note that the second condition also
+ // includes the RIP-relative case.
+ if (pOp->Info.Memory.IsDirect || !(pOp->Info.Memory.HasBase || pOp->Info.Memory.HasIndex))
+ {
+ normDisp = disp;
+ }
+ else
+ {
+ switch (pOp->Info.Memory.DispSize)
+ {
+ case 1:
+ normDisp = ((disp & 0x80) ? ~disp + 1ULL : disp) & 0xFF;
+ break;
+ case 2:
+ normDisp = ((disp & 0x8000) ? ~disp + 1ULL : disp) & 0xFFFF;
+ break;
+ case 4:
+ normDisp = ((disp & 0x80000000) ? ~disp + 1ULL : disp) & 0xFFFFFFFF;
+ break;
+ default:
+ normDisp = disp;
+ break;
+ }
+
+ // Handle compressed displacement. It is fine to cast the normDisp to uint32_t, as the
+ // compressed displacement only works with uint8_t displacements. Also, in this phase,
+ // the normDisp is converted to a positive quantity, so no sign-extension is needed.
+ if (pOp->Info.Memory.HasCompDisp)
+ {
+ normDisp = (uint64_t)(uint32_t)normDisp * pOp->Info.Memory.CompDispSize;
+ }
+ }
+
+
+ // Now displacement.
+ if (pOp->Info.Memory.HasBase || pOp->Info.Memory.HasIndex)
+ {
+ res = nd_strcat_s(Buffer, BufferSize, Instrux->SignDisp ? "-" : "+");
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+
+ if (pOp->Info.Memory.IsRipRel)
+ {
+ uint64_t target = disp + Rip + Instrux->Length;
+
+ if (Instrux->AddrMode == ND_ADDR_32)
+ {
+ target &= 0xFFFFFFFF;
+ }
+
+ status = NdSprintf(temp, sizeof(temp), "rel 0x%llx", target);
+ }
+ else
+ {
+ uint8_t trimSize;
+
+ trimSize = (Instrux->AddrMode == ND_ADDR_16) ? 2 : ((Instrux->AddrMode == ND_ADDR_32) ? 4 : 8);
+
+ // Truncate the displacement size to the size of the address length.
+ normDisp = ND_TRIM(trimSize, normDisp);
+
+ status = NdSprintf(temp, sizeof(temp), "0x%llx", normDisp);
+ }
+ if (!ND_SUCCESS(status))
+ {
+ return status;
+ }
+
+ res = nd_strcat_s(Buffer, BufferSize, temp);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+
+ // And the ending "]"
+ res = nd_strcat_s(Buffer, BufferSize, "]");
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+ break;
+
+ default:
+ return ND_STATUS_INVALID_INSTRUX;
+ }
+
+ // Handle memory broadcast.
+ if (pOp->Decorator.HasBroadcast)
+ {
+ status = NdSprintf(temp, sizeof(temp), "{1to%d}", pOp->Decorator.Broadcast.Count);
+ if (!ND_SUCCESS(status))
+ {
+ return status;
+ }
+
+ res = nd_strcat_s(Buffer, BufferSize, temp);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+
+ // Handle masking.
+ if (pOp->Decorator.HasMask)
+ {
+ if (pOp->Decorator.Mask.Msk >= ND_MAX_MSK_REGS)
+ {
+ return ND_STATUS_INVALID_INSTRUX;
+ }
+
+ status = NdSprintf(temp, sizeof(temp), "{%s}", gRegMask[pOp->Decorator.Mask.Msk]);
+ if (!ND_SUCCESS(status))
+ {
+ return status;
+ }
+
+ res = nd_strcat_s(Buffer, BufferSize, temp);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+
+ // Handle zeroing. Note that zeroing without masking is ignored.
+ if (pOp->Decorator.HasZero && pOp->Decorator.HasMask)
+ {
+ res = nd_strcat_s(Buffer, BufferSize, "{z}");
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+
+ // Append Suppress All Exceptions decorator.
+ if (pOp->Decorator.HasSae && !pOp->Decorator.HasEr)
+ {
+ // ER implies SAE, so if we have ER, we will list that.
+ res = nd_strcat_s(Buffer, BufferSize, ", {sae}");
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+
+ // Append Embedded Rounding decorator.
+ if (pOp->Decorator.HasEr)
+ {
+ if (Instrux->RoundingMode >= 4)
+ {
+ return ND_STATUS_INVALID_INSTRUX;
+ }
+
+ status = NdSprintf(temp, sizeof(temp), ", {%s-sae}", gEmbeddedRounding[Instrux->RoundingMode]);
+ if (!ND_SUCCESS(status))
+ {
+ return status;
+ }
+
+ res = nd_strcat_s(Buffer, BufferSize, temp);
+ RET_EQ(res, NULL, ND_STATUS_BUFFER_OVERFLOW);
+ }
+ }
+
+ return ND_STATUS_SUCCESS;
+}
+#else
+NDSTATUS
+NdToText(
+ const INSTRUX *Instrux,
+ uint64_t Rip,
+ uint32_t BufferSize,
+ char *Buffer
+ )
+{
+ UNREFERENCED_PARAMETER(Instrux);
+ UNREFERENCED_PARAMETER(Rip);
+
+ // At least make sure the buffer is NULL-terminated so integrators can use NdToText without checking if the
+ // BDDISASM_NO_FORMAT macro is defined. This makes switching between versions with formatting and versions without
+ // formatting easier.
+ if (Buffer != NULL && BufferSize >= 1)
+ {
+ *Buffer = '\0';
+ }
+
+ return ND_STATUS_SUCCESS;
+}
+#endif // !BDDISASM_NO_FORMAT
diff --git a/bddisasm/include/nd_crt.h b/bddisasm/include/nd_crt.h
index da38ef8..b1281cd 100644
--- a/bddisasm/include/nd_crt.h
+++ b/bddisasm/include/nd_crt.h
@@ -90,4 +90,11 @@ extern void *nd_memset(void *s, int c, size_t n);
#define nd_memzero(Dest, Size) nd_memset((Dest), 0, (Size))
+
+// Handy macros.
+#define RET_EQ(x, y, z) if ((x) == (y)) { return (z); }
+#define RET_GE(x, y, z) if ((x) >= (y)) { return (z); }
+#define RET_GT(x, y, z) if ((x) > (y)) { return (z); }
+
+
#endif // ND_CRT_H
diff --git a/bdshemu/bdshemu.c b/bdshemu/bdshemu.c
index 2de47b0..ef89ca2 100644
--- a/bdshemu/bdshemu.c
+++ b/bdshemu/bdshemu.c
@@ -1693,8 +1693,18 @@ ShemuPrintContext(
Context->Registers.RegR8, Context->Registers.RegR9, Context->Registers.RegR10, Context->Registers.RegR11);
shemu_printf(Context, " R12 = 0x%016llx R13 = 0x%016llx R14 = 0x%016llx R15 = 0x%016llx\n",
Context->Registers.RegR12, Context->Registers.RegR13, Context->Registers.RegR14, Context->Registers.RegR15);
- shemu_printf(Context, " RIP = 0x%016llx RFLAGS = 0x%016llx\n",
+ shemu_printf(Context, " RIP = 0x%016llx RFLAGS = 0x%016llx ",
Context->Registers.RegRip, Context->Registers.RegFlags);
+ shemu_printf(Context, " CF:%d PF:%d AF:%d ZF:%d SF:%d TF:%d IF:%d DF:%d OF:%d\n",
+ GET_FLAG(Context, NDR_RFLAG_CF),
+ GET_FLAG(Context, NDR_RFLAG_PF),
+ GET_FLAG(Context, NDR_RFLAG_AF),
+ GET_FLAG(Context, NDR_RFLAG_ZF),
+ GET_FLAG(Context, NDR_RFLAG_SF),
+ GET_FLAG(Context, NDR_RFLAG_TF),
+ GET_FLAG(Context, NDR_RFLAG_IF),
+ GET_FLAG(Context, NDR_RFLAG_DF),
+ GET_FLAG(Context, NDR_RFLAG_OF));
shemu_printf(Context, "Emulating: 0x%016llx %s\n", Context->Registers.RegRip, text);
}
@@ -1711,6 +1721,7 @@ ShemuEmulate(
SHEMU_VALUE res = { 0 }, dst = { 0 }, src = { 0 }, rcx = { 0 }, aux = { 0 };
bool stop = false, cf;
uint16_t cs = 0;
+ uint64_t tsc = 0x1248fe7a5c30;
if (NULL == Context)
{
@@ -1753,6 +1764,8 @@ ShemuEmulate(
uint64_t rip;
uint32_t i;
+ tsc++;
+
// Reset all the operands to 0.
nd_memzero(&dst, sizeof(dst));
nd_memzero(&src, sizeof(src));
@@ -3058,6 +3071,17 @@ check_far_branch:
stop = true;
break;
+ case ND_INS_SIDT:
+ if (Context->Ring == 0)
+ {
+ // Flag this only in ring0, as we treat the SHEMU_FLAG_SIDT as a ring0 specific indicator - it can be
+ // used to locate the kernel image.
+ Context->Flags |= SHEMU_FLAG_SIDT;
+ }
+
+ stop = true;
+ break;
+
case ND_INS_AESIMC:
case ND_INS_AESDEC:
case ND_INS_AESDECLAST:
@@ -3096,6 +3120,16 @@ check_far_branch:
break;
}
+ case ND_INS_RDTSC:
+ src.Size = 4;
+ // Set EAX to lower 32 bits.
+ src.Value.Dwords[0] = tsc & 0xFFFFFFFF;
+ SET_OP(Context, 0, &src);
+ // Set EDX to upper 32 bits.
+ src.Value.Dwords[0] = tsc >> 32;
+ SET_OP(Context, 1, &src);
+ break;
+
default:
return SHEMU_ABORT_UNSUPPORTED_INSTRUX;
diff --git a/bdshemu_test/bdshemu_test.zip b/bdshemu_test/bdshemu_test.zip
index 5ed1f78..31cdd81 100644
Binary files a/bdshemu_test/bdshemu_test.zip and b/bdshemu_test/bdshemu_test.zip differ
diff --git a/inc/version.h b/inc/version.h
index 37f5a59..48c1909 100644
--- a/inc/version.h
+++ b/inc/version.h
@@ -7,6 +7,6 @@
#define DISASM_VERSION_MAJOR 1
#define DISASM_VERSION_MINOR 34
-#define DISASM_VERSION_REVISION 4
+#define DISASM_VERSION_REVISION 5
#endif // DISASM_VER_H