mirror of
https://github.com/netwide-assembler/nasm.git
synced 2025-09-22 10:43:39 -04:00
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>
1811 lines
53 KiB
C
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;
|
|
}
|