mirror of
https://github.com/netwide-assembler/nasm.git
synced 2025-09-22 10:43:39 -04:00
Only VEX and EVEX may not take REP or OSZ prefixes. Signed-off-by: H. Peter Anvin (Intel) <hpa@zytor.com>
511 lines
13 KiB
C
511 lines
13 KiB
C
/* ----------------------------------------------------------------------- *
|
|
*
|
|
* Copyright 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.
|
|
*
|
|
* ----------------------------------------------------------------------- */
|
|
|
|
#include "disasm.h"
|
|
#include "nasmlib.h"
|
|
|
|
/*
|
|
* Parse various register combinations. Note that this does NOT
|
|
* generate the REX_[BXR][1V] flags, as they are not used by the
|
|
* disassembler.
|
|
*/
|
|
static uint32_t
|
|
xbits(uint32_t val, unsigned int from, unsigned int count, unsigned int to)
|
|
{
|
|
const uint32_t tomask = ((UINT32_C(1) << count)-1) << to;
|
|
|
|
if (to < from)
|
|
return (val >> (from-to)) & tomask;
|
|
else
|
|
return (val << (to-from)) & tomask;
|
|
}
|
|
|
|
/* ------ EVEX decoding ------ */
|
|
|
|
#define EVEX_INVERTED 0x087cf000
|
|
|
|
static uint32_t
|
|
evexbits(uint32_t val, unsigned int from, unsigned int count, unsigned int to)
|
|
{
|
|
return xbits(val ^ EVEX_INVERTED, from, count, to);
|
|
}
|
|
|
|
static uint32_t evex_rreg(uint32_t evex)
|
|
{
|
|
return evexbits(evex,15,1,3) + evexbits(evex,12,1,4);
|
|
}
|
|
static uint32_t evex_xreg(uint32_t evex, bool xv)
|
|
{
|
|
uint32_t x = evexbits(evex,14,1,3);
|
|
x += evexbits(evex,xv ? 27 : 18,1,4);
|
|
return x;
|
|
}
|
|
static uint32_t evex_breg(uint32_t evex, bool bv)
|
|
{
|
|
uint32_t b = evexbits(evex,13,1,3);
|
|
b += evexbits(evex,bv ? 14 : 11,1,4);
|
|
return b;
|
|
}
|
|
static uint32_t evex_vreg(uint32_t evex, bool xv)
|
|
{
|
|
uint32_t v = evexbits(evex,19,4,0);
|
|
if (!xv)
|
|
v += evexbits(evex,27,1,4);
|
|
return v;
|
|
}
|
|
static uint32_t evex_aaa(uint32_t evex)
|
|
{
|
|
return evexbits(evex,24,3,0);
|
|
}
|
|
static uint32_t evex_z(uint32_t evex)
|
|
{
|
|
return evexbits(evex,31,1,0);
|
|
}
|
|
static uint32_t evex_l(uint32_t evex)
|
|
{
|
|
return evexbits(evex,29,2,0);
|
|
}
|
|
static uint32_t evex_b(uint32_t evex)
|
|
{
|
|
return evexbits(evex,28,1,0);
|
|
}
|
|
static uint32_t evex_nf(uint32_t evex)
|
|
{
|
|
return evexbits(evex,26,1,0);
|
|
}
|
|
static uint32_t evex_scc(uint32_t evex)
|
|
{
|
|
return evexbits(evex,24,4,0);
|
|
}
|
|
static uint32_t evex_dfl(uint32_t evex)
|
|
{
|
|
return evexbits(evex,19,4,0);
|
|
}
|
|
static uint32_t evex_map(uint32_t evex)
|
|
{
|
|
return evexbits(evex,8,3,0);
|
|
}
|
|
static uint32_t evex_w(uint32_t evex)
|
|
{
|
|
return evexbits(evex,23,1,0);
|
|
}
|
|
static uint32_t evex_pp(uint32_t evex)
|
|
{
|
|
return evexbits(evex,16,2,0);
|
|
}
|
|
|
|
/* ------ VEX3 decoding ------ */
|
|
|
|
static uint32_t vex2to3(uint32_t vex2)
|
|
{
|
|
return (vex2 & 0x80fe) + ((vex2 & 0x7f00) << 8) + 0x6100;
|
|
}
|
|
|
|
static uint32_t vex3_rreg(uint32_t vex)
|
|
{
|
|
return xbits(~vex,15,1,3);
|
|
}
|
|
static uint32_t vex3_xreg(uint32_t vex)
|
|
{
|
|
return xbits(~vex,14,1,3);
|
|
}
|
|
static uint32_t vex3_breg(uint32_t vex)
|
|
{
|
|
return xbits(~vex,13,1,3);
|
|
}
|
|
static uint32_t vex3_map(uint32_t vex)
|
|
{
|
|
return xbits(vex,8,5,0);
|
|
}
|
|
static uint32_t vex3_vreg(uint32_t vex)
|
|
{
|
|
return xbits(~vex,19,4,0);
|
|
}
|
|
static uint32_t vex3_l(uint32_t vex)
|
|
{
|
|
return xbits(vex,18,1,0);
|
|
}
|
|
static uint32_t vex3_pp(uint32_t vex)
|
|
{
|
|
return xbits(vex,16,2,0);
|
|
}
|
|
static uint32_t vex3_w(uint32_t vex)
|
|
{
|
|
return xbits(vex,23,1,0);
|
|
}
|
|
|
|
/* ------ REX2 decoding ------ */
|
|
|
|
static uint32_t rex2_rreg(uint32_t rex)
|
|
{
|
|
return xbits(rex,14,1,4) + xbits(rex,10,1,3);
|
|
}
|
|
static uint32_t rex2_xreg(uint32_t rex)
|
|
{
|
|
return xbits(rex,13,1,4) + xbits(rex,9,1,3);
|
|
}
|
|
static uint32_t rex2_breg(uint32_t rex)
|
|
{
|
|
return xbits(rex,12,1,4) + xbits(rex,8,1,3);
|
|
}
|
|
static uint32_t rex2_w(uint32_t rex)
|
|
{
|
|
return xbits(rex,11,1,0);
|
|
}
|
|
static uint32_t rex2_map(uint32_t rex)
|
|
{
|
|
return xbits(rex,15,1,0);
|
|
}
|
|
|
|
/* ------ REX decoding ------ */
|
|
|
|
static uint32_t rex_rreg(uint32_t rex)
|
|
{
|
|
return xbits(rex,2,1,3);
|
|
}
|
|
static uint32_t rex_xreg(uint32_t rex)
|
|
{
|
|
return xbits(rex,1,1,3);
|
|
}
|
|
static uint32_t rex_breg(uint32_t rex)
|
|
{
|
|
return xbits(rex,0,1,3);
|
|
}
|
|
static uint32_t rex_w(uint32_t rex)
|
|
{
|
|
return xbits(rex,3,1,0);
|
|
}
|
|
|
|
/*
|
|
* Parse an EVEX prefix.
|
|
*/
|
|
static void parse_evex(struct rexfields *rf, uint32_t val)
|
|
{
|
|
rf->type = REX_EVEX;
|
|
rf->len = 4;
|
|
rf->raw = val;
|
|
rf->opc = (uint8_t)val;
|
|
|
|
rf->breg = evex_breg(val, false);
|
|
rf->bregbv = evex_breg(val, true);
|
|
rf->xreg = evex_xreg(val, false);
|
|
rf->xregxv = evex_xreg(val, true);
|
|
rf->vreg = evex_vreg(val, false);
|
|
rf->vregxv = evex_vreg(val, true);
|
|
|
|
rf->rreg = evex_rreg(val);
|
|
rf->map = evex_map(val);
|
|
rf->xmap = rf->map + MAP_BASE_EVEX;
|
|
rf->pp = evex_pp(val);
|
|
rf->l = evex_l(val);
|
|
rf->w = evex_w(val);
|
|
rf->z = evex_z(val);
|
|
rf->b = evex_b(val);
|
|
rf->nd = rf->b;
|
|
rf->zu = rf->b;
|
|
rf->aaa = evex_aaa(val);
|
|
rf->scc = evex_scc(val);
|
|
rf->dfl = evex_dfl(val);
|
|
rf->nf = evex_nf(val);
|
|
|
|
rf->flags = REX_EV | REX_P | ((~val >> 13) & 7) | (rf->w << 3);
|
|
}
|
|
|
|
/* ------ Set value for all moptypes ------ */
|
|
|
|
/* case statements for original REX */
|
|
#define CASE_REX \
|
|
case 0x40: case 0x41: case 0x42: case 0x43: \
|
|
case 0x44: case 0x45: case 0x46: case 0x47: \
|
|
case 0x48: case 0x49: case 0x4a: case 0x4b: \
|
|
case 0x4c: case 0x4d: case 0x4e: case 0x4f
|
|
|
|
static const uint8_t *
|
|
parse_rex(struct rexfields *rf, uint8_t op, const uint8_t *p)
|
|
{
|
|
uint32_t breg = 0;
|
|
uint32_t xreg = 0;
|
|
uint32_t vreg = 0;
|
|
uint32_t val = op;
|
|
|
|
rf->opc = op;
|
|
|
|
switch (op) {
|
|
CASE_REX:
|
|
rf->type = REX_REX;
|
|
rf->flags = op;
|
|
rf->len = 1;
|
|
rf->raw = op;
|
|
rf->opc = 0x40; /* Mask out payload bits */
|
|
breg = rex_breg(op);
|
|
xreg = rex_xreg(op);
|
|
rf->rreg = rex_rreg(op);
|
|
rf->w = rex_w(op);
|
|
break;
|
|
|
|
case 0xd5:
|
|
rf->type = REX_REX2;
|
|
rf->len = 2;
|
|
rf->raw = val = getu16(p);
|
|
rf->flags = REX_2 | REX_P | ((val >> 8) & 15);
|
|
rf->opc = (uint8_t)val;
|
|
breg = rex2_breg(val);
|
|
xreg = rex2_xreg(val);
|
|
rf->rreg = rex2_rreg(val);
|
|
rf->w = rex2_w(val);
|
|
rf->map = rex2_map(val);
|
|
rf->xmap = rf->map + MAP_BASE_REX2;
|
|
break;
|
|
|
|
case 0xc5:
|
|
rf->raw = val = getu16(p);
|
|
val = vex2to3(val);
|
|
rf->map = 1;
|
|
rf->xmap = rf->map + MAP_BASE_VEX;
|
|
rf->len = 2;
|
|
goto vex_common;
|
|
|
|
case 0x8f:
|
|
rf->raw = val = op + (getu16(p+1) << 8);
|
|
rf->map = vex3_map(val);
|
|
if (rf->map < 8)
|
|
return NULL;
|
|
rf->xmap = rf->map + MAP_BASE_XOP;
|
|
rf->len = 3;
|
|
goto vex_common;
|
|
|
|
case 0xc4:
|
|
rf->raw = val = op + (getu16(p+1) << 8);
|
|
rf->map = vex3_map(val);
|
|
rf->xmap = rf->map + MAP_BASE_VEX;
|
|
rf->len = 3;
|
|
vex_common:
|
|
rf->type = REX_VEX;
|
|
breg = vex3_breg(val);
|
|
xreg = vex3_xreg(val);
|
|
vreg = vex3_vreg(val);
|
|
rf->rreg = vex3_rreg(val);
|
|
rf->pp = vex3_pp(val);
|
|
rf->l = vex3_l(val);
|
|
rf->w = vex3_w(val);
|
|
rf->flags = REX_P | REX_V | ((~val >> 8) & 7) | (rf->w << 3);
|
|
break;
|
|
|
|
case 0x62:
|
|
parse_evex(rf, getu32(p));
|
|
return p + rf->len;
|
|
|
|
default:
|
|
return p; /* Not a prefix */
|
|
}
|
|
|
|
rf->bregbv = rf->breg = breg;
|
|
rf->xregxv = rf->xreg = xreg;
|
|
rf->vregxv = rf->vreg = vreg;
|
|
|
|
return p + rf->len;
|
|
}
|
|
|
|
/*
|
|
* The buffer must contain at least 20 readable bytes, although the
|
|
* actual values beyond the end of the current valid instruction do
|
|
* not matter. This function returns NULL if the instruction is
|
|
* inherently invalid.
|
|
*/
|
|
const uint8_t *
|
|
parse_prefixes(struct prefix_info *pf, const uint8_t *data, int bits)
|
|
{
|
|
bool end_prefix;
|
|
const uint8_t *p = data;
|
|
const uint8_t *maxp;
|
|
|
|
nasm_zero(*pf);
|
|
|
|
/*
|
|
* The maximum instruction length is 15 bytes; fail in parse_prefixes()
|
|
* unless there is at least one byte left for the actual opcode.
|
|
*/
|
|
maxp = p + 15 - 1;
|
|
|
|
pf->asize = bits;
|
|
pf->osize = (bits == 64) ? 32 : bits;
|
|
|
|
/*
|
|
* WAIT is not really a prefix, but an instruction in its own
|
|
* right. Only decode it as the very first byte (otherwise
|
|
* prefixes apply to the WAIT instruction, not to anything
|
|
* following it!) so that in case WAIT actually is prefixed with
|
|
* something, those prefixes will be separately emitted by
|
|
* eat_byte().
|
|
*
|
|
* Since WAIT is really an instruction, it doesn't count towards
|
|
* the length of the following instruction, either.
|
|
*/
|
|
|
|
if (*p == 0x9b) {
|
|
pf->wait = *p++;
|
|
maxp++; /* Does not count toward instruction length */
|
|
}
|
|
|
|
end_prefix = false;
|
|
while (!end_prefix) {
|
|
uint8_t b = *p;
|
|
|
|
switch (b) {
|
|
case 0xf2:
|
|
case 0xf3:
|
|
pf->rep = b;
|
|
pf->rex.pp = b ^ 0xfd; /* F2 = 3, F3 = 2 */
|
|
p++;
|
|
break;
|
|
|
|
case 0xf0:
|
|
pf->lock = b;
|
|
p++;
|
|
break;
|
|
|
|
case 0x26:
|
|
pf->segover = R_ES;
|
|
goto isseg;
|
|
case 0x2e:
|
|
pf->segover = R_CS;
|
|
goto isseg;
|
|
case 0x36:
|
|
pf->segover = R_SS;
|
|
goto isseg;
|
|
case 0x3e:
|
|
pf->segover = R_DS;
|
|
goto isseg;
|
|
case 0x64:
|
|
pf->segover = R_FS;
|
|
goto isseg;
|
|
case 0x65:
|
|
pf->segover = R_GS;
|
|
isseg:
|
|
pf->seg = b;
|
|
p++;
|
|
break;
|
|
|
|
case 0x66:
|
|
pf->osize = (bits == 16) ? 32 : 16;
|
|
pf->osp = b;
|
|
pf->rex.pp = 1;
|
|
p++;
|
|
break;
|
|
case 0x67:
|
|
pf->asize = (bits == 32) ? 16 : 32;
|
|
pf->asp = b;
|
|
p++;
|
|
break;
|
|
|
|
CASE_REX:
|
|
case 0xd5: /* REX2 */
|
|
if (bits == 64)
|
|
p = parse_rex(&pf->rex, b, p);
|
|
end_prefix = true;
|
|
break;
|
|
|
|
case 0x8f: /* XOP */
|
|
if (!(p[1] & 030)) {
|
|
/* Only maps 8-31 valid to protect 8F /0 */
|
|
end_prefix = true;
|
|
break;
|
|
}
|
|
/* fall through */
|
|
case 0xc4: /* VEX2 */
|
|
case 0xc5: /* VEX3 */
|
|
case 0x62: /* EVEX */
|
|
if (bits == 64 || (p[1] & 0xe0) == 0xe0)
|
|
p = parse_rex(&pf->rex, b, p);
|
|
end_prefix = true;
|
|
break;
|
|
|
|
default:
|
|
end_prefix = true;
|
|
break;
|
|
}
|
|
|
|
if (p > maxp)
|
|
return NULL; /* Invalid instruction */
|
|
}
|
|
|
|
switch (pf->rex.type) {
|
|
case REX_VEX:
|
|
case REX_EVEX:
|
|
if (pf->osp || pf->rep)
|
|
return NULL; /* Invalid instruction (illegal prefix) */
|
|
break;
|
|
|
|
case REX_REX2:
|
|
break;
|
|
|
|
case REX_REX:
|
|
/* Redundant REX prefixes are ignored */
|
|
while ((*p & 0xf0) == 0x40) {
|
|
p++;
|
|
if (p > maxp)
|
|
return NULL;
|
|
}
|
|
/* fall through */
|
|
|
|
case REX_NONE:
|
|
/*
|
|
* Look for legacy map prefixes. These must come after all
|
|
* possible REX prefixes.
|
|
*/
|
|
if (*p == 0x0f) {
|
|
pf->rex.map = 1;
|
|
p++;
|
|
switch (*p) {
|
|
case 0x38:
|
|
pf->rex.map = 2;
|
|
p++;
|
|
break;
|
|
case 0x3a:
|
|
pf->rex.map = 3;
|
|
p++;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
pf->rex.xmap = pf->rex.map + MAP_BASE_NOVEX;
|
|
break;
|
|
}
|
|
|
|
if (p > maxp)
|
|
return NULL;
|
|
|
|
return p;
|
|
}
|