0
0
mirror of https://github.com/netwide-assembler/nasm.git synced 2025-09-22 10:43:39 -04:00
Files
nasm/disasm/disasm.c
H. Peter Anvin a92616af5c disasm: fix the masking of the V4/X4 bit
This bit needs to be masked *except* when:
- There is no V operand
- The modr/m has mod == 3 OR there is an scc

Signed-off-by: H. Peter Anvin (Intel) <hpa@zytor.com>
2025-09-05 03:32:28 -07:00

1811 lines
53 KiB
C

/* ----------------------------------------------------------------------- *
*
* Copyright 1996-2025 The NASM Authors - All Rights Reserved
* See the file AUTHORS included with the NASM distribution for
* the specific copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following
* conditions are met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
* CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
* EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ----------------------------------------------------------------------- */
/*
* disasm.c where all the _work_ gets done in the Netwide Disassembler
*
* See x86/bytecode.txt for the definition of the instruction encoding
* byte codes.
*/
#include "compiler.h"
#include "disasm.h"
#include "sync.h"
#include "insns.h"
#include "tables.h"
#include "regdis.h"
#include "bytesex.h"
#include "disp8.h"
#define fetch_or_return(_start, _ptr, _size, _need) \
fetch_safe(_start, _ptr, _size, _need, return 0)
/*
* Flags that go into the `segment' field of `operand' structures
*/
#define SEG_RELATIVE 1
#define SEG_RMREG 2
#define SEG_NODISP 4
#define SEG_DISP8 8
#define SEG_DISP16 16
#define SEG_DISP32 32
#define SEG_DISP64 64
#define SEG_DISPMASK (SEG_NODISP|SEG_DISP8|SEG_DISP16|SEG_DISP32|SEG_DISP64)
#define SEG_SIGNED 128
#define SEG_RMMEM 256
#define SEG_DFV 512
#define SEG_16BIT (16 << 8)
#define SEG_32BIT (32 << 8)
#define SEG_64BIT (64 << 8)
#define SEG_BITMASK (SEG_16BIT|SEG_32BIT|SEG_64BIT)
/* These get the address size associated with *one particular operand* */
static inline uint32_t seg_set_asize(uint32_t seg, unsigned int asize)
{
return (seg & ~SEG_BITMASK) | (asize << 8);
}
static inline unsigned int seg_get_asize(uint32_t seg)
{
return (seg & SEG_BITMASK) >> 8;
}
/*
* Important: regval must already have been adjusted for rex extensions;
* the rex flags are only used to determine which 8-bit register decoding
* to use.
*/
static enum reg_enum whichreg(opflags_t regflags, int regval, uint32_t rex)
{
/* Unsigned value suitable for array lookup */
const size_t r = regval;
size_t i;
static const struct {
opflags_t flags;
const enum reg_enum *regs;
} regclasses[] = {
{REG16, nasm_rd_reg16},
{REG32, nasm_rd_reg32},
{REG64, nasm_rd_reg64},
{REG_SREG, nasm_rd_sreg},
{REG_CREG, nasm_rd_creg},
{REG_DREG, nasm_rd_dreg},
{REG_TREG, nasm_rd_treg},
{FPUREG, nasm_rd_fpureg},
{MMXREG, nasm_rd_mmxreg},
{XMMREG, nasm_rd_xmmreg},
{YMMREG, nasm_rd_ymmreg},
{ZMMREG, nasm_rd_zmmreg},
{OPMASKREG, nasm_rd_opmaskreg},
{BNDREG, nasm_rd_bndreg},
{TMMREG, nasm_rd_tmmreg},
};
/* All the entries below look up regval in 32-entry arrays */
if (r >= DISREGTBLSZ)
return 0;
if (!(regflags & (REGISTER|REGMEM)))
return 0; /* Registers not permissible?! */
if (!(regflags & REG_CLASS_GPR))
regflags &= ~SIZE_MASK;
if (is_class(REG_CLASS_GPR|BITS8, regflags)) {
if (rex & (REX_P|REX_NH))
return nasm_rd_reg8_rex[r];
else
return nasm_rd_reg8[r];
}
for (i = 0; i < ARRAY_SIZE(regclasses); i++) {
enum reg_enum reg = regclasses[i].regs[r];
if (is_class(regflags, nasm_reg_flags[reg]))
return reg;
}
return 0; /* Unknown register type */
}
/*
* An implicit register operand
*/
static enum reg_enum implicit_reg(opflags_t regflags)
{
switch (regflags) {
case REG_AL: return R_AL;
case REG_AX: return R_AX;
case REG_EAX: return R_EAX;
case REG_RAX: return R_RAX;
case REG_DL: return R_DL;
case REG_DX: return R_DX;
case REG_EDX: return R_EDX;
case REG_RDX: return R_RDX;
case REG_CL: return R_CL;
case REG_CX: return R_CX;
case REG_ECX: return R_ECX;
case REG_RCX: return R_RCX;
case FPU0: return R_ST0;
case XMM0: return R_XMM0;
case YMM0: return R_YMM0;
case ZMM0: return R_ZMM0;
case REG_ES: return R_ES;
case REG_CS: return R_CS;
case REG_SS: return R_SS;
case REG_DS: return R_DS;
case REG_FS: return R_FS;
case REG_GS: return R_GS;
case OPMASK0: return R_K0;
default: return 0;
}
}
/*
* Pick the correct REX.B prefix to add to this base register and
* get the corresponding register number.
*/
static enum reg_enum whichbreg(opflags_t regflags, int regval, uint32_t rex,
const struct prefix_info *pf)
{
if (regflags & (REG_CLASS_RM_XMM|REG_CLASS_RM_YMM|
REG_CLASS_RM_ZMM|REG_CLASS_RM_TMM))
regval += pf->rex.bregbv;
else
regval += pf->rex.breg;
return whichreg(regflags, regval, rex);
}
static enum reg_enum whichrreg(opflags_t regflags, int regval, uint32_t rex,
const struct prefix_info *pf)
{
return whichreg(regflags, regval + pf->rex.rreg, rex);
}
static uint32_t append_evex_reg_deco(char *buf, uint32_t num,
decoflags_t deco,
const struct prefix_info *pf)
{
const char * const er_names[] = {"rn-sae", "rd-sae", "ru-sae", "rz-sae"};
uint32_t num_chars = 0;
if ((deco & MASK) && pf->rex.aaa) {
enum reg_enum opmasknum = nasm_rd_opmaskreg[pf->rex.aaa];
const char * regname = nasm_reg_names[opmasknum - EXPR_REG_START];
num_chars += snprintf(buf + num_chars, num - num_chars,
"{%s}", regname);
if ((deco & Z) && pf->rex.z) {
num_chars += snprintf(buf + num_chars, num - num_chars,
"{z}");
}
}
if (pf->rex.b) {
if (deco & ER) {
num_chars += snprintf(buf + num_chars, num - num_chars,
",{%s}", er_names[pf->rex.l]);
} else if (deco & SAE) {
num_chars += snprintf(buf + num_chars, num - num_chars,
",{sae}");
}
}
return num_chars;
}
static uint32_t append_evex_mem_deco(char *buf, uint32_t num, opflags_t type,
decoflags_t deco,
const struct prefix_info *pf)
{
uint32_t num_chars = 0;
if (pf->rex.b && (deco & BRDCAST_MASK)) {
decoflags_t deco_brsize = deco & BRSIZE_MASK;
opflags_t template_opsize = brsize_to_size(deco_brsize);
unsigned int br_num = (type & SIZE_MASK) / BITS128 *
BITS64 / template_opsize * 2;
num_chars += snprintf(buf + num_chars, num - num_chars,
"{1to%d}", br_num);
}
if ((deco & MASK) && pf->rex.aaa) {
enum reg_enum opmasknum = nasm_rd_opmaskreg[pf->rex.aaa];
const char * regname = nasm_reg_names[opmasknum - EXPR_REG_START];
num_chars += snprintf(buf + num_chars, num - num_chars,
"{%s}", regname);
if ((deco & Z) && pf->rex.z) {
num_chars += snprintf(buf + num_chars, num - num_chars,
"{z}");
}
}
return num_chars;
}
/*
* Process an effective address (ModRM) specification.
*/
static const uint8_t *do_ea(const uint8_t *data, int modrm,
int bits, enum ea_type type,
operand *op, insn *ins,
const struct prefix_info *prefix)
{
int mod, rm, scale, index, base;
int asize = ins->addr_size;
mod = (modrm >> 6) & 03;
rm = modrm & 07;
if (mod == 3) { /* pure register version */
op->basereg = whichbreg(op->type, rm, ins->rex, prefix);
if (!op->basereg)
return NULL;
op->segment |= SEG_RMREG;
return data;
}
op->disp_size = 0;
op->eaflags = 0;
op->segment = seg_set_asize(op->segment, asize) | SEG_RMMEM;
if (asize == 16) {
/*
* <mod> specifies the displacement size (none, byte or
* word), and <rm> specifies the register combination.
* Exception: mod=0,rm=6 does not specify [BP] as one might
* expect, but instead specifies [disp16].
*/
if (type != EA_SCALAR)
return NULL;
op->indexreg = op->basereg = 0;
op->scale = 1; /* always, in 16 bits */
switch (rm) {
case 0:
op->basereg = R_BX;
op->indexreg = R_SI;
break;
case 1:
op->basereg = R_BX;
op->indexreg = R_DI;
break;
case 2:
op->basereg = R_BP;
op->indexreg = R_SI;
break;
case 3:
op->basereg = R_BP;
op->indexreg = R_DI;
break;
case 4:
op->basereg = R_SI;
break;
case 5:
op->basereg = R_DI;
break;
case 6:
op->basereg = R_BP;
break;
case 7:
op->basereg = R_BX;
break;
}
switch (mod) {
case 0:
if (rm != 6) {
op->segment |= SEG_NODISP;
op->disp_size = 0;
break;
} else {
/* disp16 only, no base register */
op->basereg = 0;
}
/* fall through */
case 2:
op->segment |= SEG_DISP16;
op->disp_size = 16;
op->offset = gets16(data);
data += 2;
break;
case 1:
op->segment |= SEG_DISP8;
op->offset = gets8(data) << get_disp8_shift(ins);
op->disp_size = 8;
data++;
break;
}
return data;
} else {
/*
* Once again, <mod> specifies displacement size (this time
* none, byte or *dword*), while <rm> specifies the base
* register. Again, [EBP] is missing, replaced by a pure
* disp32 (this time that's mod=0,rm=*5*) in 32-bit mode,
* and RIP-relative addressing in 64-bit mode.
*
* However, rm=4
* indicates not a single base register, but instead the
* presence of a SIB byte...
*/
const enum reg_enum *regs;
regs = (asize == 64) ? nasm_rd_reg64 : nasm_rd_reg32;
op->basereg = op->indexreg = 0;
base = rm;
if (rm == 4) {
/* There is a SIB byte */
uint8_t sib = *data++;
scale = (sib >> 6) & 03;
index = (sib >> 3) & 07;
base = sib & 07;
op->scale = 1 << scale;
if (type != EA_SCALAR) {
index += prefix->rex.xregxv;
/* A VSIB always has an index register */
if (type == EA_XMMVSIB)
op->indexreg = nasm_rd_xmmreg[index];
else if (type == EA_YMMVSIB)
op->indexreg = nasm_rd_ymmreg[index];
else if (type == EA_ZMMVSIB)
op->indexreg = nasm_rd_zmmreg[index];
} else {
/* Not a VSIB */
index += prefix->rex.xreg;
/* ESP/RSP cannot be an index */
if (index != 4)
op->indexreg = regs[index];
}
} else {
/* Can't have VSIB without SIB */
if (type != EA_SCALAR)
return NULL;
base = rm;
}
if (base == 5 && mod == 0) {
/* disp32 without anything else */
if (rm != 4 && bits == 64) {
/* If no SIB byte, this is IP-relative in 64-bit mode */
op->eaflags |= EAF_REL;
op->segment |= SEG_RELATIVE;
}
op->basereg = 0;
mod = 2; /* force disp32 */
} else {
/* "Normal" base encoding */
op->basereg = regs[base + prefix->rex.breg];
}
switch (mod) {
case 0:
op->segment |= SEG_NODISP;
op->disp_size = 0;
break;
case 1:
op->segment |= SEG_DISP8;
op->disp_size = 8;
op->offset = gets8(data) << get_disp8_shift(ins);
data++;
break;
case 2:
op->segment |= SEG_DISP32;
op->disp_size = 32;
op->offset = gets32(data);
data += 4;
break;
}
return data;
}
}
/*
* Return true if the following byte is a modr/m byte which is a memory
* encoding (mod != 3).
*/
static bool is_mem_modrm(const uint8_t *bytecode, const uint8_t *data)
{
uint8_t b = *bytecode & 0344;
if (b == 0100 || b == 0200)
return (*data & 0300) != 0300;
else
return false; /* Not a modr/m per instruction template */
}
/*
* Determine whether the instruction template in ins->itemp
* corresponds to the data stream in data and parse it into "ins" if
* so. "ins" has already been partially initialized in disasm() to
* point contain the address of the instruction (loc.offset), the mode
* (bits), and the desired template (itemp); all other fields must be
* initialized to zero.
*
* The "data" pointer should point to the first byte after parsed
* prefixes (after call to parse_prefixes()), and must have enough
* slack buffer space that the longest template cannot overrun the
* data buffer, although the content beyond the end of any valid
* instruction is allowed to be garbage.
*/
#define case4(x) case (x): case (x)+1: case (x)+2: case (x)+3
static int matches(const uint8_t *data, const struct prefix_info *prefix,
insn *ins)
{
const struct itemplate * const t = ins->itemp;
const uint8_t *r = t->code;
const uint8_t * const origdata = data; /* First byte after prefixes */
bool a_used = false, o_used = false;
enum prefixes drep = 0;
enum prefixes dwait = 0;
enum prefixes dlock = prefix->lock ? P_LOCK : 0;
int osize = prefix->osize;
int asize = prefix->asize;
const int bits = ins->bits;
const int defosize = (bits == 16) ? 16 : 32;
int i, c;
int op1, op2;
struct operand *opx, *opy;
uint8_t opex = 0;
uint8_t regmask = (bits == 64) ? 31 : 7;
enum ea_type eat = EA_SCALAR;
decoflags_t decoflags = 0;
if (bits == 64 && prefix->rex.w &&
(prefix->rex.type < REX_VEX || itemp_has(t, IF_WW)))
ins->op_size = osize = 64;
ins->addr_size = asize;
ins->op_size = osize;
ins->itemp = t;
ins->opcode = t->opcode;
ins->operands = t->operands;
for (i = 0; i < ins->operands; i++) {
struct operand *o = &ins->oprs[i];
o->type = t->opd[i];
o->opidx = i;
if (is_class(IMMEDIATE, t->opd[i])) {
o->segment = seg_set_asize(bits, bits);
} else if (is_class(MEM_OFFS, t->opd[i])) {
a_used = true;
o->segment = seg_set_asize(bits, asize) | SEG_RMMEM;
} else {
o->segment = seg_set_asize(bits, asize);
}
decoflags |= t->deco[i];
}
/* Decorators are only possible with EVEX */
if (decoflags && prefix->rex.type != REX_EVEX)
return 0;
ins->rex = prefix->rex.flags;
if (itemp_has(t, (bits == 64 ? IF_NOLONG : IF_LONG)))
return 0;
if (prefix->rep == 0xF2)
drep = (itemp_has(t, IF_BND) ? P_BND : P_REPNE);
else if (prefix->rep == 0xF3)
drep = P_REP;
dwait = prefix->wait ? P_WAIT : 0;
while ((c = *r++) != 0) {
op1 = (c & 3) + ((opex & 1) << 2);
op2 = ((c >> 3) & 3) + ((opex & 2) << 1);
opx = &ins->oprs[op1];
opy = &ins->oprs[op2];
opex = 0;
switch (c) {
case 01:
case 02:
case 03:
case 04:
while (c--)
if (*r++ != *data++)
return 0;
break;
case 05:
case 06:
case 07:
opex = c;
break;
case4(010):
{
int t = *r++, d = *data++;
if (d < t || d > t + 7)
return 0;
else {
opx->basereg = whichbreg(opx->type, d-t, ins->rex, prefix);
opx->segment |= SEG_RMREG;
}
break;
}
case4(014):
/* this is an separate index reg position of MIB operand (ICC) */
/* Disassembler uses NASM's split EA form only */
break;
case4(0274):
opx->offset = (int8_t)*data++;
opx->disp_size = 8;
opx->segment |= SEG_SIGNED;
break;
case4(020):
opx->offset = *data++;
opx->disp_size = 8;
break;
case4(024):
opx->offset = *data++;
opx->disp_size = 8;
break;
case4(030):
opx->offset = getu16(data);
opx->disp_size = 16;
data += 2;
break;
case4(034):
if (osize == 32) {
opx->offset = getu32(data);
opx->disp_size = 32;
data += 4;
} else {
opx->offset = getu16(data);
opx->disp_size = 16;
data += 2;
}
break;
case4(040):
opx->offset = getu32(data);
opx->disp_size = 32;
data += 4;
break;
case4(0254):
opx->offset = gets32(data);
opx->disp_size = 32;
data += 4;
break;
case4(044):
opx->disp_size = asize;
switch (asize) {
case 16:
opx->offset = getu16(data);
data += 2;
break;
case 32:
opx->offset = getu32(data);
data += 4;
break;
case 64:
opx->offset = getu64(data);
data += 8;
break;
}
break;
case4(050):
opx->offset = gets8(data++);
opx->disp_size = 8;
opx->segment |= SEG_RELATIVE;
break;
case4(054):
opx->offset = getu64(data);
opx->disp_size = 64;
data += 8;
break;
case4(060):
opx->offset = gets16(data);
opx->disp_size = 16;
data += 2;
opx->segment = seg_set_asize(opx->segment, 16) | SEG_RELATIVE;
break;
case4(064): /* rel */
if (bits == 64) {
/*
* In long mode rel is always 32 bits, sign extended.
* REX.W or 66 prefixes have no effect.
*/
opx->offset = gets32(data);
opx->disp_size = 32;
data += 4;
opx->segment = seg_set_asize(opx->segment, 64) | SEG_RELATIVE;
} else if (osize == 32) {
opx->offset = gets32(data);
opx->disp_size = 32;
data += 4;
opx->segment = seg_set_asize(opx->segment, 32) | SEG_RELATIVE;
} else {
opx->offset = gets16(data);
opx->disp_size = 16;
data += 2;
opx->segment = seg_set_asize(opx->segment, 16) | SEG_RELATIVE;
}
break;
case4(070):
opx->offset = gets32(data);
opx->disp_size = 32;
data += 4;
opx->disp_size = 32;
opx->segment = seg_set_asize(opx->segment, bits == 64 ? 64 : 32)
| SEG_RELATIVE;
break;
case4(0100):
case4(0110):
case4(0120):
case4(0130):
{
int modrm = *data++;
data = do_ea(data, modrm, bits, eat, opy, ins, prefix);
if (!data)
return 0;
a_used |= !!(opy->segment & SEG_RMMEM);
opx->basereg = whichrreg(opx->type, (modrm >> 3) & 7,
ins->rex, prefix);
if (!opx->basereg)
return 0;
opx->segment |= SEG_RMREG;
break;
}
case 0171:
{
uint8_t t = *r++;
uint8_t d = *data++;
if ((d ^ t) & ~070) {
return 0;
} else {
op2 = (op2 & ~3) | ((t >> 3) & 3);
opy = &ins->oprs[op2];
opy->basereg = whichrreg(opy->type, (d >> 3) & 7,
ins->rex, prefix);
if (!opy->basereg)
return 0;
opy->segment |= SEG_RMREG;
}
break;
}
case 0172:
c = *r++;
opx = &ins->oprs[(c >> 3) & 7];
ins->oprs[c & 7].offset = *data & 7;
goto do_is4;
case 0173:
c = *r++;
if ((c ^ *data) & 7)
return 0;
opx = &ins->oprs[(c >> 4) & 7];
goto do_is4;
case4(0174):
do_is4:
{
uint8_t ximm = *data++;
unsigned int nreg = ((ximm >> 4) + ((ximm & 8) << 1)) & regmask;
opx->basereg = whichbreg(opx->type, nreg, ins->rex, prefix);
if (!opx->basereg)
return 0;
opx->segment |= SEG_RMREG;
break;
}
case4(0200):
case4(0204):
case4(0210):
case4(0214):
case4(0220):
case4(0224):
case4(0230):
case4(0234):
{
int modrm = *data++;
if (((modrm >> 3) & 07) != (c & 07))
return 0; /* spare field doesn't match up */
data = do_ea(data, modrm, bits, eat, opy, ins, prefix);
if (!data)
return 0;
a_used |= !!(opy->segment & SEG_RMMEM);
break;
}
case4(0240):
case 0250:
{
uint32_t evextemp, mismatch, matchmask;
bool memop;
unsigned int vmask;
if (prefix->rex.type != REX_EVEX)
return 0;
ins->rex |= REX_EV;
/* Get the evex prefix expected by the template */
evextemp = (getu32(r-1) & ~0xff) + 0x62; r += 3;
mismatch = prefix->rex.raw ^ evextemp;
memop = is_mem_modrm(r, data+1);
/* Now mask out "mismatched" bits that are proper parameters */
matchmask = ~(EVEX_P0BP|EVEX_P0RP|EVEX_P0B|EVEX_P0X|EVEX_P0R|
EVEX_P1XP);
if (c != 0250 || (memop && !itemp_has(t, IF_SCC))) {
/*
* V4 unless X is a is a vector (which is only
* possible if this is a memory operation), in which
* case this is X4. However, X4 is explicitly
* forbidden if this is not a memory operation,
* otherwise this bit is supposedly ignored if not
* used.
*/
matchmask &= ~EVEX_P2VP; /* Either V4 or X4 */
}
if (c == 0250) {
vmask = 0; /* No V register */
} else if (is_class(IMMEDIATE, opx->type)) {
vmask = 15; /* V operand is used as immediate */
opx->offset = (mismatch >> 19) & vmask;
if (itemp_has(t, IF_DFV))
opx->segment |= SEG_DFV;
} else {
unsigned int regnum;
vmask = regmask;
opx->segment |= SEG_RMREG;
if (eat >= EA_XMMVSIB)
regnum = prefix->rex.vregxv;
else
regnum = prefix->rex.vreg;
opx->basereg = whichreg(opx->type, regnum & vmask, ins->rex);
if (!opx->basereg)
return 0;
}
matchmask &= ~((vmask & 15) << 19); /* VVVV field */
if (prefix->rex.w)
ins->rex |= REX_W;
if (itemp_has(t, IF_WIG)) {
ins->rex &= ~REX_W;
ins->rex |= REX_NW;
matchmask &= ~EVEX_P1W;
}
if (itemp_has(t, IF_LIG) ||
(prefix->rex.b && !memop)) /* LL used for rounding control */
matchmask &= ~EVEX_P2LL;
if (decoflags & MASK)
mismatch &= ~EVEX_P2AAA;
if (decoflags & Z)
matchmask &= ~EVEX_P2Z;
if (itemp_has(t, IF_NF_E)) {
matchmask &= ~EVEX_P2NF;
if (prefix->rex.nf)
ins->prefixes[PPS_NF] = P_NF;
}
if (itemp_has(t, IF_ZU_E)) {
matchmask &= ~EVEX_P2ZU;
if (prefix->rex.zu)
ins->prefixes[PPS_ZU] = P_ZU;
}
#if 0
if (mismatch & matchmask) {
extern const struct itemplate instrux[];
printf("EVEX mismatch: %4zd: raw %08x template %08x mask %08x -> %08x\n",
t - instrux, prefix->rex.raw, evextemp, matchmask,
mismatch & matchmask);
}
#endif
if (mismatch & matchmask)
return 0;
ins->evex_tuple = *r++ - 0300;
if (itemp_has(t, IF_NF_E) && prefix->rex.nf)
ins->prefixes[PPS_NF] = P_NF;
if (itemp_has(t, IF_ZU_E) && prefix->rex.zu)
ins->prefixes[PPS_ZU] = P_ZU;
break;
}
case4(0260):
case 0270:
{
uint8_t vexm = *r++;
uint8_t vexwlp = *r++;
if (prefix->rex.type != REX_VEX)
return 0;
ins->rex |= REX_V;
if ((vexm & 0x1f) != prefix->rex.map)
return 0;
if (!itemp_has(t, IF_WIG)) {
if (prefix->rex.w != !!(vexwlp & 0x80))
return 0;
}
if ((vexwlp & 3) != prefix->rex.pp)
return 0;
if (!itemp_has(t, IF_LIG)) {
if (((vexwlp >> 2) & 3) != prefix->rex.l)
return 0;
}
if (c == 0270) {
if (prefix->rex.vreg != 0)
return 0;
} else {
opx->segment |= SEG_RMREG;
opx->basereg =
whichreg(opx->type, prefix->rex.vreg, ins->rex);
if (!opx->basereg)
return 0;
}
break;
}
case 0271:
if (prefix->rep == 0xF3)
drep = P_XRELEASE;
break;
case 0272:
if (prefix->rep == 0xF2)
drep = P_XACQUIRE;
else if (prefix->rep == 0xF3)
drep = P_XRELEASE;
break;
case 0273:
if (prefix->lock == 0xF0) {
if (prefix->rep == 0xF2)
drep = P_XACQUIRE;
else if (prefix->rep == 0xF3)
drep = P_XRELEASE;
}
break;
case 0310:
if (asize != 16)
return 0;
else
a_used = true;
break;
case 0311:
if (asize != 32)
return 0;
else
a_used = true;
break;
case 0312:
if (asize != bits)
return 0;
else
a_used = true;
break;
case 0313:
if (asize != 64)
return 0;
else
a_used = true;
break;
case 0314:
if (prefix->rex.flags & REX_B)
return 0;
break;
case 0315:
if (prefix->rex.flags & REX_X)
return 0;
break;
case 0316:
if (prefix->rex.flags & REX_R)
return 0;
break;
case 0317:
if (prefix->rex.flags & REX_W)
return 0;
break;
case 0320:
if (ins->rex & (REX_V | REX_EV))
osize = 16;
else if (osize != 16)
return 0;
o_used = true;
break;
case 0321:
if (ins->rex & (REX_V | REX_EV))
osize = 32;
else if (osize != 32)
return 0;
o_used = true;
break;
case 0322:
if (ins->rex & (REX_V | REX_EV))
osize = defosize;
else if (osize != defosize)
return 0;
o_used = true;
break;
case 0323:
/* 64-bit only instruction, no REX.W required */
ins->op_size = osize = 64;
ins->rex |= REX_NW;
break;
case 0324:
if (osize != 64)
return 0;
o_used = true;
break;
case 0325:
ins->rex |= REX_NH;
break;
case 0326:
if (prefix->rep == 0xF3)
return 0;
break;
case 0327:
if (bits == 64) {
ins->op_size = osize = 64;
ins->rex |= REX_NW;
}
break;
case 0330:
break;
case 0331:
if (prefix->rep)
return 0;
break;
case 0332:
if (prefix->rep != 0xF2)
return 0;
drep = 0;
break;
case 0333:
if (prefix->rep != 0xF3)
return 0;
drep = 0;
break;
case 0334:
if (bits != 64 && prefix->lock) {
ins->rex |= REX_R;
dlock = 0;
}
break;
case 0335:
if (drep == P_REP)
drep = P_REPE;
break;
case 0336:
case 0337:
break;
case 0340:
return 0;
case 0341:
if (prefix->wait != 0x9B)
return 0;
dwait = 0;
break;
case 0344:
ins->rex |= REX_B;
break;
case 0345:
ins->rex |= REX_X;
break;
case 0346:
ins->rex |= REX_R;
break;
case 0347:
ins->rex |= REX_W;
break;
case 0350:
if (prefix->rex.type != REX_REX2)
return 0;
ins->rex |= REX_2;
break;
case 0351:
if (prefix->rex.type != REX_REX2)
return 0;
ins->rex |= REX_2 | REX_W;
break;
case 0355:
case 0356:
case 0357:
nasm_assert(prefix->rex.map == c - 0354);
break;
case 0360:
if (prefix->osp || prefix->rep)
return 0;
break;
case 0361:
if (!prefix->osp || prefix->rep)
return 0;
o_used = true;
break;
case 0364:
if (prefix->osp)
return 0;
break;
case 0365:
if (prefix->asp)
return 0;
break;
case 0366:
if (!prefix->osp)
return 0;
o_used = true;
break;
case 0367:
if (!prefix->asp)
return 0;
a_used = true;
break;
case 0370:
case 0371:
break;
case 0374:
eat = EA_XMMVSIB;
break;
case 0375:
eat = EA_YMMVSIB;
break;
case 0376:
eat = EA_ZMMVSIB;
break;
default:
fprintf(stderr, "ndisasm: unknown byte code: 0%03o\n", c);
return 0; /* Unknown code */
}
}
/*
* VEX or EVEX encoding in byte code, but no such prefix present,
* or vice versa?
*/
if ((prefix->rex.flags ^ ins->rex) & (REX_V | REX_EV))
return 0;
/* Required REX bits not present? */
if ((ins->rex & ~prefix->rex.flags) &
(REX_2 | REX_P | REX_B | REX_X | REX_R | REX_W))
return 0;
/*
* Check for unused rep or a/o prefixes.
*/
if (dlock) {
if ((ins->rex & (REX_V | REX_EV)) || !itemp_has(t, IF_LOCK))
return 0; /* Instruction not lockable */
ins->prefixes[PPS_LOCK] = dlock;
}
if (drep) {
if (ins->prefixes[PPS_REP])
return 0;
ins->prefixes[PPS_REP] = drep;
}
ins->prefixes[PPS_WAIT] = dwait;
if (prefix->rex.w && osize == 64 && (ins->rex & REX_NW))
o_used = false;
if (!o_used) {
enum prefixes pfx = 0;
if (prefix->osp) {
if (osize == 16 && bits != 16)
pfx = P_O16;
else if (osize == 32 && bits == 16)
pfx = P_O32;
else
pfx = P_OSP;
} else if (prefix->rex.w && osize == 64) {
pfx = P_O64;
}
if (ins->prefixes[PPS_OSIZE])
return 0;
ins->prefixes[PPS_OSIZE] = pfx;
}
if (itemp_has(t, IF_JCC_HINT)) {
if ((prefix->seg & ~0x10) == 0x2e)
ins->prefixes[PPS_SEG] = prefix->seg & 0x10 ? P_PT : P_PN;
}
if (!a_used) {
/* Emit any possible segment override prefix explicitly */
if (!ins->prefixes[PPS_SEG])
ins->prefixes[PPS_SEG] = prefix->segover;
if (prefix->asp) {
if (ins->prefixes[PPS_ASIZE])
return 0;
ins->prefixes[PPS_ASIZE] = asize == 16 ? P_A16 : P_A32;
}
}
if (itemp_has(t, IF_NF_R))
ins->prefixes[PPS_NF] = P_NF;
if (itemp_has(t, IF_ZU_R))
ins->prefixes[PPS_ZU] = P_ZU;
if (itemp_has(t, IF_LATEVEX) && prefix->rex.type == REX_VEX) {
ins->prefixes[PPS_REX] = P_VEX;
}
if ((prefix->rex.raw & 0x807fff) == 0x0061c4) {
/* A VEX3-prefixed instruction which could have been VEX2-encoded */
ins->prefixes[PPS_REX] = P_VEX3;
}
/* Final fixup of operands */
for (i = 0; i < ins->operands; i++) {
struct operand *o = &ins->oprs[i];
if (o->segment & SEG_RMREG) {
o->type = nasm_reg_flags[o->basereg];
} else if (o->segment & SEG_RMMEM) {
o->type &= ~(REGISTER | IMMEDIATE);
o->type |= MEMORY;
} else if (is_class(REGISTER, o->type) && !o->basereg) {
/* An implicit register operand? */
o->basereg = implicit_reg(o->type);
if (!o->basereg)
return 0;
o->segment |= SEG_RMREG;
}
}
return data - origdata;
}
static const char *regname(enum reg_enum reg)
{
if (!is_register(reg))
return "";
return nasm_reg_names[reg - EXPR_REG_START];
}
/*
* If there is a SM indicator in the instruction template, then:
* - If any of the operands is a sized register, remove sizes from
* any other size-matched non-register operands;
* - Otherwise remove the sizes from all but the first such operand.
*/
static void remove_redundant_sizes(insn *ins)
{
unsigned int smmask = itemp_smx(ins->itemp);
int i;
opflags_t sized = 0;
for (i = 0; i < ins->operands; i++) {
const struct operand *o = &ins->oprs[i];
if (!(smmask & (1 << i)))
continue;
if (o->type & REGISTER)
sized |= o->type & SIZE_MASK;
}
for (i = 0; i < ins->operands; i++) {
struct operand *o = &ins->oprs[i];
/* This is true if more than one bit is set in "sized" */
if (sized & (sized-1))
break; /* How the heck did this even match? */
if (!(smmask & (1 << i)))
continue;
/* True if exactly 0 or 1 bits set in "sized" */
if ((o->type & SIZE_MASK) == sized)
o->type -= sized; /* Remove size */
else
sized |= o->type & SIZE_MASK;
}
}
int32_t disasm(const uint8_t *dp, int32_t data_size,
char *output, int outbufsize,
int bits, int64_t offset, int autosync,
iflag_t *prefer)
{
const struct itemplate * const * const *ix;
const struct itemplate * const *p;
const struct itemplate *itemp, *best_itemp;
int length, best_length = 0;
int maxlen = 15;
int i, slen;
char separator;
const uint8_t *origdata = dp;
int works;
insn ins;
iflag_t goodness, best;
int best_pref;
struct prefix_info prefix;
/*
* Scan for prefixes.
*/
dp = parse_prefixes(&prefix, dp, bits);
if (!dp) /* Invalid or no space for even one opcode */
return 0;
/*
* WAIT is not really a prefix, it is an instruction,
* so it doesn't count toward the maximum instruction
* length. See comment in rexdis.c.
*/
if (prefix.wait)
maxlen++;
maxlen -= dp - origdata; /* Prefixes count toward instruction length */
iflag_set_all(&best); /* Worst possible */
best_itemp = NULL;
best_pref = INT_MAX;
ix = ndisasm_itable[prefix.rex.xmap];
if (!ix)
return 0; /* Instruction map does not exist */
fetch_or_return(origdata, dp, data_size, 1);
p = ix[*dp];
/* dp now points to the primary opcode byte */
if (!p)
return 0; /* No instructions for this opcode */
nasm_zero(ins);
while ((itemp = *p++)) {
insn tmp_ins;
nasm_zero(tmp_ins);
tmp_ins.loc.offset = offset;
tmp_ins.bits = bits;
tmp_ins.itemp = itemp;
if ((length = matches(dp, &prefix, &tmp_ins))) {
if (length > maxlen)
continue; /* Instruction too long */
works = true;
/*
* Final check to make sure the operand types are valid
*/
for (i = 0; i < tmp_ins.operands; i++) {
opflags_t tt = itemp->opd[i];
opflags_t it = tmp_ins.oprs[i].type;
/* Strip flags that are not applicable to disassembler matching */
if (!is_class(REG_GPR, it))
tt &= ~SIZE_MASK;
if (is_class(IMMEDIATE, tt))
tt &= ~SUBCLASS_MASK;
if (!is_class(tt, it)) {
#if 0
bool is_reg = !!(tmp_ins.oprs[i].segment & SEG_RMREG);
printf("flags 0x%lx%s%s do not match template 0x%lx\n",
it,
is_reg ? " for register " : "",
is_reg ? regname(tmp_ins.oprs[i].basereg) : "",
tt);
#endif
works = false;
break;
}
}
/*
* Note: we always prefer instructions which incorporate
* prefixes in the instructions themselves. This is to allow
* e.g. PAUSE to be preferred to REP NOP, and deal with
* MMX/SSE instructions where prefixes are used to select
* between MMX and SSE register sets or outright opcode
* selection.
*/
if (works) {
int i, nprefix;
goodness = iflag_pfmask(itemp);
goodness = iflag_xor(&goodness, prefer);
nprefix = 0;
for (i = 0; i < MAXPREFIX; i++)
if (tmp_ins.prefixes[i])
nprefix++;
if (nprefix < best_pref ||
(nprefix == best_pref &&
iflag_cmp(&goodness, &best) < 0)) {
/* This is the best one found so far */
best = goodness;
best_itemp = itemp;
best_pref = nprefix;
best_length = length;
ins = tmp_ins;
}
if (itemp_has(itemp, IF_BESTDIS))
break; /* Don't search any further */
}
}
}
if (!best_itemp)
return 0; /* no instruction was matched */
/* Pick the best match */
itemp = best_itemp;
length = best_length;
slen = 0;
for (i = 0; i < MAXPREFIX; i++) {
const char *pfx = prefix_name(ins.prefixes[i]);
if (pfx)
slen += snprintf(output+slen, outbufsize-slen, "%s ", pfx);
}
slen += snprintf(output + slen, outbufsize - slen, "%s",
nasm_insn_names[ins.opcode]);
remove_redundant_sizes(&ins);
separator = ' ';
length += dp - origdata; /* fix up for prefixes */
for (i = 0; i < ins.operands; i++) {
decoflags_t deco = itemp->deco[i];
const operand *o = &ins.oprs[i];
opflags_t t = o->type;
int64_t offs = o->offset;
int asize = seg_get_asize(o->segment);
int nasize = 64 - asize; /* Address bits to mask off */
output[slen++] = separator;
if (o->segment & SEG_RELATIVE) {
/*
* sort out wraparound
*/
offs += ins.loc.offset + length;
offs = (uint64_t)offs << nasize >> nasize;
if ((t & (IMMEDIATE|SIZE_MASK)) == IMMEDIATE) {
if (asize != bits) {
switch (asize) {
case 16:
t |= BITS16;
break;
case 32:
t |= BITS32;
break;
case 64:
t |= BITS64;
break;
}
}
}
/*
* add sync marker, if autosync is on
*/
if (autosync)
add_sync(offs, 0L);
}
separator = (t & COLON) ? ':' : ',';
if ((t & (REGISTER | FPUREG)) ||
(o->segment & SEG_RMREG)) {
enum reg_enum reg = o->basereg;
if (t & TO)
slen += snprintf(output + slen, outbufsize - slen, "to ");
slen += snprintf(output + slen, outbufsize - slen, "%s",
regname(reg));
if (t & REGSET_MASK)
slen += snprintf(output + slen, outbufsize - slen, "+%d",
(int)((t & REGSET_MASK) >> (REGSET_SHIFT-1))-1);
if (deco)
slen += append_evex_reg_deco(output + slen, outbufsize - slen,
deco, &prefix);
} else if (t & IMMEDIATE) {
if (is_class(t, UNITY)) {
output[slen++] = '1';
} else if (o->segment & SEG_DFV) {
int fl;
static const char dfv_flags[] = "czso";
const char *flsep = "";
memcpy(&output[slen], "{dfv=", 5);
slen += 5;
for (fl = 0; fl < 4; fl++) {
if (offs & (1 << fl)) {
slen += snprintf(output + slen, outbufsize - slen,
"%s%cf", flsep, dfv_flags[fl]);
flsep = ",";
}
}
output[slen++] = '}';
separator = ' '; /* No comma after dfv */
} else {
/* Immediates that will actually be printed as numbers */
if (o->segment & SEG_RELATIVE) {
/* Don't print any sizes for near jump targets */
} else if (t & BITS8) {
slen +=
snprintf(output + slen, outbufsize - slen, "byte ");
if (o->segment & SEG_SIGNED) {
if (offs < 0) {
offs = -offs;
output[slen++] = '-';
} else {
output[slen++] = '+';
}
}
} else if (t & BITS16) {
slen +=
snprintf(output + slen, outbufsize - slen, "word ");
} else if (t & BITS32) {
slen +=
snprintf(output + slen, outbufsize - slen, "dword ");
} else if (t & BITS64) {
slen +=
snprintf(output + slen, outbufsize - slen, "qword ");
} else if (t & NEAR) {
slen +=
snprintf(output + slen, outbufsize - slen, "near ");
} else if (t & SHORT) {
slen +=
snprintf(output + slen, outbufsize - slen, "short ");
}
slen +=
snprintf(output + slen, outbufsize - slen, "0x%"PRIx64"",
offs);
}
} else if (is_class(REGMEM, t)) {
int started = false;
if (t & BITS8)
slen +=
snprintf(output + slen, outbufsize - slen, "byte ");
if (t & BITS16)
slen +=
snprintf(output + slen, outbufsize - slen, "word ");
if (t & BITS32)
slen +=
snprintf(output + slen, outbufsize - slen, "dword ");
if (t & BITS64)
slen +=
snprintf(output + slen, outbufsize - slen, "qword ");
if (t & BITS80)
slen +=
snprintf(output + slen, outbufsize - slen, "tword ");
if (prefix.rex.b && (deco & BRDCAST_MASK)) {
/* when broadcasting, each element size should be used */
if (deco & BR_BITS16)
slen +=
snprintf(output + slen, outbufsize - slen, "word ");
else if (deco & BR_BITS32)
slen +=
snprintf(output + slen, outbufsize - slen, "dword ");
else if (deco & BR_BITS64)
slen +=
snprintf(output + slen, outbufsize - slen, "qword ");
} else {
if (t & BITS128)
slen +=
snprintf(output + slen, outbufsize - slen, "oword ");
if (t & BITS256)
slen +=
snprintf(output + slen, outbufsize - slen, "yword ");
if (t & BITS512)
slen +=
snprintf(output + slen, outbufsize - slen, "zword ");
}
if (t & FAR)
slen += snprintf(output + slen, outbufsize - slen, "far ");
if (t & NEAR)
slen +=
snprintf(output + slen, outbufsize - slen, "near ");
output[slen++] = '[';
if (prefix.segover) {
slen +=
snprintf(output + slen, outbufsize - slen, "%s:",
prefix_name(prefix.segover));
}
if (o->basereg) {
slen += snprintf(output + slen, outbufsize - slen, "%s",
regname(o->basereg));
started = true;
}
if (o->indexreg && !itemp_has(best_itemp, IF_MIB)) {
if (started)
output[slen++] = '+';
slen += snprintf(output + slen, outbufsize - slen, "%s",
regname(o->indexreg));
if (o->scale > 1)
slen +=
snprintf(output + slen, outbufsize - slen, "*%d",
o->scale);
started = true;
}
/* Show a displacement */
if (o->disp_size) {
unsigned int defdisp = asize == 16 ? 16 : 32;
bool do_sign = started || (o->segment & SEG_SIGNED);
bool do_sext = do_sign || o->disp_size < asize;
uint64_t offset;
const char *rel = "";
const char *sizename = "";
const char *sign = "";
if (o->eaflags & EAF_REL)
rel = "rel ";
else if (!started && bits == 64)
rel = "abs ";
if (o->disp_size != defdisp ||
(!started && asize != bits)) {
switch (o->disp_size) {
case 16:
sizename = "word ";
break;
case 32:
sizename = "dword ";
break;
case 64:
sizename = "qword ";
break;
default:
break; /* This includes 8 bits */
}
}
if (do_sext)
offset = (int64_t)offs << nasize >> nasize;
else
offset = (uint64_t)offs << nasize >> nasize;
if (do_sign) {
if (started)
sign = "+";
if ((int64_t)offset < 0) {
sign = "-";
offset = -offset;
}
}
slen += snprintf(output + slen, outbufsize - slen,
"%s%s%s0x%"PRIx64"",
rel, sizename, sign, offset);
}
if (o->indexreg && itemp_has(best_itemp, IF_MIB)) {
output[slen++] = ',';
slen += snprintf(output + slen, outbufsize - slen, "%s",
regname(o->indexreg));
if (o->scale > 1)
slen +=
snprintf(output + slen, outbufsize - slen, "*%d",
o->scale);
started = true;
}
output[slen++] = ']';
if (deco)
slen += append_evex_mem_deco(output + slen, outbufsize - slen,
t, deco, &prefix);
} else {
slen +=
snprintf(output + slen, outbufsize - slen, "<operand%d>",
i);
}
}
output[slen] = '\0';
return length;
}
/*
* This is called when we don't have a complete instruction. If it
* is a standalone *single-byte* prefix show it as such, otherwise
* print it as a literal.
*/
int32_t eatbyte(uint8_t byte, char *output, int outbufsize, int bits)
{
const char *str = NULL;
switch (byte) {
case 0xF2:
str = "repne";
break;
case 0xF3:
str = "rep";
break;
case 0x9B:
str = "wait";
break;
case 0xF0:
str = "lock";
break;
case 0x2E:
str = "cs";
break;
case 0x36:
str = "ss";
break;
case 0x3E:
str = "ds";
break;
case 0x26:
str = "es";
break;
case 0x64:
str = "fs";
break;
case 0x65:
str = "gs";
break;
case 0x66:
str = (bits == 16) ? "o32" : "o16";
break;
case 0x67:
str = (bits == 32) ? "a16" : "a32";
break;
case REX_P + 0x0:
case REX_P + 0x1:
case REX_P + 0x2:
case REX_P + 0x3:
case REX_P + 0x4:
case REX_P + 0x5:
case REX_P + 0x6:
case REX_P + 0x7:
case REX_P + 0x8:
case REX_P + 0x9:
case REX_P + 0xA:
case REX_P + 0xB:
case REX_P + 0xC:
case REX_P + 0xD:
case REX_P + 0xE:
case REX_P + 0xF:
if (bits == 64) {
snprintf(output, outbufsize, "rex%s%s%s%s%s",
(byte == REX_P) ? "" : ".",
(byte & REX_W) ? "w" : "",
(byte & REX_R) ? "r" : "",
(byte & REX_X) ? "x" : "",
(byte & REX_B) ? "b" : "");
break;
}
/* else fall through */
default:
snprintf(output, outbufsize, "db 0x%02x", byte);
break;
}
if (str)
snprintf(output, outbufsize, "%s", str);
return 1;
}