/* ----------------------------------------------------------------------- * * * 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) { /* * specifies the displacement size (none, byte or * word), and 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, specifies displacement size (this time * none, byte or *dword*), while 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, "", 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; }